#!/bin/sh -e # SPDX-License-Identifier: GPL-2.0-only # hyp-trace-test - Tracefs for pKVM hypervisor test # # Copyright (C) 2024 - Google LLC # Author: Vincent Donnefort # log_and_die() { echo "$1" exit 1 } host_clock() { # BOOTTIME clock awk '/now/ { printf "%.6f\n", $3 / 1000000000 }' /proc/timer_list } page_size() { echo "$(awk '/KernelPageSize/ {print $2; exit}' /proc/self/smaps) * 1024" | bc } goto_hyp_trace() { if [ -d "/sys/kernel/debug/tracing/hypervisor" ]; then cd /sys/kernel/debug/tracing/hypervisor return fi if [ -d "/sys/kernel/tracing/hypervisor" ]; then cd /sys/kernel/tracing/hypervisor return fi echo "ERROR: hyp tracing folder not found!" exit 1 } reset_hyp_trace() { echo 0 > tracing_on echo 0 > trace for event in events/hypervisor/*; do echo 0 > $event/enable done } setup_hyp_trace() { reset_hyp_trace echo 16 > buffer_size_kb echo 1 > events/hypervisor/selftest/enable echo 1 > tracing_on } stop_hyp_trace() { echo 0 > tracing_on } hyp_trace_loaded() { grep -q "(loaded)" buffer_size_kb } write_events() { local num="$1" local func="$2" for i in $(seq 1 $num); do echo 1 > selftest_event [ -z "$func" -o $i -eq $num ] || eval $func done } consuming_read() { local output=$1 cat trace_pipe > $output & echo $! } run_test_consuming() { local nr_events=$1 local func=$2 local tmp="$(mktemp)" local start_ts=0 local end_ts=0 local pid=0 echo "Output trace file: $tmp" setup_hyp_trace pid=$(consuming_read $tmp) start_ts=$(host_clock) write_events $nr_events $func stop_hyp_trace end_ts=$(host_clock) kill $pid validate_test $tmp $nr_events $start_ts $end_ts rm $tmp } validate_test() { local output=$1 local expected_events=$2 local start_ts=$3 local end_ts=$4 local prev_ts=$3 local ts=0 local num_events=0 IFS=$'\n' for line in $(cat $output); do echo "$line" | grep -q -E "^# " && continue ts=$(echo "$line" | awk '{print $2}' | cut -d ':' -f1) if [ $(echo "$ts<$prev_ts" | bc) -eq 1 ]; then log_and_die "Error event @$ts < $prev_ts" fi prev_ts=$ts num_events=$((num_events + 1)) done if [ $(echo "$ts>$end_ts" | bc) -eq 1 ]; then log_and_die "Error event @$ts > $end_ts" fi if [ $num_events -ne $expected_events ]; then log_and_die "Expected $expected_events events, got $num_events" fi } test_ts() { echo "Test Timestamps..." run_test_consuming 1000 echo "done." } test_extended_ts() { echo "Test Extended Timestamps..." run_test_consuming 1000 "sleep 0.1" echo "done." } assert_loaded() { hyp_trace_loaded || log_and_die "Expected loaded buffer" } assert_unloaded() { ! hyp_trace_loaded || log_and_die "Expected unloaded buffer" } test_unloading() { local tmp="$(mktemp)" echo "Test unloading..." setup_hyp_trace assert_loaded echo 0 > tracing_on assert_unloaded pid=$(consuming_read $tmp) sleep 1 assert_loaded kill $pid assert_unloaded echo 1 > tracing_on write_events 1 echo 0 > trace assert_loaded echo 0 > tracing_on assert_unloaded echo "done." } test_reset() { local tmp="$(mktemp)" echo "Test Reset..." setup_hyp_trace write_events 1000 echo 0 > trace clock_before=$(host_clock) write_events 5 pid=$(consuming_read $tmp) sleep 1 stop_hyp_trace kill $pid validate_test $tmp 5 $clock_before $(host_clock) rm $tmp echo "done." } test_big_bpacking() { local hyp_buffer_page_size=48 local page_size=$(page_size) local min_buf_size=$(echo "$page_size * $page_size / ($hyp_buffer_page_size * $(nproc))" | bc) min_buf_size=$(echo "$min_buf_size * 2 / 1024" | bc) echo "Test loading $min_buf_size kB buffer..." reset_hyp_trace echo $min_buf_size > buffer_size_kb echo 1 > tracing_on stop_hyp_trace echo "done." } validate_event() { local line="$1" local expect_event="$2" local expect_arg="$3" local event="$(echo "$line" | awk '{print $3}')" local arg="$(echo "$line" | awk '{print $4}')" [ $event == $expect_event ] || log_and_die "Expected event '$expect_event', got: '$event'" [ $arg == $expect_arg ] || log_and_die "Expected arg '$expect_arg', got: '$arg'" } setup_hyp_ftrace() { reset_hyp_trace echo 1 > events/hypervisor/func/enable echo 1 > events/hypervisor/func_ret/enable } run_test_ftrace() { local output="$2" setup_hyp_ftrace echo 1 > tracing_on write_events 1 echo 0 > tracing_on pid=$(consuming_read $tmp) sleep 1 kill $pid } test_ftrace_nofilter() { local func="__kvm_nvhe_handle___pkvm_selftest_event" local tmp="$(mktemp)" echo "Test ftrace..." echo "*" > set_ftrace_filter run_test_ftrace $tmp grep -qE "func *$func" $tmp || \ log_and_die "Couldn't find 'func' event with arg '$func'" grep -q "func_ret $func" $tmp || \ log_and_die "Couldn't find 'func_ret' event with arg '$func'" rm $tmp } test_ftrace_filter() { local func="__kvm_nvhe_handle___pkvm_selftest_event" local tmp="$(mktemp)" echo "Test ftrace filtering..." echo "$func" > set_ftrace_filter [ "$(cat set_ftrace_filter)" == "$func" ] || \ log_and_die "Failed to set set_ftrace_filter" run_test_ftrace $tmp validate_event "$(awk 'NR==1 {print}' $tmp)" "func" "$func" validate_event "$(awk 'NR==2 {print}' $tmp)" "func_ret" "$func" rm $tmp } goto_hyp_trace test_reset test_unloading test_big_bpacking test_ts test_extended_ts test_ftrace_nofilter test_ftrace_filter exit 0