From aa8e581b253daaec2d20953a5d98153e823972dc Mon Sep 17 00:00:00 2001 From: Emilia Kurdybelska Date: Thu, 12 Feb 2026 11:00:14 +0100 Subject: [PATCH 1/2] test: add compressed audio with DSP offload test Signed-off-by: Emilia Kurdybelska --- case-lib/hijack.sh | 4 + case-lib/lib.sh | 43 ++++++++ test-case/residency-time-test.sh | 9 +- test-case/test-cplay.sh | 168 +++++++++++++++++++++++++++++++ tools/analyze-pc-states.py | 46 +++++++++ 5 files changed, 263 insertions(+), 7 deletions(-) create mode 100644 test-case/test-cplay.sh create mode 100644 tools/analyze-pc-states.py diff --git a/case-lib/hijack.sh b/case-lib/hijack.sh index 6f14e954..3090af26 100644 --- a/case-lib/hijack.sh +++ b/case-lib/hijack.sh @@ -14,6 +14,10 @@ function func_exit_handler() func_lib_check_and_disable_pipewire + if [ "$RUN_SOCWATCH" == true ]; then + unload_socwatch + fi + # call trace if [ "$exit_status" -ne 0 ] ; then dloge "Starting ${FUNCNAME[0]}(), exit status=$exit_status, FUNCNAME stack:" diff --git a/case-lib/lib.sh b/case-lib/lib.sh index 15d122ba..982be4e8 100644 --- a/case-lib/lib.sh +++ b/case-lib/lib.sh @@ -87,6 +87,10 @@ start_test() func_lib_enable_pipewire fi + if [ "$RUN_SOCWATCH" == true ]; then + load_socwatch + fi + if is_subtest; then return 0 fi @@ -1599,3 +1603,42 @@ analyze_mixed_sound() return 1 fi } + +# Generates 20s .mp3 file for testing +# Arguments: 1 - output filename +generate_mp3_file() +{ + ffmpeg -f lavfi -i "sine=frequency=1000:duration=20" "$1" +} + +# Load socwatch and check if module was loaded correctly +load_socwatch() +{ + sudo bash "$SOCWATCH_PATH"/drivers/insmod-socwatch || true + lsmod | grep -q socwatch || die "Socwatch is not loaded" +} + +unload_socwatch() +{ + sudo bash "$SOCWATCH_PATH"/drivers/rmmod-socwatch +} + +# Run any command with socwatch +# Arguments: +# 1 - socwatch output report filename +# 2 - command you want to run with socwatch (with arguments) +run_with_socwatch() +{ + if [ -z "$SOCWATCH_PATH" ]; then + die "SOCWATCH_PATH not set" + fi + + local output_file="$1" + shift + + ( set -x + sudo "$SOCWATCH_PATH"/socwatch -m -f sys -f cpu -f cpu-hw -f pcie \ + -f hw-cpu-cstate -f pcd-slps0 -f tcss-state -f tcss -f pcie-lpm -n 200 \ + -r json -o "$output_file" -p "$@") || + die "socwatch returned $?" +} diff --git a/test-case/residency-time-test.sh b/test-case/residency-time-test.sh index 71f5738b..b3837052 100755 --- a/test-case/residency-time-test.sh +++ b/test-case/residency-time-test.sh @@ -137,10 +137,6 @@ load_modules() run_socwatch_tests() { - # load socwatch module, if the module is loaded, go ahead with the testing - sudo bash "$SOCWATCH_PATH"/drivers/insmod-socwatch || true - check_socwatch_module_loaded || die "socwatch module not loaded" - # Create a dir for all socwatch reports mkdir "$LOG_ROOT/socwatch-results" pc10_results_file="$LOG_ROOT/socwatch-results/pc10_results.json" @@ -159,15 +155,14 @@ run_socwatch_tests() cd "$LOG_ROOT" tar -zcvf socwatch-results.tar.gz socwatch-results/ rm -rf "$LOG_ROOT/socwatch-results/" - - # unload socwatch module - sudo bash "$SOCWATCH_PATH"/drivers/rmmod-socwatch } main() { unload_modules + load_socwatch run_socwatch_tests + unload_socwatch load_modules } diff --git a/test-case/test-cplay.sh b/test-case/test-cplay.sh new file mode 100644 index 00000000..97560a57 --- /dev/null +++ b/test-case/test-cplay.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +## +## Case Name: check alsabat +## +## Preconditions: +## This test case requires physical loopback between playback and capture. +## playback <=====> capture +## nocodec : no need to use hw loopback cable, It support DSP loopback by quirk +## +## Description: +## Run two alsabat instances concurrently, one on each specified PCM: playback +## and capture. +## +## Warning: as of January 2024, "man alsabat" is incomplete and +## documents only the "single instance" mode where a single alsabat +## process performs both playback and capture. +## +## Case step: +## 1. Specify the pcm IDs for playback and catpure +## 3. run alsabat test +## +## Expect result: +## The return value of alsabat is 0 +## + +# remove the existing alsabat wav files +rm -f /tmp/bat.wav.* + +# shellcheck source=case-lib/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")"/../case-lib/lib.sh + +OPT_NAME['p']='pcm_p' OPT_DESC['p']='pcm for playback. Example: hw:0,0' +OPT_HAS_ARG['p']=1 OPT_VAL['p']='' + +OPT_NAME['C']='channel_c' OPT_DESC['C']='channel number for capture.' +OPT_HAS_ARG['C']=1 OPT_VAL['C']='1' + +OPT_NAME['N']='channel_p' OPT_DESC['N']='channel number for playback.' +OPT_HAS_ARG['N']=1 OPT_VAL['N']='2' + +OPT_NAME['r']='rate' OPT_DESC['r']='sample rate' +OPT_HAS_ARG['r']=1 OPT_VAL['r']=48000 + +OPT_NAME['c']='pcm_c' OPT_DESC['c']='pcm for capture. Example: hw:1,0' +OPT_HAS_ARG['c']=1 OPT_VAL['c']='' + +OPT_NAME['f']='format' OPT_DESC['f']='target format' +OPT_HAS_ARG['f']=1 OPT_VAL['f']="S16_LE" + +OPT_NAME['F']='frequency' OPT_DESC['F']='target frequency' +OPT_HAS_ARG['F']=1 OPT_VAL['F']=821 + +OPT_NAME['k']='sigmak' OPT_DESC['k']='sigma k value' +OPT_HAS_ARG['k']=1 OPT_VAL['k']=2.1 + +OPT_NAME['n']='frames' OPT_DESC['n']='test frames' +OPT_HAS_ARG['n']=1 OPT_VAL['n']=240000 + +OPT_NAME['s']='sof-logger' OPT_DESC['s']="Open sof-logger trace the data will store at $LOG_ROOT" +OPT_HAS_ARG['s']=0 OPT_VAL['s']=1 + +OPT_NAME['d']='duration' OPT_DESC['d']='duration time for socwatch to collect the data' +OPT_HAS_ARG['d']=1 OPT_VAL['d']=10 + +: "${SOCWATCH_PATH:=$HOME/socwatch}" +SOCWATCH_VERSION=$(sudo "$SOCWATCH_PATH"/socwatch --version | grep Version) + +func_opt_parse_option "$@" +setup_kernel_check_point + +pcm_p=${OPT_VAL['p']} +pcm_c=${OPT_VAL['c']} +rate=${OPT_VAL['r']} +channel_c=${OPT_VAL['C']} +channel_p=${OPT_VAL['N']} +format=${OPT_VAL['f']} +frequency=${OPT_VAL['F']} +sigmak=${OPT_VAL['k']} +frames=${OPT_VAL['n']} +duration=${OPT_VAL['d']} + +analyze_socwatch_results() +{ + pc_states_file="$LOG_ROOT/pc_states.csv" + touch "$pc_states_file" + results=$(cat "$socwatch_output".csv | grep "Platform Monitoring Technology CPU Package C-States Residency Summary: Residency" -A 10) + echo "$results" | tee "$pc_states_file" + + expected_results='{"PC0":12.00, "PC2":88, "PC6.1":0, "PC6.2":11, "PC10.1":2, "PC10.2":72, "PC10.3":0}' + + # Analyze if the % of the time spent in given PC state was as expected + if python3 "$SCRIPT_HOME"/tools/analyze-pc-states.py "$pc_states_file" "$expected_results"; then + dlogi "All Package Residency (%) values were as expected" + else + die "Some Package Residency (%) values different from expected!" + fi +} + +check_for_PC10_state() +{ + pc10_count=$(awk '/Package C-State Summary: Entry Counts/{f=1; next} f && /PC10/{print $3; exit}' "$socwatch_output".csv) + if [ -z "$pc10_count" ]; then + die "PC10 State not achieved" + fi + dlogi "Entered into PC10 State $pc10_count times" + + pc10_per=$(awk '/Package C-State Summary: Residency/{f=1; next} f && /PC10/{print $3; exit}' "$socwatch_output".csv) + pc10_time=$(awk '/Package C-State Summary: Residency/{f=1; next} f && /PC10/{print $5; exit}' "$socwatch_output".csv) + dlogi "Spent $pc10_time ms ($pc10_per %) in PC10 State" + + json_str=$( jq -n \ + --arg id "$i" \ + --arg cnt "$pc10_count" \ + --arg time "$pc10_time" \ + --arg per "$pc10_per" \ + '{$id: {pc10_entires_count: $cnt, time_ms: $time, time_percentage: $per}}' ) + + results=$(jq --slurp 'add' <(echo "$results") <(echo "$json_str")) +} + +check_the_pcms() +{ + aplay "-Dplug${pcm_p}" -d 1 /dev/zero -q || die "Failed to play on PCM: ${pcm_p}" + arecord "-Dplug${pcm_c}" -d 1 /dev/null -q || die "Failed to capture on PCM: ${pcm_c}" +} + +# Checks for soundfile needed for test, generates missing ones +prepare_test_soundfile() +{ + mkdir -p "$HOME/Music" + if [ ! -f "$audio_filename" ]; then + generate_mp3_file "$audio_filename" + fi +} + +run_test() +{ + check_the_pcms + # audio_filename="$HOME/Music/test.mp3" + # prepare_test_soundfile + + socwatch_output="$LOG_ROOT/socwatch-results/socwatch_report" + + # play_command="cplay -D${pcm_p} -d ${duration} ${audio_filename}" + play_command=(aplay -Dplug${pcm_p} -d ${duration} /dev/zero) + run_with_socwatch "$socwatch_output" "${play_command[@]}" + + analyze_socwatch_results +} + +main() +{ + export RUN_SOCWATCH=true + start_test + if [ "$pcm_p" = "" ]||[ "$pcm_c" = "" ]; + then + dloge "No playback or capture PCM specified." + exit 2 + fi + logger_disabled || func_lib_start_log_collect + + run_test +} + +{ + main "$@"; exit "$?" +} diff --git a/tools/analyze-pc-states.py b/tools/analyze-pc-states.py new file mode 100644 index 00000000..6b12dc3c --- /dev/null +++ b/tools/analyze-pc-states.py @@ -0,0 +1,46 @@ +import csv +import sys +import json +import re + +ACCEPTANCE_BUFFER = 2.0 + + +def compare_values(real, expected): + if abs(real-expected) <= ACCEPTANCE_BUFFER: + return True + return False + + +def analyze_pc_states(pc_states_file, expected_results): + pattern = re.compile(r'^PC(\d+)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*$') + failures = 0 + + with open(pc_states_file) as file: + for line in file: + m = pattern.match(line) + if m: + pc_state_nr = int(m.group(1)) + pc_state = "PC"+str(pc_state_nr) + value = float(m.group(2)) + expected_value = float(expected_results.get(pc_state)) + if not expected_value: + continue + if not compare_values(value, expected_value): + print(f"Incorrect value: {pc_state} time % was {value}, expected {expected_value}") + failures += 1 + + return 0 if failures == 0 else 1 + + +# This script analyzes if the % of the time spent in given PC state was as expected +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Incorrect number of args!") + sys.exit(1) + + pc_states_results_file = sys.argv[1] + pc_states_thresholds = json.loads(sys.argv[2]) + + result = analyze_pc_states(pc_states_results_file, pc_states_thresholds) + sys.exit(result) From 3ef77fe8f31f553dfba533bf25a7b430f33545a6 Mon Sep 17 00:00:00 2001 From: Emilia Kurdybelska Date: Thu, 19 Feb 2026 14:54:56 +0100 Subject: [PATCH 2/2] test-cplay: add test Signed-off-by: Emilia Kurdybelska --- case-lib/lib.sh | 3 ++- test-case/test-cplay.sh | 40 ++++++++-------------------------------- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/case-lib/lib.sh b/case-lib/lib.sh index 982be4e8..9491b3cd 100644 --- a/case-lib/lib.sh +++ b/case-lib/lib.sh @@ -1608,7 +1608,8 @@ analyze_mixed_sound() # Arguments: 1 - output filename generate_mp3_file() { - ffmpeg -f lavfi -i "sine=frequency=1000:duration=20" "$1" + mkdir -p "$HOME/Music" + ffmpeg -f lavfi -i "sine=frequency=1000:duration=20" -ac 2 "$1" } # Load socwatch and check if module was loaded correctly diff --git a/test-case/test-cplay.sh b/test-case/test-cplay.sh index 97560a57..710e8464 100644 --- a/test-case/test-cplay.sh +++ b/test-case/test-cplay.sh @@ -97,28 +97,6 @@ analyze_socwatch_results() fi } -check_for_PC10_state() -{ - pc10_count=$(awk '/Package C-State Summary: Entry Counts/{f=1; next} f && /PC10/{print $3; exit}' "$socwatch_output".csv) - if [ -z "$pc10_count" ]; then - die "PC10 State not achieved" - fi - dlogi "Entered into PC10 State $pc10_count times" - - pc10_per=$(awk '/Package C-State Summary: Residency/{f=1; next} f && /PC10/{print $3; exit}' "$socwatch_output".csv) - pc10_time=$(awk '/Package C-State Summary: Residency/{f=1; next} f && /PC10/{print $5; exit}' "$socwatch_output".csv) - dlogi "Spent $pc10_time ms ($pc10_per %) in PC10 State" - - json_str=$( jq -n \ - --arg id "$i" \ - --arg cnt "$pc10_count" \ - --arg time "$pc10_time" \ - --arg per "$pc10_per" \ - '{$id: {pc10_entires_count: $cnt, time_ms: $time, time_percentage: $per}}' ) - - results=$(jq --slurp 'add' <(echo "$results") <(echo "$json_str")) -} - check_the_pcms() { aplay "-Dplug${pcm_p}" -d 1 /dev/zero -q || die "Failed to play on PCM: ${pcm_p}" @@ -128,7 +106,6 @@ check_the_pcms() # Checks for soundfile needed for test, generates missing ones prepare_test_soundfile() { - mkdir -p "$HOME/Music" if [ ! -f "$audio_filename" ]; then generate_mp3_file "$audio_filename" fi @@ -137,13 +114,12 @@ prepare_test_soundfile() run_test() { check_the_pcms - # audio_filename="$HOME/Music/test.mp3" - # prepare_test_soundfile + audio_filename="$HOME/Music/test.mp3" + prepare_test_soundfile socwatch_output="$LOG_ROOT/socwatch-results/socwatch_report" - # play_command="cplay -D${pcm_p} -d ${duration} ${audio_filename}" - play_command=(aplay -Dplug${pcm_p} -d ${duration} /dev/zero) + play_command="cplay -d 0 -c 50 ${audio_filename}" run_with_socwatch "$socwatch_output" "${play_command[@]}" analyze_socwatch_results @@ -153,11 +129,11 @@ main() { export RUN_SOCWATCH=true start_test - if [ "$pcm_p" = "" ]||[ "$pcm_c" = "" ]; - then - dloge "No playback or capture PCM specified." - exit 2 - fi + # if [ "$pcm_p" = "" ]||[ "$pcm_c" = "" ]; + # then + # dloge "No playback or capture PCM specified." + # exit 2 + # fi logger_disabled || func_lib_start_log_collect run_test