blob: 699deb100594e79697321f8e3ca7d1d42a92d84c [file] [log] [blame] [edit]
#!/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 <vdonnefort@google.com>
#
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 5
echo 0 > trace
write_events 5
pid=$(consuming_read $tmp)
sleep 1
stop_hyp_trace
kill $pid
validate_test $tmp 5 0 $(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