profile.sh raw
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 # Runs the ORLY relay with CPU profiling enabled and opens the resulting
5 # pprof profile in a local web UI.
6 #
7 # Usage:
8 # ./profile.sh [duration_seconds]
9 #
10 # - Builds the relay.
11 # - Starts it with ORLY_PPROF=cpu and minimal logging.
12 # - Waits for the profile path printed at startup.
13 # - Runs for DURATION seconds (default 10), then stops the relay to flush the
14 # CPU profile to disk.
15 # - Launches `go tool pprof -http=:8000` for convenient browsing.
16 #
17 # Notes:
18 # - The profile file path is detected from the relay's stdout/stderr lines
19 # emitted by github.com/pkg/profile, typically like:
20 # profile: cpu profiling enabled, path: /tmp/profile123456/cpu.pprof
21 # - You can change DURATION by passing a number of seconds as the first arg
22 # or by setting DURATION env var.
23
24 SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
25 REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)"
26 cd "$REPO_ROOT"
27
28 DURATION="${1:-${DURATION:-10}}"
29 PPROF_HTTP_PORT="${PPROF_HTTP_PORT:-8000}"
30
31 # Load generation controls
32 LOAD_ENABLED="${LOAD_ENABLED:-1}" # set to 0 to disable load
33 # Use the benchmark main package in cmd/benchmark as the load generator
34 BENCHMARK_PKG_DIR="$REPO_ROOT/cmd/benchmark"
35 BENCHMARK_BIN="${BENCHMARK_BIN:-}" # if empty, we will build to $RUN_DIR/benchmark
36 BENCHMARK_EVENTS="${BENCHMARK_EVENTS:-}" # optional override for -events
37 BENCHMARK_DURATION="${BENCHMARK_DURATION:-}" # optional override for -duration (e.g. 30s); defaults to DURATION seconds
38
39 BIN="$REPO_ROOT/next.orly.dev"
40 LOG_DIR="${LOG_DIR:-$REPO_ROOT/cmd/benchmark/reports}"
41 mkdir -p "$LOG_DIR"
42 RUN_TS="$(date +%Y%m%d_%H%M%S)"
43 RUN_DIR="$LOG_DIR/profile_run_${RUN_TS}"
44 mkdir -p "$RUN_DIR"
45 LOG_FILE="$RUN_DIR/relay.log"
46 LOAD_LOG_FILE="$RUN_DIR/load.log"
47
48 echo "[profile.sh] Building relay binary (pure Go + purego)..."
49 CGO_ENABLED=0 go build -o "$BIN" .
50
51 # Copy libsecp256k1.so next to binary if available (runtime optional)
52 if [[ -f "pkg/crypto/p8k/libsecp256k1.so" ]]; then
53 cp pkg/crypto/p8k/libsecp256k1.so "$(dirname "$BIN")/"
54 fi
55
56 # Ensure we clean up the child process on exit
57 RELAY_PID=""
58 LOAD_PID=""
59 cleanup() {
60 if [[ -n "$LOAD_PID" ]] && kill -0 "$LOAD_PID" 2>/dev/null; then
61 echo "[profile.sh] Stopping load generator (pid=$LOAD_PID) ..."
62 kill -INT "$LOAD_PID" 2>/dev/null || true
63 sleep 0.5
64 kill -TERM "$LOAD_PID" 2>/dev/null || true
65 fi
66 if [[ -n "$RELAY_PID" ]] && kill -0 "$RELAY_PID" 2>/dev/null; then
67 echo "[profile.sh] Stopping relay (pid=$RELAY_PID) ..."
68 kill -INT "$RELAY_PID" 2>/dev/null || true
69 # give it a moment to exit and flush profile
70 sleep 1
71 kill -TERM "$RELAY_PID" 2>/dev/null || true
72 fi
73 }
74 trap cleanup EXIT
75
76 # Start the relay with CPU profiling enabled. Capture both stdout and stderr.
77 echo "[profile.sh] Starting relay with CPU profiling enabled ..."
78 (
79 ORLY_LOG_LEVEL=off \
80 ORLY_LISTEN="${ORLY_LISTEN:-127.0.0.1}" \
81 ORLY_PORT="${ORLY_PORT:-3334}" \
82 ORLY_PPROF=cpu \
83 "$BIN"
84 ) >"$LOG_FILE" 2>&1 &
85 RELAY_PID=$!
86 echo "[profile.sh] Relay started with pid $RELAY_PID; logging to $LOG_FILE"
87
88 # Wait until the profile path is printed. Timeout after reasonable period.
89 PPROF_FILE=""
90 START_TIME=$(date +%s)
91 TIMEOUT=30
92
93 echo "[profile.sh] Waiting for profile path to appear in relay output ..."
94 while :; do
95 if grep -Eo "/tmp/profile[^ ]+/cpu\.pprof" "$LOG_FILE" >/dev/null 2>&1; then
96 PPROF_FILE=$(grep -Eo "/tmp/profile[^ ]+/cpu\.pprof" "$LOG_FILE" | tail -n1)
97 break
98 fi
99 NOW=$(date +%s)
100 if (( NOW - START_TIME > TIMEOUT )); then
101 echo "[profile.sh] ERROR: Timed out waiting for profile path in $LOG_FILE" >&2
102 echo "Last 50 log lines:" >&2
103 tail -n 50 "$LOG_FILE" >&2
104 exit 1
105 fi
106 sleep 0.3
107 done
108
109 echo "[profile.sh] Detected profile file: $PPROF_FILE"
110
111 # Optionally start load generator to exercise the relay
112 if [[ "$LOAD_ENABLED" == "1" ]]; then
113 # Build benchmark binary if not provided
114 if [[ -z "$BENCHMARK_BIN" ]]; then
115 BENCHMARK_BIN="$RUN_DIR/benchmark"
116 echo "[profile.sh] Building benchmark load generator (pure Go + purego) ($BENCHMARK_PKG_DIR) ..."
117 cd "$BENCHMARK_PKG_DIR"
118 CGO_ENABLED=0 go build -o "$BENCHMARK_BIN" .
119 # Copy libsecp256k1.so if available (runtime optional)
120 if [[ -f "$REPO_ROOT/pkg/crypto/p8k/libsecp256k1.so" ]]; then
121 cp "$REPO_ROOT/pkg/crypto/p8k/libsecp256k1.so" "$RUN_DIR/"
122 fi
123 cd "$REPO_ROOT"
124 fi
125 BENCH_DB_DIR="$RUN_DIR/benchdb"
126 mkdir -p "$BENCH_DB_DIR"
127 DURATION_ARG="${BENCHMARK_DURATION:-${DURATION}s}"
128 EXTRA_EVENTS=""
129 if [[ -n "$BENCHMARK_EVENTS" ]]; then
130 EXTRA_EVENTS="-events=$BENCHMARK_EVENTS"
131 fi
132 echo "[profile.sh] Starting benchmark load generator for duration $DURATION_ARG ..."
133 RELAY_URL="ws://${ORLY_LISTEN:-127.0.0.1}:${ORLY_PORT:-3334}"
134 echo "[profile.sh] Using relay URL: $RELAY_URL"
135 (
136 "$BENCHMARK_BIN" -relay-url="$RELAY_URL" -net-workers="${NET_WORKERS:-2}" -net-rate="${NET_RATE:-20}" -duration="$DURATION_ARG" $EXTRA_EVENTS \
137 >"$LOAD_LOG_FILE" 2>&1 &
138 )
139 LOAD_PID=$!
140 echo "[profile.sh] Load generator started (pid=$LOAD_PID); logging to $LOAD_LOG_FILE"
141 else
142 echo "[profile.sh] LOAD_ENABLED=0; not starting load generator."
143 fi
144
145 echo "[profile.sh] Letting the relay run for ${DURATION}s to collect CPU samples ..."
146 sleep "$DURATION"
147
148 # Stop the relay to flush the CPU profile
149 cleanup
150 # Disable trap so we don't double-kill
151 trap - EXIT
152
153 # Wait briefly to ensure the profile file is finalized
154 for i in {1..20}; do
155 if [[ -s "$PPROF_FILE" ]]; then
156 break
157 fi
158 sleep 0.2
159 done
160
161 if [[ ! -s "$PPROF_FILE" ]]; then
162 echo "[profile.sh] WARNING: Profile file exists but is empty or missing: $PPROF_FILE" >&2
163 fi
164
165 # Launch pprof HTTP UI
166 echo "[profile.sh] Launching pprof web UI (http://localhost:${PPROF_HTTP_PORT}) ..."
167 exec go tool pprof -http=":${PPROF_HTTP_PORT}" "$BIN" "$PPROF_FILE"
168