#!/usr/bin/env bash
# gputop - top-like live view of the Apple M1 (Asahi) GPU. Fully passive:
# it never dispatches GPU work. Utilization is the duty-cycle of the GPU
# power rail (the GPU gates off when idle); clock is read from the active
# pstate; and it lists the processes currently holding GPU contexts.
#
#   sudo ./gputop.sh           # ~1s refresh, Ctrl-C to quit
#   N=60 SLEEP=0.015 sudo ./gputop.sh
set -u
RAIL=/sys/kernel/debug/gpu_rail_mv
GPUMON=/sys/kernel/debug/gpumon
CLIENTS=/sys/kernel/debug/dri/206400000.gpu/clients
N=${N:-40}                      # rail samples per refresh
SLEEP=${SLEEP:-0.02}            # gap between samples (spreads them over the interval)
ONCE=${ONCE:-0}

sudo test -r "$RAIL" 2>/dev/null || { echo "error: $RAIL unreadable — need root + gpumon.ko loaded" >&2; exit 1; }

mv2mhz() { awk -v v="$1" 'BEGIN{
  n=split("400 0 596 396 631 528 681 720 771 924 862 1128 934 1278",a," ");
  if(v<450){print 0;exit} b=1e9;
  for(i=1;i<=n;i+=2){d=a[i]-v;if(d<0)d=-d;if(d<b){b=d;f=a[i+1]}} print f}'; }

bar() { local v=$1 mx=$2 w=$3 n i s=""; n=$(( v*w/mx )); [ $n -gt $w ] && n=$w
        for ((i=0;i<w;i++)); do [ $i -lt $n ] && s+="#" || s+="-"; done; printf "[%s]" "$s"; }

frame() {
  local busy=0 sum=0 mx=0 i mv util amv mhz procs nctx
  for ((i=0;i<N;i++)); do
    mv=$(cat "$RAIL" 2>/dev/null); mv=${mv:-0}
    if [ "$mv" -gt 450 ]; then busy=$((busy+1)); sum=$((sum+mv)); [ "$mv" -gt "$mx" ] && mx=$mv; fi
    sleep "$SLEEP"
  done
  util=$(( busy*100/N ))
  if [ "$busy" -gt 0 ]; then amv=$(( sum/busy )); mhz=$(mv2mhz "$mx"); else amv=0; mhz=0; fi
  procs=$(cat "$CLIENTS" 2>/dev/null | awk 'NR>1 && $1!="" {c[$1]++} END{for(k in c) printf "%s(%d ctx) ",k,c[k]}')
  nctx=$(cat "$CLIENTS" 2>/dev/null | awk 'NR>1 && $1!=""' | wc -l)
  # passive die-temp proxy (GPU-region SMC sensor Ts1z); tGMD is dead on Asahi
  temp=$(cat "$GPUMON" 2>/dev/null | awk '/TEMP Ts1z/{printf "%.0f",$3}')
  [ -t 1 ] && printf "\033[H\033[2J"
  printf "Apple M1 GPU (G13G B1, 7-core) — Asahi  ·  passive gputop  ·  %s\n\n" "$(date +%H:%M:%S)"
  printf "  util   %3d%%  %s\n" "$util" "$(bar "$util" 100 44)"
  printf "  clock  %4d MHz / 1278    rail %4d mV    state: %s\n" "$mhz" "$amv" \
         "$([ $util -gt 0 ] && echo ACTIVE || echo idle/gated)"
  printf "  temp   die~%s°C (SMC Ts1, GPU-region proxy)\n\n" "${temp:-?}"
  printf "  GPU contexts (%s): %s\n" "$nctx" "${procs:-none}"
  printf "\n  util = rail duty-cycle over ~%ss · 100%% passive: reads SMC + debugfs, no GPU work · Ctrl-C to quit\n" \
         "$(awk -v n=$N -v s=$SLEEP 'BEGIN{printf "%.1f", n*s}')"
}

if [ "$ONCE" = 1 ]; then frame; else while true; do frame; done; fi
