[Tools] (bench): clock profiles, bench sessions that release the perf stack's frequency caps, and a per-run frequency/thermal validity line.

This commit is contained in:
2026-10-08 09:31:26 -04:00
parent 4e27342e81
commit efa415b00c
8 changed files with 260 additions and 6 deletions
+21
View File
@@ -134,3 +134,24 @@ Reducers, all reading a probe directory:
The simpleperf reducers need `SIMPLEPERF_DIR` and a `binary_cache` from `binary_cache_builder.py`.
Findings: `docs/Disaggregated/notes/perf-p15/PLAN-P15.md` section 2.
## Clocks, bench sessions and run validity (P15)
Every performance number carries the device state it was measured under.
- **`pin_clocks.sh`** pins CPU and GPU to a named profile (`PROFILE=sustained|gpumax|gpulow|cpulow|cpuhunt|gpuhunt`,
see the script header; `sustained` is the default) and records it on the device.
- **`bench_session.sh start|stop`**:
- `start` records, then stops, the services that move frequency caps (`thermal-engine`, the QTI perf HAL,
`vendor.perfservice`, Lenovo's `performance` and `hyperschedule_hal_service`). It releases their leftover
requests in `/sys/kernel/msm_performance/parameters/cpu_{max,min}_freq`, which otherwise clamp
`scaling_max_freq` under any pin, and then pins.
- `stop` restarts every service that was running and verifies it is running again.
- Wrap every device job so `stop` always runs (trap on EXIT/INT/TERM).
- **`freq_sampler.sh`** runs on the device for each measurement window and samples every 2 s: CPU cur/max per
policy, GPU cur, kgsl thermal level, busy and temperatures.
- **`freqcheck.py`** decides VALID/INVALID against the intended pin (one OPP of tolerance, GPU checked while
busy). Invalid runs are discarded and re-run after cool-down.
- **`devstate.sh <outdir> [trace]`** writes the per-run `state.txt` line: profile and clocks, validity with the
observed min/max frequencies and peak temperatures, daemons, MC version, swap interval and anland on/off.
`fcl_probe.sh` and `matrix_p14.sh` (benchmark mode) call it.
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# bench_session.sh start|stop|status - P15 measurement-validity rule, device side (Lenovo Y700, SM8650).
#
# start:
# 1. records each frequency-moving service's prior state and the msm_performance requests
# (/data/local/tmp/p15-bench-session, also echoed to the caller's log);
# 2. stops those services: thermal-engine, the QTI perf HAL, the vendor perf service, Lenovo's
# performance and hyperschedule services. Their requests OUTLIVE them: the perf stack's
# game-mode levels land in /sys/kernel/msm_performance/parameters/cpu_{max,min}_freq, a
# frequency QoS that clamps scaling_max_freq below any pin (measured 2026-10-08: 787 / 1286 /
# 1075 / 1248 MHz on policies 0/2/5/7 while pin_clocks.sh believed it had pinned 2035 MHz);
# 3. releases those requests and re-pins (pin_clocks.sh).
# stop: starts every service that was running before start, and verifies each is running again.
# ALWAYS run it - callers trap it on EXIT/INT/TERM, so an interrupted session restores too.
# Kernel-level protection (LMh, BCL, kernel thermal trip points) is never touched; the run's sampler
# (freq_sampler.sh -> freqcheck.py) shows any frequency collapse, and a collapse ends benchmarking.
set -u
export MSYS_NO_PATHCONV=1
SER="${SER:-HA27Q3LQ}"
HERE="$(cd "$(dirname "$0")" && pwd)"
SERVICES="${BENCH_STOP_SERVICES:-thermal-engine perf2-hal-1-0 vendor.perfservice performance hyperschedule_hal_service}"
MARK=/data/local/tmp/p15-bench-session
MP=/sys/kernel/msm_performance/parameters
su_sh() { adb -s "$SER" shell "su -c '$1'"; }
case "${1:-status}" in
start)
su_sh "if [ ! -f $MARK ]; then for s in $SERVICES; do echo svc \$s \$(getprop init.svc.\$s); done > $MARK; echo maxreq \$(cat $MP/cpu_max_freq) >> $MARK; echo minreq \$(cat $MP/cpu_min_freq) >> $MARK; fi; cat $MARK"
su_sh "for s in $SERVICES; do setprop ctl.stop \$s; done"
sleep 2
su_sh "echo \"0:4294967295 1:4294967295 2:4294967295 3:4294967295 4:4294967295 5:4294967295 6:4294967295 7:4294967295\" > $MP/cpu_max_freq; echo \"0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0\" > $MP/cpu_min_freq"
SER=$SER bash "$HERE/pin_clocks.sh" pin
"$0" status ;;
stop)
su_sh "if [ -f $MARK ]; then grep \"^svc \" $MARK | while read k s st; do [ \"\$st\" = running ] && setprop ctl.start \$s; done; else for s in $SERVICES; do setprop ctl.start \$s; done; fi"
sleep 3
bad=$(su_sh "if [ -f $MARK ]; then grep \"^svc \" $MARK | while read k s st; do [ \"\$st\" = running ] && [ \"\$(getprop init.svc.\$s)\" != running ] && echo \$s; done; fi" | tr -d '\r')
if [ -n "$bad" ]; then echo "bench_session: NOT RESTORED: $bad" >&2; exit 1; fi
su_sh "rm -f $MARK"
"$0" status ;;
status)
su_sh "for s in $SERVICES; do echo \$s=\$(getprop init.svc.\$s); done | tr '\n' ' '; echo; echo maxreq \$(cat $MP/cpu_max_freq)" ;;
esac
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# devstate.sh <outdir>: the one-line device state every reported number carries (P15 measurement rules).
# Needs <outdir>/freq.txt + pin.txt (freq_sampler / pin_clocks show). Writes <outdir>/profile.txt,
# <outdir>/valid.txt and <outdir>/state.txt, and prints the state line:
# STATE profile=<name>(cpu/little/gpu MHz) <VALID|INVALID ... freqcheck summary> daemons=<..> swap=<..> anland=<on|off>
set -u
export MSYS_NO_PATHCONV=1
out="$1"; kind="${2:-fcl}"; SER="${SER:-HA27Q3LQ}"; HERE="$(cd "$(dirname "$0")" && pwd)"
FCL=com.tungsten.fcl.mgdebug.debug
su_sh() { adb -s "$SER" shell "su -c '$1'"; }
su_sh "cat /data/local/tmp/p15-bench-profile 2>/dev/null || echo profile=unpinned" | tr -d '\r' > "$out/profile.txt"
python "$(cygpath -w "$HERE/freqcheck.py" 2>/dev/null || echo "$HERE/freqcheck.py")" "$(cygpath -w "$out/freq.txt")" "$(cygpath -w "$out/pin.txt")" > "$out/valid.txt" 2>&1
daemons=$(su_sh "test -f /data/local/tmp/p15-bench-session && echo stopped\(\$(grep -c \"^svc \" /data/local/tmp/p15-bench-session)\) || echo running" | tr -d '\r')
ver=$(adb -s "$SER" exec-out "su -c 'cat /data/data/$FCL/files/config.json'" 2>/dev/null | grep -o '"selectedMinecraftVersion": *"[^"][^"]*"' | head -1 | sed 's/.*: *"//; s/"$//')
swap=$(su_sh "grep -h enableVsync /sdcard/FCL/.minecraft/versions/$ver/options.txt 2>/dev/null" | tr -d '\r' | sed 's/enableVsync://')
anland=$(adb -s "$SER" shell pidof com.anland.consumer.mobilegl | tr -dc '0-9' | head -c 8)
p=$(cat "$out/profile.txt")
pn=$(echo "$p" | sed -n 's/.*profile=\([^ ]*\).*/\1/p'); pc=$(echo "$p" | sed -n 's/.*cpu=\([0-9]*\).*/\1/p'); pl=$(echo "$p" | sed -n 's/.*little=\([0-9]*\).*/\1/p'); pg=$(echo "$p" | sed -n 's/.*gpu=\([0-9]*\).*/\1/p')
[ "$kind" = trace ] && { ver=-; swap=pbuffer; }
line="STATE profile=${pn:-unpinned}(${pc:+$((pc/1000))/$((pl/1000))/$((pg/1000000))MHz}) $(head -1 "$out/valid.txt") daemons=$daemons mc=$ver swap=${swap:-?} anland=$([ -n "$anland" ] && echo on || echo off)"
echo "$line" > "$out/state.txt"; echo "$line"
+8
View File
@@ -36,6 +36,11 @@ echo "waited=$t ok=$ok attempt=$attempt" >> "$out/wait.txt"
pid=$(adb -s $SER shell pidof $FCL | tr -d '\r' | awk '{print $NF}')
echo "pid=$pid" >> "$out/wait.txt"
snap() { adb -s $SER shell "su -c 'cat /proc/uptime; cat /sys/class/kgsl/kgsl-3d0/gpu_clock_stats; for t in /proc/$pid/task/*; do echo T \${t##*/} \$(cut -d\" \" -f1-3 \$t/schedstat) \$(cat \$t/comm); done'" > "$1"; }
# P15 validity rule: frequency/thermal sampler for the whole measurement window (devstate.sh judges it)
BENCH="$(cd "$(dirname "$0")" && pwd)"
SER=$SER bash $BENCH/pin_clocks.sh show > "$out/pin.txt" 2>&1
adb -s $SER push "$(cygpath -w $BENCH/freq_sampler.sh)" /data/local/tmp/p15-freq_sampler.sh >/dev/null
su_sh "chmod 755 /data/local/tmp/p15-freq_sampler.sh; rm -f /data/local/tmp/p15freq.txt; (nohup sh /data/local/tmp/p15-freq_sampler.sh /data/local/tmp/p15freq.txt 2 >/dev/null 2>&1 &)"
# A: ftrace
EV=/sys/kernel/tracing/events
su_sh "atrace --async_start -c -b 65536 -a $FCL gfx view sched freq >/dev/null 2>&1; for e in adreno_cmdbatch_submitted adreno_cmdbatch_retired kgsl_waittimestamp_entry kgsl_waittimestamp_exit adreno_syncobj_submitted adreno_syncobj_retired adreno_drawctxt_wait_start adreno_drawctxt_wait_done kgsl_timeline_wait; do echo 1 > $EV/kgsl/\$e/enable; done; echo 1 > $EV/sched/sched_waking/enable"
@@ -52,6 +57,9 @@ if [ "$sp" = 1 ]; then
su_sh "chmod 644 /data/local/tmp/p15perf.data"
adb -s $SER pull /data/local/tmp/p15perf.data "$(cygpath -w "$out/perf.data")" >/dev/null
fi
su_sh "pkill -f p15-freq_sampler.sh; chmod 644 /data/local/tmp/p15freq.txt"
adb -s $SER pull /data/local/tmp/p15freq.txt "$(cygpath -w "$out/freq.txt")" >/dev/null
SER=$SER bash $BENCH/devstate.sh "$out"
adb -s $SER logcat -d > "$out/logcat.txt"
su_sh "am force-stop $FCL"
grep -a "MobileGL fps:" "$out/logcat.txt" | tail -8 | sed 's/.*MobileGL fps: //' > "$out/fps.txt"
+26
View File
@@ -0,0 +1,26 @@
#!/system/bin/sh
# freq_sampler.sh <outfile> [period_s=2] - DEVICE-SIDE (pushed to /data/local/tmp, run as root in the
# background for the length of one measurement, killed by the caller). One line per sample:
# t=<uptime> c<policy>=<cur>/<max> ... gpu=<devfreq cur> gpuclk=<kgsl gpuclk> tpl=<thermal_pwrlevel>
# thr=<throttling> busy=<gpu_busy_percentage> cpuT=<hottest cpu zone> gpuT=<hottest gpuss zone> skinT=<skin>
# Cheap sysfs reads only, so the run it watches is not perturbed. freqcheck.py turns the file into
# min/max/peak and a VALID/INVALID verdict (P15 measurement-validity rule).
o=$1; p=${2:-2}; : > "$o"
K=/sys/class/kgsl/kgsl-3d0
zones=""
for z in /sys/class/thermal/thermal_zone*; do
case $(cat $z/type) in cpu-*|cpuss-*|gpuss-*|skin-msm-therm) zones="$zones $z";; esac
done
while :; do
l="t=$(cut -d' ' -f1 /proc/uptime)"
for c in /sys/devices/system/cpu/cpufreq/policy*; do
l="$l c${c##*policy}=$(cat $c/scaling_cur_freq)/$(cat $c/scaling_max_freq)"
done
m=0; g=0; s=0
for z in $zones; do
v=$(cat $z/temp 2>/dev/null); [ -z "$v" ] && continue
case $(cat $z/type) in cpu-*|cpuss-*) [ $v -gt $m ] && m=$v;; gpuss-*) [ $v -gt $g ] && g=$v;; skin-msm-therm) s=$v;; esac
done
echo "$l gpu=$(cat $K/devfreq/cur_freq) gpuclk=$(cat $K/gpuclk) tpl=$(cat $K/thermal_pwrlevel) thr=$(cat $K/throttling) busy=$(cat $K/gpu_busy_percentage | tr -d ' %') cpuT=$m gpuT=$g skinT=$s" >> "$o"
sleep $p
done
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""freqcheck.py <freq.txt> [pin.txt] - P15 measurement-validity verdict for one run.
freq.txt is freq_sampler.sh output; pin.txt is the `pin_clocks.sh show` output taken when the
session pinned (bench_session.sh writes it). A run is VALID only if every sample shows each CPU
policy at its pinned frequency, with its scaling_max still at the pin (no cap), and - whenever the
GPU is busy - the GPU at its pinned frequency, with no kgsl thermal power level or throttling.
One OPP step of tolerance is allowed: the next available frequency below the pin.
Prints one line: VALID|INVALID, per-policy min/max cur freq, GPU min/max (busy samples), peak
temperatures, and the reasons when invalid. Exit code 0 = valid, 1 = invalid, 2 = no samples.
"""
import re
import sys
def parse(path):
rows = []
for line in open(path, encoding="utf-8", errors="replace"):
kv = dict(re.findall(r"(\w+)=([^\s]+)", line))
if "t" in kv:
rows.append(kv)
return rows
def pins(path):
"""The INTENDED pin, from pin_clocks.sh's targets (env PIN_CPU_KHZ / PIN_LITTLE_KHZ / PIN_GPU_HZ,
same defaults) mapped onto each policy's available frequencies - never the caps read back from
the device, which a frequency daemon may already have lowered."""
import os
big = int(os.environ.get("PIN_CPU_KHZ", "2035200"))
little = int(os.environ.get("PIN_LITTLE_KHZ", "1574400"))
gpu = int(os.environ.get("PIN_GPU_HZ", "680000000"))
# The session's profile (pin_clocks.sh writes it on the device; probes save it as profile.txt).
prof = os.path.join(os.path.dirname(path), "profile.txt") if path else None
if prof and os.path.exists(prof):
kv = dict(re.findall(r"(\w+)=(\S+)", open(prof, encoding="utf-8", errors="replace").read()))
big = int(kv.get("cpu", big)); little = int(kv.get("little", little)); gpu = int(kv.get("gpu", gpu))
cpu, steps = {}, {}
if not path:
return cpu, gpu, steps
for line in open(path, encoding="utf-8", errors="replace"):
m = re.search(r"gpu cur=\d+ min=\d+ max=\d+ avail=([\d ]+)", line)
if m:
# kgsl power levels index this table from the top (level 0 = fastest).
steps["gpu"] = sorted((int(x) for x in m.group(1).split()), reverse=True)
for line in open(path, encoding="utf-8", errors="replace"):
m = re.search(r"policy(\d+) cur=\d+ min=(\d+) max=(\d+) avail=([\d ]+)", line)
if m:
avail = sorted(int(x) for x in m.group(4).split())
want = little if m.group(1) == "0" else big
ok = [a for a in avail if a <= want]
cpu[m.group(1)] = ok[-1] if ok else avail[0]
steps[m.group(1)] = avail
return cpu, gpu, steps
def main():
rows = parse(sys.argv[1])
cpu_pin, gpu_pin, steps = pins(sys.argv[2] if len(sys.argv) > 2 else None)
if not rows:
print("NO-SAMPLES")
sys.exit(2)
reasons = []
out = []
pols = sorted({k[1:] for k in rows[0] if re.fullmatch(r"c\d+", k)}, key=int)
for p in pols:
cur = [int(r["c" + p].split("/")[0]) for r in rows]
mx = [int(r["c" + p].split("/")[1]) for r in rows]
out.append(f"c{p}={min(cur) // 1000}-{max(cur) // 1000}MHz")
pin = cpu_pin.get(p)
if pin:
lower = [s for s in steps.get(p, []) if s < pin]
floor = lower[-1] if lower else pin
if min(cur) < floor:
reasons.append(f"c{p} cur {min(cur) // 1000} < pin {pin // 1000}")
if min(mx) < pin:
reasons.append(f"c{p} max capped to {min(mx) // 1000}")
busy = [r for r in rows if int(r.get("busy", "0") or 0) > 0]
gpus = [int(r["gpu"]) for r in (busy or rows)]
out.append(f"gpu={min(gpus) // 1000000}-{max(gpus) // 1000000}MHz(busy samples {len(busy)}/{len(rows)})")
if gpu_pin and busy and min(gpus) < gpu_pin * 0.85:
reasons.append(f"gpu {min(gpus) // 1000000} < pin {gpu_pin // 1000000}")
tpl = max(int(r.get("tpl", 0)) for r in rows)
thr = max(int(r.get("thr", 0)) for r in rows)
# A devfreq max_freq write (the pin itself) shows up as kgsl's thermal_pwrlevel; only a level
# whose frequency is below the pin is a cap.
table = steps.get("gpu", [])
if tpl and table and tpl < len(table):
if gpu_pin and table[tpl] < gpu_pin:
reasons.append(f"gpu thermal_pwrlevel {tpl} = {table[tpl] // 1000000} MHz < pin")
elif tpl and not table:
reasons.append(f"thermal_pwrlevel {tpl} (no OPP table to map it)")
out.append(f"tpl={tpl} thr={thr}")
peak = lambda k: max(int(r.get(k, 0)) for r in rows) / 1000.0
out.append(f"peak cpu {peak('cpuT'):.1f}C gpu {peak('gpuT'):.1f}C skin {peak('skinT'):.1f}C n={len(rows)}")
verdict = "VALID" if not reasons else "INVALID"
print(verdict, " ".join(out), ("| " + "; ".join(reasons)) if reasons else "")
sys.exit(0 if not reasons else 1)
if __name__ == "__main__":
main()
+12 -2
View File
@@ -51,6 +51,8 @@ wl_args() { # trace golden w h target crop
rd12) echo "rd12 minecraft-1.21.4-rd12-odinlite-in-world.0004660351.png 854 480 4660351 0 0 0 0" ;;
# P15: GPU-bound shaderpack scene (a single-frame trim: ssim only, not a benchmark).
bsl) echo "bsl minecraft-1.21.4-fabric-iris-bsl-in-world.0000110725.png 854 480 110725 0 0 0 0" ;;
# P15 S0b: FCL's MC 1.21.5 vanilla scene, a 252-frame window (benchmark + ssim on the final frame).
fcl) echo "fcl minecraft-1.21.5-vanilla-fcl-in-world.0005998912.png 854 480 5998912 0 0 0 0" ;;
esac
}
@@ -82,9 +84,10 @@ prep_pkg() {
openra) tgz="$FIXTURES/openra.tgz" ;;
rd12) tgz="$FIXTURES/minecraft-1.21.4-rd12-odinlite-in-world.tgz" ;;
bsl) tgz="$FIXTURES/minecraft-1.21.4-fabric-iris-bsl-in-world.tgz" ;;
fcl) tgz="${FCL_FIXTURES:-$FIXTURES}/minecraft-1.21.5-vanilla-fcl-in-world.tgz" ;;
esac
set -- $(wl_args $wl)
local golden="$FIXTURES/$2"
local golden="$FIXTURES/$2"; [ "$wl" = fcl ] && golden="${FCL_FIXTURES:-$FIXTURES}/$2"
if [ ! -f "$tmp/$wl.trace" ]; then
rm -rf "$tmp/x"; mkdir -p "$tmp/x"; tar xzf "$tgz" -C "$tmp/x" || exit 1
mv "$(find "$tmp/x" -name '*.trace' | head -1)" "$tmp/$wl.trace"; rm -rf "$tmp/x"
@@ -146,6 +149,9 @@ run_one() {
[ "$mode" = benchmark ] && passflag="--ez benchmark true --ez benchmark_finish ${FINISH:-false} --es benchmark_result_path $app/output/benchmark.json"
local env; env="$(arm_env "$arm")"; [ -n "$EXTRA_ENV" ] && env="${env:+$env;}$EXTRA_ENV"
if [ "$mode" = benchmark ]; then
# P15 validity rule: frequency/thermal sampler for the whole run (devstate.sh judges it).
adb -s "$SER" push "$(cygpath -w "$SCRIPT_DIR/freq_sampler.sh")" /data/local/tmp/p15-freq_sampler.sh >/dev/null 2>&1
shq "su -c 'chmod 755 /data/local/tmp/p15-freq_sampler.sh; pkill -f p15-freq_sampler.sh; rm -f /data/local/tmp/p15freq.txt; (nohup sh /data/local/tmp/p15-freq_sampler.sh /data/local/tmp/p15freq.txt 2 >/dev/null 2>&1 &)'" >/dev/null 2>&1
shq "su -c 'nohup taskset 01 $SAMPLER $pkg 10 $app/output/benchmark.json /data/local/tmp/p14cpu.csv $TIMEOUT >/dev/null 2>&1 &'" >/dev/null 2>&1
fi
local am_out
@@ -172,6 +178,10 @@ run_one() {
sleep 1
adb -s "$SER" exec-out "su -c 'cat /data/local/tmp/p14cpu.csv'" >"$dir/cpu.csv" 2>/dev/null
shq "su -c 'pkill -f cpusampler'" >/dev/null 2>&1
shq "su -c 'pkill -f p15-freq_sampler.sh'" >/dev/null 2>&1
adb -s "$SER" exec-out "su -c 'cat /data/local/tmp/p15freq.txt'" >"$dir/freq.txt" 2>/dev/null
SER=$SER bash "$SCRIPT_DIR/pin_clocks.sh" show >"$dir/pin.txt" 2>&1
SER=$SER bash "$SCRIPT_DIR/devstate.sh" "$dir" trace >/dev/null 2>&1
fi
shq "am force-stop $pkg" >/dev/null 2>&1
local passed="none"; [ -f "$dir/result.json" ] && passed=$(sed -n 's/.*"passed": *\([a-z]*\).*/\1/p' "$dir/result.json" | head -1)
@@ -179,7 +189,7 @@ run_one() {
echo "tag=$TAG wl=$wl backend=$backend arm=$arm rep=$rep mode=$mode status=$status passed=$passed dur=$(( $(date +%s) - epoch0 )) temp0=$t0 temp1=$t1" >"$dir/meta.txt"
verify_run "$dir" "$arm" "$backend"
local fps="-"; [ -f "$dir/benchmark.json" ] && fps=$(sed -n 's/.*"fps": *\([0-9.]*\).*/\1/p' "$dir/benchmark.json" | head -1)
log "END $TAG $wl/$backend/$arm r$rep $mode status=$status passed=$passed fps=$fps temp=$t1 :: $(tr '\n' ' ' <"$dir/verify.txt")"
log "END $TAG $wl/$backend/$arm r$rep $mode status=$status passed=$passed fps=$fps temp=$t1 :: $(tr '\n' ' ' <"$dir/verify.txt") $(cat "$dir/state.txt" 2>/dev/null)"
}
main() {
+27 -4
View File
@@ -8,9 +8,30 @@
set -u
export MSYS_NO_PATHCONV=1
SER="${SER:-HA27Q3LQ}"
PIN_CPU_KHZ="${PIN_CPU_KHZ:-2035200}"
PIN_LITTLE_KHZ="${PIN_LITTLE_KHZ:-1574400}"
PIN_GPU_HZ="${PIN_GPU_HZ:-903000000}"
# Clock profiles (P15 measurement rules). Compare numbers only within one profile.
# sustained (default): CPU 2035.2 MHz big/prime, 1574.4 little; GPU 680 MHz (soaked)
# gpumax: as sustained but GPU 903 MHz (shows whether a scene is GPU-bound; not sustainable)
# gpulow: as sustained but GPU 422 MHz (amplifies GPU cost)
# cpulow: CPU 1286.4 big/prime, 1017.6 little; GPU 680 MHz (amplifies CPU differences)
# cpuhunt: CPU <=1.5 GHz big/prime, <=1.25 GHz little; GPU 903 MHz - cleanly CPU-bound (CPU-overhead
# hunting: vanilla/rd12 gap, driver-call redundancy, S1-S3). Short cool-started runs.
# gpuhunt: CPU <=2.4 GHz big/prime, <=1.8 GHz little; GPU 578 MHz - cleanly GPU-bound (GPU-side
# hunting: the Espryt BSL gap, pass structure, load/store).
# PIN_* variables still override a profile's values.
PROFILE="${PROFILE:-sustained}"
case "$PROFILE" in
sustained) p_cpu=2035200 p_little=1574400 p_gpu=680000000 ;;
gpumax) p_cpu=2035200 p_little=1574400 p_gpu=903000000 ;;
gpulow) p_cpu=2035200 p_little=1574400 p_gpu=422000000 ;;
cpulow) p_cpu=1286400 p_little=1017600 p_gpu=680000000 ;;
cpuhunt) p_cpu=1500000 p_little=1250000 p_gpu=903000000 ;;
gpuhunt) p_cpu=2400000 p_little=1800000 p_gpu=578000000 ;;
*) echo "pin_clocks.sh: unknown PROFILE $PROFILE" >&2; exit 2 ;;
esac
PIN_CPU_KHZ="${PIN_CPU_KHZ:-$p_cpu}"
PIN_LITTLE_KHZ="${PIN_LITTLE_KHZ:-$p_little}"
PIN_GPU_HZ="${PIN_GPU_HZ:-$p_gpu}"
PROFILE_FILE=/data/local/tmp/p15-bench-profile
KGSL=/sys/class/kgsl/kgsl-3d0/devfreq
SAVED=/data/local/tmp/p14-clocks.saved
@@ -20,10 +41,12 @@ case "${1:-show}" in
pin)
su_sh "test -f $SAVED || { for p in /sys/devices/system/cpu/cpufreq/policy*; do echo \$p \$(cat \$p/scaling_min_freq) \$(cat \$p/scaling_max_freq); done; echo $KGSL \$(cat $KGSL/min_freq) \$(cat $KGSL/max_freq); } > $SAVED"
su_sh "for p in /sys/devices/system/cpu/cpufreq/policy*; do want=$PIN_CPU_KHZ; [ \$p = /sys/devices/system/cpu/cpufreq/policy0 ] && want=$PIN_LITTLE_KHZ; f=0; for a in \$(cat \$p/scaling_available_frequencies); do [ \$a -le \$want ] && f=\$a; done; echo \$f > \$p/scaling_max_freq; echo \$f > \$p/scaling_min_freq; echo \$f > \$p/scaling_max_freq; done; echo $PIN_GPU_HZ > $KGSL/max_freq; echo $PIN_GPU_HZ > $KGSL/min_freq; echo $PIN_GPU_HZ > $KGSL/max_freq"
su_sh "echo profile=$PROFILE cpu=$PIN_CPU_KHZ little=$PIN_LITTLE_KHZ gpu=$PIN_GPU_HZ > $PROFILE_FILE"
"$0" show ;;
restore)
su_sh "rm -f $PROFILE_FILE"
su_sh "test -f $SAVED && while read p mn mx; do if [ \$p = $KGSL ]; then echo \$mx > \$p/max_freq; echo \$mn > \$p/min_freq; else echo \$mx > \$p/scaling_max_freq; echo \$mn > \$p/scaling_min_freq; fi; done < $SAVED; rm -f $SAVED"
"$0" show ;;
show)
su_sh "for p in /sys/devices/system/cpu/cpufreq/policy*; do echo \$p cur=\$(cat \$p/scaling_cur_freq) min=\$(cat \$p/scaling_min_freq) max=\$(cat \$p/scaling_max_freq); done; echo gpu cur=\$(cat $KGSL/cur_freq) min=\$(cat $KGSL/min_freq) max=\$(cat $KGSL/max_freq); dumpsys battery | grep temperature" ;;
su_sh "for p in /sys/devices/system/cpu/cpufreq/policy*; do echo \$p cur=\$(cat \$p/scaling_cur_freq) min=\$(cat \$p/scaling_min_freq) max=\$(cat \$p/scaling_max_freq) avail=\$(cat \$p/scaling_available_frequencies); done; echo gpu cur=\$(cat $KGSL/cur_freq) min=\$(cat $KGSL/min_freq) max=\$(cat $KGSL/max_freq) avail=\$(cat /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies); dumpsys battery | grep temperature" ;;
esac