157 lines
5.3 KiB
Bash
Executable File
157 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Runs the ORLY relay with CPU profiling enabled and opens the resulting
|
|
# pprof profile in a local web UI.
|
|
#
|
|
# Usage:
|
|
# ./profile.sh [duration_seconds]
|
|
#
|
|
# - Builds the relay.
|
|
# - Starts it with ORLY_PPROF=cpu and minimal logging.
|
|
# - Waits for the profile path printed at startup.
|
|
# - Runs for DURATION seconds (default 10), then stops the relay to flush the
|
|
# CPU profile to disk.
|
|
# - Launches `go tool pprof -http=:8000` for convenient browsing.
|
|
#
|
|
# Notes:
|
|
# - The profile file path is detected from the relay's stdout/stderr lines
|
|
# emitted by github.com/pkg/profile, typically like:
|
|
# profile: cpu profiling enabled, path: /tmp/profile123456/cpu.pprof
|
|
# - You can change DURATION by passing a number of seconds as the first arg
|
|
# or by setting DURATION env var.
|
|
|
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
DURATION="${1:-${DURATION:-10}}"
|
|
PPROF_HTTP_PORT="${PPROF_HTTP_PORT:-8000}"
|
|
|
|
# Load generation controls
|
|
LOAD_ENABLED="${LOAD_ENABLED:-1}" # set to 0 to disable load
|
|
# Use the benchmark main package in cmd/benchmark as the load generator
|
|
BENCHMARK_PKG_DIR="$REPO_ROOT/cmd/benchmark"
|
|
BENCHMARK_BIN="${BENCHMARK_BIN:-}" # if empty, we will build to $RUN_DIR/benchmark
|
|
BENCHMARK_EVENTS="${BENCHMARK_EVENTS:-}" # optional override for -events
|
|
BENCHMARK_DURATION="${BENCHMARK_DURATION:-}" # optional override for -duration (e.g. 30s); defaults to DURATION seconds
|
|
|
|
BIN="$REPO_ROOT/next.orly.dev"
|
|
LOG_DIR="${LOG_DIR:-$REPO_ROOT/cmd/benchmark/reports}"
|
|
mkdir -p "$LOG_DIR"
|
|
RUN_TS="$(date +%Y%m%d_%H%M%S)"
|
|
RUN_DIR="$LOG_DIR/profile_run_${RUN_TS}"
|
|
mkdir -p "$RUN_DIR"
|
|
LOG_FILE="$RUN_DIR/relay.log"
|
|
LOAD_LOG_FILE="$RUN_DIR/load.log"
|
|
|
|
echo "[profile.sh] Building relay binary ..."
|
|
go build -o "$BIN" .
|
|
|
|
# Ensure we clean up the child process on exit
|
|
RELAY_PID=""
|
|
LOAD_PID=""
|
|
cleanup() {
|
|
if [[ -n "$LOAD_PID" ]] && kill -0 "$LOAD_PID" 2>/dev/null; then
|
|
echo "[profile.sh] Stopping load generator (pid=$LOAD_PID) ..."
|
|
kill -INT "$LOAD_PID" 2>/dev/null || true
|
|
sleep 0.5
|
|
kill -TERM "$LOAD_PID" 2>/dev/null || true
|
|
fi
|
|
if [[ -n "$RELAY_PID" ]] && kill -0 "$RELAY_PID" 2>/dev/null; then
|
|
echo "[profile.sh] Stopping relay (pid=$RELAY_PID) ..."
|
|
kill -INT "$RELAY_PID" 2>/dev/null || true
|
|
# give it a moment to exit and flush profile
|
|
sleep 1
|
|
kill -TERM "$RELAY_PID" 2>/dev/null || true
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Start the relay with CPU profiling enabled. Capture both stdout and stderr.
|
|
echo "[profile.sh] Starting relay with CPU profiling enabled ..."
|
|
(
|
|
ORLY_LOG_LEVEL=off \
|
|
ORLY_LISTEN="${ORLY_LISTEN:-127.0.0.1}" \
|
|
ORLY_PORT="${ORLY_PORT:-3334}" \
|
|
ORLY_PPROF=cpu \
|
|
"$BIN"
|
|
) >"$LOG_FILE" 2>&1 &
|
|
RELAY_PID=$!
|
|
echo "[profile.sh] Relay started with pid $RELAY_PID; logging to $LOG_FILE"
|
|
|
|
# Wait until the profile path is printed. Timeout after reasonable period.
|
|
PPROF_FILE=""
|
|
START_TIME=$(date +%s)
|
|
TIMEOUT=30
|
|
|
|
echo "[profile.sh] Waiting for profile path to appear in relay output ..."
|
|
while :; do
|
|
if grep -Eo "/tmp/profile[^ ]+/cpu\.pprof" "$LOG_FILE" >/dev/null 2>&1; then
|
|
PPROF_FILE=$(grep -Eo "/tmp/profile[^ ]+/cpu\.pprof" "$LOG_FILE" | tail -n1)
|
|
break
|
|
fi
|
|
NOW=$(date +%s)
|
|
if (( NOW - START_TIME > TIMEOUT )); then
|
|
echo "[profile.sh] ERROR: Timed out waiting for profile path in $LOG_FILE" >&2
|
|
echo "Last 50 log lines:" >&2
|
|
tail -n 50 "$LOG_FILE" >&2
|
|
exit 1
|
|
fi
|
|
sleep 0.3
|
|
done
|
|
|
|
echo "[profile.sh] Detected profile file: $PPROF_FILE"
|
|
|
|
# Optionally start load generator to exercise the relay
|
|
if [[ "$LOAD_ENABLED" == "1" ]]; then
|
|
# Build benchmark binary if not provided
|
|
if [[ -z "$BENCHMARK_BIN" ]]; then
|
|
BENCHMARK_BIN="$RUN_DIR/benchmark"
|
|
echo "[profile.sh] Building benchmark load generator ($BENCHMARK_PKG_DIR) ..."
|
|
go build -o "$BENCHMARK_BIN" "$BENCHMARK_PKG_DIR"
|
|
fi
|
|
BENCH_DB_DIR="$RUN_DIR/benchdb"
|
|
mkdir -p "$BENCH_DB_DIR"
|
|
DURATION_ARG="${BENCHMARK_DURATION:-${DURATION}s}"
|
|
EXTRA_EVENTS=""
|
|
if [[ -n "$BENCHMARK_EVENTS" ]]; then
|
|
EXTRA_EVENTS="-events=$BENCHMARK_EVENTS"
|
|
fi
|
|
echo "[profile.sh] Starting benchmark load generator for duration $DURATION_ARG ..."
|
|
RELAY_URL="ws://${ORLY_LISTEN:-127.0.0.1}:${ORLY_PORT:-3334}"
|
|
echo "[profile.sh] Using relay URL: $RELAY_URL"
|
|
(
|
|
"$BENCHMARK_BIN" -relay-url="$RELAY_URL" -net-workers="${NET_WORKERS:-2}" -net-rate="${NET_RATE:-20}" -duration="$DURATION_ARG" $EXTRA_EVENTS \
|
|
>"$LOAD_LOG_FILE" 2>&1 &
|
|
)
|
|
LOAD_PID=$!
|
|
echo "[profile.sh] Load generator started (pid=$LOAD_PID); logging to $LOAD_LOG_FILE"
|
|
else
|
|
echo "[profile.sh] LOAD_ENABLED=0; not starting load generator."
|
|
fi
|
|
|
|
echo "[profile.sh] Letting the relay run for ${DURATION}s to collect CPU samples ..."
|
|
sleep "$DURATION"
|
|
|
|
# Stop the relay to flush the CPU profile
|
|
cleanup
|
|
# Disable trap so we don't double-kill
|
|
trap - EXIT
|
|
|
|
# Wait briefly to ensure the profile file is finalized
|
|
for i in {1..20}; do
|
|
if [[ -s "$PPROF_FILE" ]]; then
|
|
break
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
|
|
if [[ ! -s "$PPROF_FILE" ]]; then
|
|
echo "[profile.sh] WARNING: Profile file exists but is empty or missing: $PPROF_FILE" >&2
|
|
fi
|
|
|
|
# Launch pprof HTTP UI
|
|
echo "[profile.sh] Launching pprof web UI (http://localhost:${PPROF_HTTP_PORT}) ..."
|
|
exec go tool pprof -http=":${PPROF_HTTP_PORT}" "$BIN" "$PPROF_FILE"
|