#!/usr/bin/env bash set -euo pipefail # M3: Generate flamegraphs + top hotspots for Manifest decode+profile (Ours vs Routinator). # # Outputs under: # specs/develop/20260224/flamegraph/ # specs/develop/20260224/hotspots/ # specs/develop/20260224/perf/ # # Notes: # - On WSL2, /usr/bin/perf is often a wrapper that fails. This script uses a real perf binary # from /usr/lib/linux-tools/*/perf (if present). # - Ours profiling uses perf + flamegraph --perfdata to avoid rebuilding the whole crate graph # with RocksDB. ROOT_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" RPKI_DIR="$ROOT_REPO/rpki" DATE_TAG="${DATE_TAG:-20260224}" OUT_BASE="$ROOT_REPO/specs/develop/${DATE_TAG}" OUT_FLAME="$OUT_BASE/flamegraph" OUT_HOT="$OUT_BASE/hotspots" OUT_PERF="$OUT_BASE/perf" RUN_TAG="${RUN_TAG:-p2}" OURS_BENCH_DIR="$RPKI_DIR/benchmark/ours_manifest_bench" OURS_BIN="$OURS_BENCH_DIR/target/release/ours-manifest-bench" ROUT_BENCH_DIR="${ROUT_BENCH_DIR:-/home/yuyr/dev/rust_playground/routinator/benchmark}" ROUT_BIN="$ROUT_BENCH_DIR/target/release/routinator-manifest-benchmark" ROUT_ISSUER="$ROUT_BENCH_DIR/fixtures/ta.cer" PROFILE_HZ="${PROFILE_HZ:-99}" mkdir -p "$OUT_FLAME" "$OUT_HOT" "$OUT_PERF" PERF_WRAPPER_OUT="$(perf --version 2>&1 || true)" PERF_REAL="" if echo "${PERF_WRAPPER_OUT}" | grep -q "WARNING: perf not found for kernel"; then PERF_REAL="$(ls -1 /usr/lib/linux-tools/*/perf 2>/dev/null | head -n 1 || true)" else PERF_REAL="$(command -v perf || true)" fi if [[ -z "${PERF_REAL}" ]]; then echo "ERROR: usable perf binary not found (wrapper detected and no /usr/lib/linux-tools/*/perf)." >&2 exit 2 fi SHIM_DIR="$RPKI_DIR/target/bench/tools" mkdir -p "$SHIM_DIR" cat > "$SHIM_DIR/perf" </dev/null 2>&1; then taskset_prefix="taskset -c 0" fi profile_ours() { local sample="$1" local iters="$2" local warm="$3" local fixture="$RPKI_DIR/tests/benchmark/selected_der/${sample}.mft" if [[ ! -f "$fixture" ]]; then echo "ERROR: ours fixture not found: $fixture" >&2 exit 1 fi local perfdata="$OUT_PERF/ours_${sample}_${RUN_TAG}.perf.data" local svg="$OUT_FLAME/ours_${sample}_${RUN_TAG}.svg" local tsv="$OUT_HOT/ours_${sample}_${RUN_TAG}.tsv" echo "== ours $sample (iters=$iters warmup=$warm hz=$PROFILE_HZ)" $taskset_prefix perf record -o "$perfdata" -F "$PROFILE_HZ" -g -- \ "$OURS_BIN" --manifest "$fixture" --iterations "$iters" --warmup-iterations "$warm" --repeats 1 >/dev/null flamegraph --perfdata "$perfdata" --output "$svg" --title "ours ${sample} ManifestObject::decode_der" --deterministic >/dev/null perf report -i "$perfdata" --stdio --no-children --sort symbol --percent-limit 0.5 \ | awk '/^[[:space:]]*[0-9.]+%/ {pct=$1; sub(/%/,"",pct); $1=""; sub(/^[[:space:]]+/,""); print pct "\t" $0}' \ > "$tsv" } profile_routinator() { local sample="$1" local iters="$2" local warm="$3" local fixture="$ROUT_BENCH_DIR/fixtures/selected_der/${sample}.mft" if [[ ! -f "$fixture" ]]; then echo "ERROR: routinator fixture not found: $fixture" >&2 exit 1 fi local svg="$OUT_FLAME/routinator_${sample}_${RUN_TAG}.svg" local tsv="$OUT_HOT/routinator_${sample}_${RUN_TAG}.tsv" echo "== routinator $sample (iters=$iters warmup=$warm hz=$PROFILE_HZ)" $taskset_prefix "$ROUT_BIN" \ --target decode_only \ --manifest "$fixture" \ --issuer "$ROUT_ISSUER" \ --iterations "$iters" \ --repeats 1 \ --warmup-iterations "$warm" \ --strict false \ --profile-hz "$PROFILE_HZ" \ --flamegraph "$svg" \ --hotspots "$tsv" \ >/dev/null } echo "[3/3] Profile samples..." # Choose iterations so each capture runs ~10-20s serially. profile_ours small-01 200000 0 profile_routinator small-01 200000 0 profile_ours large-02 50000 0 profile_routinator large-02 50000 0 profile_ours xlarge-02 5000 0 profile_routinator xlarge-02 5000 0 echo "Done." echo "- Flamegraphs: $OUT_FLAME/" echo "- Hotspots: $OUT_HOT/" echo "- Perf data: $OUT_PERF/"