#!/bin/bash
# bench-gc-http.sh — validate the asm naive GC under sustained HTTP
# load. Starts each server, hits it with N requests, samples peak
# RSS. Expected: server with GC holds memory flat under the
# no-arena workload; server without GC leaks monotonically.
#
# Four configs compared:
#   1. asm no-GC + http-server.lsp          (bounded via heap-snapshot)
#   2. asm GC   + http-server.lsp          (bounded via snapshot + GC backstop)
#   3. asm no-GC + http-server-noarena.lsp (leaks — baseline failure)
#   4. asm GC   + http-server-noarena.lsp  (bounded via GC alone)

set -u
cd "$(dirname "$0")/.."

REQUESTS=${REQUESTS:-5000}
CONCURRENCY=${CONCURRENCY:-8}

# Kill stragglers on exit. The noarena + no-GC case can balloon
# to gigabytes if the test somehow overshoots REQUESTS; ulimit -v
# on each spawn caps blast radius.
declare -a SPAWNED=()
cleanup() {
    local pid
    for pid in "${SPAWNED[@]}"; do kill -9 "$pid" 2>/dev/null; done
    sleep 0.2
    pkill -9 -u "$USER" -f 'examples/http-server' 2>/dev/null || true
}
trap cleanup EXIT INT TERM

make -s -C asm all

printf "\n═══════════════════════════════════════════════════════\n"
printf "asm GC vs no-GC under sustained HTTP load\n"
printf "  %s requests, concurrency %s, 1 KB body per request\n" "$REQUESTS" "$CONCURRENCY"
printf "═══════════════════════════════════════════════════════\n"

run_case() {
    local label="$1" bin="$2" srv="$3" vcap="$4"
    # Fresh port per case so we don't collide with a lingering socket.
    local pid peak=0 rss time_s=0 t0 t1
    printf "\n── %s ──\n" "$label"
    ( ulimit -v "$vcap" && exec "$bin" < "$srv" >/dev/null 2>&1 ) &
    pid=$!
    SPAWNED+=("$pid")
    # Wait for port.
    for _ in $(seq 1 30); do
        if curl -s -o /dev/null -w "%{http_code}" "http://localhost:8080/" 2>/dev/null | grep -q 200; then
            break
        fi
        sleep 0.1
    done
    if ! kill -0 "$pid" 2>/dev/null; then
        echo "  SERVER FAILED TO START (ulimit -v $vcap may be too tight)"
        return 1
    fi
    # Baseline RSS
    local rss0
    rss0=$(awk '/^VmRSS:/ {print $2}' /proc/$pid/status 2>/dev/null || echo 0)
    printf "  baseline_rss_kb=%s\n" "$rss0"
    # Drive traffic; sample RSS every 200ms.
    t0=$(date +%s.%N)
    ( seq 1 "$REQUESTS" | xargs -P "$CONCURRENCY" -I_ \
        curl -s -o /dev/null "http://localhost:8080/bench" ) &
    local curl_pid=$!
    while kill -0 "$curl_pid" 2>/dev/null; do
        if kill -0 "$pid" 2>/dev/null; then
            rss=$(awk '/^VmRSS:/ {print $2}' /proc/$pid/status 2>/dev/null || echo 0)
            [ "$rss" -gt "$peak" ] && peak=$rss
        else
            echo "  SERVER DIED MID-RUN (likely OOM at ulimit -v $vcap)"
            kill "$curl_pid" 2>/dev/null
            break
        fi
        sleep 0.2
    done
    wait "$curl_pid" 2>/dev/null || true
    t1=$(date +%s.%N)
    # Final RSS right before we kill the server.
    local final_rss=0
    if kill -0 "$pid" 2>/dev/null; then
        final_rss=$(awk '/^VmRSS:/ {print $2}' /proc/$pid/status 2>/dev/null || echo 0)
    fi
    kill -9 "$pid" 2>/dev/null
    wait "$pid" 2>/dev/null || true
    time_s=$(python3 -c "print(f'{float(\"$t1\")-float(\"$t0\"):.2f}')")
    local rps
    rps=$(python3 -c "print(f'{$REQUESTS / ($time_s if $time_s>0 else 1):.0f}')")
    printf "  time_s=%s  req/s=%s  peak_rss_kb=%s  final_rss_kb=%s  growth_kb=%s\n" \
        "$time_s" "$rps" "$peak" "$final_rss" "$((final_rss - rss0))"
}

# vcap = ulimit -v in KB. 131072 = 128 MB — enough for one GC chunk
# and the process; tight enough that a leaker will OOM before the
# kernel eats all RAM. Widen if the no-GC noarena case needs to run
# to completion rather than OOM'd.
VCAP=${VCAP:-524288}

run_case "asm no-GC + snapshot loop"     "./asm/lumbda"    "examples/http-server.lsp"         "$VCAP" || true
run_case "asm GC   + snapshot loop"      "./asm/lumbda-gc" "examples/http-server.lsp"         "$VCAP" || true
run_case "asm no-GC + no snapshot"       "./asm/lumbda"    "examples/http-server-noarena.lsp" "$VCAP" || true
run_case "asm GC   + no snapshot"        "./asm/lumbda-gc" "examples/http-server-noarena.lsp" "$VCAP" || true

printf "\n"
