@@ -33,14 +33,33 @@ sudo sysctl -w kernel.perf_event_paranoid=-1 kernel.kptr_restrict=0 >/dev/null 2
3333PIDS=$(sudo docker top walshadow -eo pid --no-headers 2>/dev/null | awk '{print $1}' | paste -sd,)
3434[ -n "$PIDS" ] || PIDS=$(sudo pgrep -f 'walshadow-stream|postgres' 2>/dev/null | paste -sd,)
3535[ -n "$PIDS" ] || { echo "no walshadow processes found — is the daemon up?" >&2; exit 1; }
36- # The walshadow-stream daemon (decode/insert engine) for the single-PID eBPF profiler.
36+ # The walshadow-stream daemon (decode/insert engine) for the single-PID eBPF
37+ # profiler. Required and must be live — profiling with no walshadow-stream
38+ # process is meaningless, so fail loudly here rather than producing empty output.
3739WS=$(sudo pgrep -f 'walshadow-stream' 2>/dev/null | head -1 || true)
3840[ -n "$WS" ] || WS=$(sudo docker inspect -f '{{.State.Pid}}' walshadow 2>/dev/null || true)
39- echo "walshadow pids (perf): $PIDS walshadow-stream pid (eBPF): ${WS:-none}"
41+ { [ -n "$WS" ] && sudo test -d "/proc/$WS"; } \
42+ || { echo "walshadow-stream daemon not running — nothing to profile (is the walshadow container up?)" >&2; exit 1; }
43+ echo "walshadow pids (perf): $PIDS walshadow-stream pid (eBPF): $WS"
4044
4145sudo nohup bash -c "
42- { [ -n \"$WS\" ] && profile-bpfcc -F 99 -f -p $WS $DUR > $OUT/oncpu-walshadow-$TS.folded 2>$OUT/oncpu-$TS.log ; } &
43- perf record -F 99 -g -p $PIDS -o $OUT/perf-$TS.data -- sleep $DUR 2>>$OUT/perf-$TS.log \
46+ profile-bpfcc -F 99 -f -p $WS $DUR > $OUT/oncpu-walshadow-$TS.folded 2>$OUT/oncpu-$TS.log &
47+ # Re-filter the PID list to processes still alive RIGHT NOW: perf -p aborts the
48+ # whole record if any one PID has exited (transient PG backends churn between
49+ # the snapshot above and this point) → 'Couldn't create thread/CPU maps'. The
50+ # walshadow-stream PID is asserted live above, so the list is never empty.
51+ LIVE=\"\"; for x in \$(echo '$PIDS' | tr ',' ' '); do [ -d /proc/\$x ] && LIVE=\"\$LIVE,\$x\"; done; LIVE=\${LIVE#,}
52+ # DWARF call-graph: walshadow links musl and has no frame pointers, so -g/fp
53+ # unwinding dead-ends at the leaf (memcpy/[unknown]). DWARF unwinds via the
54+ # binary's .eh_frame (present — default panic=unwind). Tuning that matters:
55+ # * dwarf,65528 — large per-sample stack dump. Rust async frames are big;
56+ # a small dump (8192) truncates the unwind so callers above memcpy are lost.
57+ # * -m 64M — ring buffer big enough for those dumps (avoids a wakeup storm).
58+ # * --no-buildid* — skip perf's end-of-record build-id/addr2line finalization,
59+ # which fails on this host and left the file unfinalized ('data size 0').
60+ # Safe: we run `perf script` on this same host, resolving symbols live.
61+ perf record -F 99 --call-graph dwarf,65528 -m 64M --no-buildid --no-buildid-cache \
62+ -p \"\$LIVE\" -o $OUT/perf-$TS.data -- sleep $DUR 2>>$OUT/perf-$TS.log \
4463 || echo 'perf record failed (see log)' >>$OUT/perf-$TS.log
4564 wait
4665 chown -R ubuntu $OUT
0 commit comments