#!/usr/bin/env bash
# gpumon.sh - passive M1 GPU frequency-lock watchdog (no GPU workload).
# Reads the GPU rail voltage (SMC key VP1b) via gpumon.ko and maps it to the
# G13G pstate table to report the live GPU clock. Run it alongside a benchmark
# to confirm the GPU stays pegged at 1278 MHz and to catch any downclock.
#   sudo ./gpumon.sh            # continuous watch (default 0.5s)
#   sudo ./gpumon.sh -n         # single sample
#   INTERVAL=0.25 sudo ./gpumon.sh
set -u
SRC=/sys/kernel/debug/gpumon
RAIL=${GPU_RAIL:-VP1b}
INTERVAL=${INTERVAL:-0.5}
MAXF=1278
[ -r "$SRC" ] || { echo "error: $SRC unreadable (need root + gpumon.ko loaded)" >&2; exit 1; }
# G13G OPP table: "mV MHz" pairs, ascending
PSTATES="400 0 596 396 631 528 681 720 771 924 862 1128 934 1278"

read_mhz() {            # echoes: "<mV> <MHz>"
  local d vmv f
  d=$(cat "$SRC" 2>/dev/null) || return 1
  vmv=$(awk -v k="$RAIL" '$1=="VOLT"&&$2==k{printf "%d",$3*1000}' <<<"$d")
  f=$(awk -v v="$vmv" -v ps="$PSTATES" 'BEGIN{
    if(v<450){print 0;exit} n=split(ps,a," "); b=1e9;
    for(i=1;i<=n;i+=2){dv=a[i]-v;if(dv<0)dv=-dv;if(dv<b){b=dv;f=a[i+1]}} print f}')
  echo "${vmv:-0} ${f:-0}"
}

if [ "${1:-}" = "-n" ]; then
  set -- $(read_mhz)
  if [ "$2" -ge "$MAXF" ]; then s="LOCKED @ ${MAXF} MHz"
  elif [ "$2" -eq 0 ];     then s="idle (gated)"
  else s="DROP -> $2 MHz"; fi
  printf "freq %5s MHz   rail=%4dmV   %s\n" "$2" "$1" "$s"; exit
fi

minf=99999; drops=0; n=0
echo "watching GPU clock (rail $RAIL, ${INTERVAL}s) - Ctrl-C to stop"
while r=$(read_mhz); do
  set -- $r; vmv=$1; f=$2; n=$((n+1))
  [ "$f" -lt "$minf" ] && minf=$f
  [ "$f" -ne 0 ] && [ "$f" -lt "$MAXF" ] && drops=$((drops+1))
  if   [ "$f" -ge "$MAXF" ]; then tag="locked";  elif [ "$f" -eq 0 ]; then tag="idle"; else tag="DROP!"; fi
  printf "%s  %5s MHz  %4dmV  %-6s [min=%s MHz, drops=%d/%d]\n" \
         "$(date +%H:%M:%S)" "$f" "$vmv" "$tag" "$minf" "$drops" "$n"
  sleep "$INTERVAL"
done
