Skip to content

Commit 3c0746c

Browse files
committed
test: PC10 residency and resume time
Signed-off-by: Emilia Kurdybelska <emiliax.kurdybelska@intel.com>
1 parent 60f43ba commit 3c0746c

File tree

5 files changed

+258
-42
lines changed

5 files changed

+258
-42
lines changed

test-case/check-suspend-resume.sh

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,33 @@ source "$TOPDIR"/case-lib/lib.sh
2626
random_min=3 # wait time should >= 3 for other device wakeup from sleep
2727
random_max=20
2828

29-
OPT_NAME['l']='loop' OPT_DESC['l']='loop count'
30-
OPT_HAS_ARG['l']=1 OPT_VAL['l']=5
29+
OPT_NAME['l']='loop' OPT_DESC['l']='loop count'
30+
OPT_HAS_ARG['l']=1 OPT_VAL['l']=5
3131

32-
OPT_NAME['T']='type' OPT_DESC['T']="suspend/resume type from /sys/power/mem_sleep"
33-
OPT_HAS_ARG['T']=1 OPT_VAL['T']=""
32+
OPT_NAME['T']='type' OPT_DESC['T']="suspend/resume type from /sys/power/mem_sleep"
33+
OPT_HAS_ARG['T']=1 OPT_VAL['T']=""
3434

35-
OPT_NAME['S']='sleep' OPT_DESC['S']='suspend/resume command:rtcwake sleep duration'
36-
OPT_HAS_ARG['S']=1 OPT_VAL['S']=5
35+
OPT_NAME['S']='sleep' OPT_DESC['S']='suspend/resume command:rtcwake sleep duration'
36+
OPT_HAS_ARG['S']=1 OPT_VAL['S']=5
3737

38-
OPT_NAME['u']='unload-audio' OPT_DESC['u']='unload audio modules for the test'
39-
OPT_HAS_ARG['u']=0 OPT_VAL['u']=0
38+
OPT_NAME['u']='unload-audio' OPT_DESC['u']='unload audio modules for the test'
39+
OPT_HAS_ARG['u']=0 OPT_VAL['u']=0
4040

41-
OPT_NAME['w']='wait' OPT_DESC['w']='idle time after suspend/resume wakeup'
42-
OPT_HAS_ARG['w']=1 OPT_VAL['w']=5
41+
OPT_NAME['w']='wait' OPT_DESC['w']='idle time after suspend/resume wakeup'
42+
OPT_HAS_ARG['w']=1 OPT_VAL['w']=5
4343

44-
OPT_NAME['r']='random' OPT_DESC['r']="Randomly setup wait/sleep time, range is [$random_min-$random_max], this option will overwrite s & w option"
45-
OPT_HAS_ARG['r']=0 OPT_VAL['r']=0
44+
OPT_NAME['r']='random' OPT_DESC['r']="Randomly setup wait/sleep time, range is [$random_min-$random_max], this option will overwrite s & w option"
45+
OPT_HAS_ARG['r']=0 OPT_VAL['r']=0
4646

4747
# processid is set by check-suspend-resume-with-audio.sh for audio test case
48-
OPT_NAME['p']='processid' OPT_DESC['p']='Fail immediately if this process dies'
49-
OPT_HAS_ARG['p']=1 OPT_VAL['p']=''
48+
OPT_NAME['p']='processid' OPT_DESC['p']='Fail immediately if this process dies'
49+
OPT_HAS_ARG['p']=1 OPT_VAL['p']=''
50+
51+
OPT_NAME['tt']='times-thresholds' OPT_DESC['tt']='sleepgraph thresholds'
52+
OPT_HAS_ARG['tt']=1 OPT_VAL['tt']=''
53+
54+
OPT_NAME['st']='states-thresholds' OPT_DESC['st']='power states min % values'
55+
OPT_HAS_ARG['st']=1 OPT_VAL['st']=''
5056

5157
func_opt_parse_option "$@"
5258
func_lib_check_sudo
@@ -170,6 +176,54 @@ main()
170176
die "Found kernel error after reloading audio drivers"
171177
}
172178

179+
analyze_sleepgraph_results()
180+
{
181+
dlogi "Analyzing sleepgraph results"
182+
results_file=$(ls suspend-* | grep html)
183+
thresholds=${OPT_VAL['tt']}
184+
185+
dlogi "Analyzing $results_file file..."
186+
if python3 "$SCRIPT_HOME"/tools/analyze-sleepgraph-results.py "$results_file" "$thresholds"; then
187+
dlogi "All times measurements within the thresholds"
188+
return 0
189+
else
190+
dlogw "Time measurements not within the thresholds!"
191+
return 1
192+
fi
193+
}
194+
195+
analyze_powertop_results()
196+
{
197+
dlogi "Analyzing powertop results"
198+
results_file=$(ls suspend-* | grep html)
199+
thresholds=${OPT_VAL['st']}
200+
201+
dlogi "Analyzing $results_file file..."
202+
if python3 "$SCRIPT_HOME"/tools/analyze-powertop-results.py "$results_file" "$thresholds"; then
203+
dlogi "All times measurements within the thresholds"
204+
return 0
205+
else
206+
dlogw "Time measurements not within the thresholds!"
207+
return 1
208+
fi
209+
}
210+
211+
run_rtcwake()
212+
{
213+
if [[ "$RUN_SLEEPGRAPH" == true ]]; then
214+
dlogc "Run the command: $SLEEPGRAPH_PATH -rtcwake ${sleep_lst[$i]} -m freeze" #TODO: change it back to mem
215+
sudo powertop --csv="$log_dir"/powertop.csv -w "$HOME/pm-graph/sleepgraph.py -rtcwake 5 -m freeze"
216+
sudo "$SLEEPGRAPH_PATH" -rtcwake "${sleep_lst[$i]}" -m freeze ||
217+
dump_and_die "rtcwake returned $?"
218+
analyze_sleepgraph_results
219+
220+
else
221+
dlogc "Run the command: rtcwake -m mem -s ${sleep_lst[$i]}"
222+
sudo rtcwake -m mem -s "${sleep_lst[$i]}" ||
223+
dump_and_die "rtcwake returned $?"
224+
fi
225+
}
226+
173227
sleep_once()
174228
{
175229
local i="$1"
@@ -179,9 +233,7 @@ sleep_once()
179233
setup_kernel_check_point
180234
expected_wakeup_count=$((expected_wakeup_count+1))
181235
expected_stats_success=$((expected_stats_success+1))
182-
dlogc "Run the command: rtcwake -m mem -s ${sleep_lst[$i]}"
183-
sudo rtcwake -m mem -s "${sleep_lst[$i]}" ||
184-
dump_and_die "rtcwake returned $?"
236+
run_rtcwake
185237
dlogc "sleep for ${wait_lst[$i]}"
186238
sleep ${wait_lst[$i]}
187239
dlogi "Check for the kernel log status"

test-case/sleepgraph-wrapper.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
##
4+
## Case Name: Wrapper to run a given test case with sleepgraph.py in setup that cannot set the environment variable.
5+
## Keep this script as simple as possible and avoid additional layers of indirections when possible.
6+
## Preconditions:
7+
## Sleepgraph.py script is available.
8+
## Description:
9+
## This script serves as a wrapper to execute a test case script with additionally running sleepgraph.py.
10+
## It expects the test case script file name (without path) as the first parameter,
11+
## followed by other parameters required for that test case.
12+
##
13+
14+
set -e
15+
16+
# Ensure the test case script file name is provided
17+
if [ -z "$1" ]; then
18+
echo "Error: No test case script file name provided. Exiting..."
19+
exit 1
20+
fi
21+
22+
# Check if sleepgraph is installed
23+
export SLEEPGRAPH_PATH="$HOME/pm-graph/sleepgraph.py"
24+
if [ ! -f "$SLEEPGRAPH_PATH" ]; then
25+
echo "Sleepgraph is not installed! Exiting..."
26+
exit 1
27+
fi
28+
29+
export RUN_SLEEPGRAPH=true
30+
31+
TESTDIR=$(realpath -e "$(dirname "${BASH_SOURCE[0]}")/..")
32+
33+
# shellcheck disable=SC2145
34+
[ -x "$TESTDIR/test-case/$(basename "$1")" ] && exec "$TESTDIR"/test-case/"$@"

test-case/test-socwatch.sh

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ OPT_NAME['u']='unload-audio' OPT_DESC['u']='unload audio modules for the test'
3838
OPT_HAS_ARG['u']=0 OPT_VAL['u']=0
3939

4040
: "${SOCWATCH_PATH:=$HOME/socwatch}"
41-
SOCWATCH_VERSION=$("$SOCWATCH_PATH"/socwatch --version |grep Version)
41+
SOCWATCH_VERSION=$(sudo "$SOCWATCH_PATH"/socwatch --version |grep Version)
4242

4343
# reference cmd: sudo ./socwatch -t 20 -s 5 -f cpu-cstate -f pkg-pwr -o fredtest5
4444
#SOCWATCH_CMD="./socwatch"
@@ -57,41 +57,57 @@ check_socwatch_module_loaded()
5757
lsmod | grep -q socwatch || dlogi "socwatch is not loaded"
5858
}
5959

60+
check_for_PC10_state()
61+
{
62+
pc10_count=$(awk '/Package C-State Summary: Entry Counts/{f=1; next} f && /PC10/{print $3; exit}' $socwatch_output.csv)
63+
if [ -z "$pc10_count" ]; then
64+
die "PC10 State not achieved"
65+
fi
66+
dlogi "Entered into PC10 State $pc10_count times"
67+
68+
pc10_per=$(awk '/Package C-State Summary: Residency/{f=1; next} f && /PC10/{print $3; exit}' $socwatch_output.csv)
69+
pc10_time=$(awk '/Package C-State Summary: Residency/{f=1; next} f && /PC10/{print $5; exit}' $socwatch_output.csv)
70+
dlogi "Spent $pc10_time ms ($pc10_per %) in PC10 State"
71+
72+
json_str=$( jq -n \
73+
--arg id "$i" \
74+
--arg cnt "$pc10_count" \
75+
--arg time "$pc10_time" \
76+
--arg per "$pc10_per" \
77+
'{$id: {pc10_entires_count: $cnt, time_ms: $time, time_percentage: $per}}' )
78+
79+
results=$(jq --slurp 'add' <(echo "$results") <(echo "$json_str"))
80+
}
81+
6082
socwatch_test_once()
6183
{
6284
local i="$1"
6385
dlogi "===== Loop($i/$loop_count) ====="
6486
dlogi "SoCWatch version: ${SOCWATCH_VERSION}"
6587

88+
socwatch_output="$LOG_ROOT/socwatch-results/socwatch_output_$i"
89+
6690
# set up checkpoint for each iteration
6791
setup_kernel_check_point
6892

69-
# load socwatch module, if the module is loaded, go ahead with the testing (-q)
70-
sudo "$SOCWATCH_PATH"/drivers/insmod-socwatch -q || true
71-
check_socwatch_module_loaded || die "socwatch module not loaded"
72-
7393
( set -x
74-
sudo "$SOCWATCH_PATH"/socwatch -t "$duration" -s "$wait_time" "${SOCWATCH_FEATURE_PARAMS[@]}" -o "$SOCWATCH_PATH/sofsocwatch-$i" ) ||
94+
sudo "$SOCWATCH_PATH"/socwatch -m -f sys -f cpu -f cpu-hw -f pcie -f hw-cpu-cstate \
95+
-f pcd-slps0 -f tcss-state -f tcss -f pcie-lpm -n 200 -t "$duration" -s "$wait_time" \
96+
-r json -o "$socwatch_output" ) ||
7597
die "socwatch returned $?"
7698

77-
# filter output and copy to log directory
78-
grep "Package C-State Summary: Residency" -B 8 -A 11 "$SOCWATCH_PATH/sofsocwatch-$i.csv" | tee "$SOCWATCH_PATH/socwatch-$i.txt"
79-
grep "Package Power Summary: Average Rate" -B 6 -A 4 "$SOCWATCH_PATH/sofsocwatch-$i.csv" | tee -a "$SOCWATCH_PATH/socwatch-$i.txt"
80-
# zip original csv report
81-
gzip "$SOCWATCH_PATH/sofsocwatch-$i.csv"
82-
mv "$SOCWATCH_PATH/socwatch-$i.txt" "$SOCWATCH_PATH/sofsocwatch-$i.csv.gz" "$LOG_ROOT"/
99+
# analyze SoCWatch results
100+
check_for_PC10_state
83101

84-
dlogi "Check for the kernel log status"
85102
# check kernel log for each iteration to catch issues
103+
dlogi "Check for the kernel log status"
86104
sof-kernel-log-check.sh "$KERNEL_CHECKPOINT" || die "Caught error in kernel log"
87-
88-
# unload socwatch module
89-
sudo "$SOCWATCH_PATH"/drivers/rmmod-socwatch || true
90105
}
91106

92-
main()
107+
unload_modules()
93108
{
94-
local keep_modules=true already_unloaded=false
109+
keep_modules=true
110+
already_unloaded=false
95111

96112
[ -d "$SOCWATCH_PATH" ] ||
97113
die "SOCWATCH not found in SOCWATCH_PATH=$SOCWATCH_PATH"
@@ -108,20 +124,44 @@ main()
108124

109125
$already_unloaded || $keep_modules || "$TOPDIR"/tools/kmod/sof_remove.sh ||
110126
die "Failed to unload audio drivers"
127+
}
128+
129+
load_modules()
130+
{
131+
$already_unloaded || $keep_modules || "$TOPDIR"/tools/kmod/sof_insert.sh ||
132+
die "Failed to reload audio drivers"
133+
sof-kernel-log-check.sh "$KERNEL_CHECKPOINT" ||
134+
die "Found kernel error after reloading audio drivers"
135+
}
136+
137+
run_socwatch_tests()
138+
{
139+
# load socwatch module, if the module is loaded, go ahead with the testing
140+
sudo "$SOCWATCH_PATH"/drivers/insmod-socwatch || true
141+
check_socwatch_module_loaded || die "socwatch module not loaded"
142+
143+
mkdir "$LOG_ROOT/socwatch-results"
144+
pc10_results_file="$LOG_ROOT/socwatch-results/pc10_results.json"
145+
touch "$pc10_results_file"
111146

112-
# socwatch test from here
113147
for i in $(seq 1 "$loop_count")
114148
do
115149
socwatch_test_once "$i"
116150
done
151+
echo "$results" > "$pc10_results_file"
117152

118-
$already_unloaded || $keep_modules || "$TOPDIR"/tools/kmod/sof_insert.sh ||
119-
die "Failed to reload audio drivers"
120-
sof-kernel-log-check.sh "$KERNEL_CHECKPOINT" ||
121-
die "Found kernel error after reloading audio drivers"
153+
# zip all SoCWatch reports
154+
tar -zcvf "$LOG_ROOT/socwatch-results.tar.gz" "$LOG_ROOT/socwatch-results/"
122155

123-
# DON"T delete socwatch directory after test, delete before new test
124-
# rm -rf $SOCWATCH_PATH
156+
# unload socwatch module
157+
sudo "$SOCWATCH_PATH"/drivers/rmmod-socwatch
158+
}
159+
160+
main()
161+
{
162+
unload_modules
163+
run_socwatch_tests
164+
load_modules
125165
}
126166

127167
main "$@"

tools/analyze-powertop-results.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import re
2+
import sys
3+
import json
4+
5+
6+
def analyze_powertop_file(filename, thresholds):
7+
results={}
8+
test_passed=True
9+
regex_pattern = r'^\s*C\d+;\s*\d+(\.\d+)?%$' # Search for lines like: C1; 0,5%
10+
11+
with open(filename, "r", encoding="utf-8") as f:
12+
for line in f:
13+
if re.match(regex_pattern, line):
14+
text = line.strip().split("; ")
15+
power_state = text[0]
16+
percentage = float(text[1].strip("%"))
17+
results[power_state] = {"percentage_value": percentage}
18+
print(f"{power_state}: {percentage}%")
19+
if power_state in thresholds:
20+
results[power_state] = {"percentage_value": percentage, "pass": True}
21+
if percentage < float(thresholds[power_state].strip("%")):
22+
results[power_state]["pass"] = False
23+
test_passed = False
24+
25+
26+
print(results)
27+
return test_passed, results
28+
29+
30+
31+
if __name__ == "__main__":
32+
if len(sys.argv) != 3:
33+
print("Incorrect args. Usage: python3 analyze-powertop-results.py <path_to_csv_file> <expected_pc10_%")
34+
sys.exit(1)
35+
36+
filename = sys.argv[1]
37+
thresholds = json.loads(sys.argv[2])
38+
39+
test_passed, results = analyze_powertop_file(filename, thresholds)
40+
print(f"Powertop report analysis passed: {test_passed}")
41+
sys.exit(0 if test_passed else 1)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from bs4 import BeautifulSoup
2+
import re
3+
import sys
4+
import json
5+
6+
ACCEPTANCE_RANGE=0.2
7+
8+
9+
def analyze_sleepgraph_file(file, thresholds, acceptance_range=ACCEPTANCE_RANGE):
10+
with open(file, 'r', encoding='utf-8') as f:
11+
soup = BeautifulSoup(f, 'lxml')
12+
complete_results={}
13+
test_passed=True
14+
15+
components=thresholds.keys()
16+
for component in components:
17+
results = {}
18+
divs = soup.find_all("div", title=lambda t: t and component in t)
19+
20+
for div in divs:
21+
title = div.get('title')
22+
match = re.search(r'\((\d+(?:\.\d+)?)\s*ms\)\s+(\S+)$', title)
23+
if match:
24+
time_ms = float(match.group(1))
25+
measurement_name = match.group(2)
26+
if measurement_name in thresholds[component]:
27+
results[measurement_name] = {"value": time_ms, "pass": True}
28+
threshold = float(thresholds[component][measurement_name])
29+
if time_ms<(threshold * (1-acceptance_range)) and time_ms>(threshold * (1+acceptance_range)):
30+
results[measurement_name]["pass"] = False
31+
test_passed = False
32+
33+
complete_results[component]=results
34+
35+
print(complete_results)
36+
return test_passed, complete_results
37+
38+
39+
if __name__ == "__main__":
40+
if len(sys.argv) != 3:
41+
print("Incorrect args. Usage: python3 analyze-sleepgraph-results.py <path_to_html_file> <thresholds_as_json_str>")
42+
sys.exit(1)
43+
44+
sleepgraph_file = sys.argv[1]
45+
thresholds = json.loads(sys.argv[2])
46+
47+
test_passed, results = analyze_sleepgraph_file(sleepgraph_file, thresholds)
48+
print(f"Sleepgraph report analysis passed: {test_passed}")
49+
sys.exit(0 if test_passed else 1)

0 commit comments

Comments
 (0)