#!/usr/bin/env bash
#
# Runs ./build/nufi_poisson under `perf record`, then converts the
# result into a flamegraph. Run this from the project root (".").
#
# All profiling artifacts are written to ./results/perf/

set -euo pipefail

# ---- config -----------------------------------------------------------
BINARY="./build/nufi_poisson"
FLAMEGRAPH_DIR="${HOME}/.local/bin/FlameGraph"
OUT_DIR="./results/perf"
PERF_FREQ=99
TITLE="${FLAMEGRAPH_TITLE:-NuFI-deal.II FlameGraph}"

# ---- checks -------------------------------------------------------------
if [[ ! -x "${BINARY}" ]]; then
  echo "error: ${BINARY} not found or not executable. Build the project first." >&2
  exit 1
fi

if [[ ! -x "${FLAMEGRAPH_DIR}/stackcollapse-perf.pl" ]] || [[ ! -x "${FLAMEGRAPH_DIR}/flamegraph.pl" ]]; then
  echo "error: FlameGraph scripts not found under ${FLAMEGRAPH_DIR}" >&2
  exit 1
fi

if ! command -v perf >/dev/null 2>&1; then
  echo "error: 'perf' not found on PATH." >&2
  exit 1
fi

PARANOID_PATH="/proc/sys/kernel/perf_event_paranoid"
DESIRED_PARANOID="${PERF_EVENT_PARANOID:--1}"

if [[ -r "${PARANOID_PATH}" ]]; then
  CURRENT_PARANOID="$(cat "${PARANOID_PATH}")"
  if [[ "${CURRENT_PARANOID}" -gt "${DESIRED_PARANOID}" ]]; then
    echo "==> kernel.perf_event_paranoid is ${CURRENT_PARANOID}, lowering to ${DESIRED_PARANOID} (needs sudo) ..."
    if ! sudo sysctl -w "kernel.perf_event_paranoid=${DESIRED_PARANOID}" >/dev/null; then
      echo "warning: could not lower kernel.perf_event_paranoid -- kernel symbols/call graphs may be incomplete." >&2
      echo "         you can set it manually with: sudo sysctl -w kernel.perf_event_paranoid=${DESIRED_PARANOID}" >&2
    fi
  fi
fi

mkdir -p "${OUT_DIR}"

PERF_DATA="${OUT_DIR}/perf.data"
PERF_SCRIPT_OUT="${OUT_DIR}/out.perf"
FOLDED_OUT="${OUT_DIR}/out.folded"
FLAMEGRAPH_OUT="${OUT_DIR}/flamegraph.svg"

# ---- run ----------------------------------------------------------------
echo "==> Recording with perf (-F ${PERF_FREQ}) ..."
echo "    (Ctrl+C stops the binary early and still runs the rest of the pipeline)"

set +e
trap '' SIGINT
perf record -F "${PERF_FREQ}" -g -o "${PERF_DATA}" -- "${BINARY}"
PERF_EXIT=$?
trap - SIGINT
set -e

if [[ ${PERF_EXIT} -ne 0 ]]; then
  echo "    (perf record exited with code ${PERF_EXIT}, likely from an interrupt -- continuing anyway)"
fi

if [[ ! -s "${PERF_DATA}" ]]; then
  echo "error: ${PERF_DATA} is missing or empty, nothing to process." >&2
  exit 1
fi

echo "==> Generating perf script output ..."
perf script -i "${PERF_DATA}" > "${PERF_SCRIPT_OUT}"

echo "==> Collapsing stacks ..."
"${FLAMEGRAPH_DIR}/stackcollapse-perf.pl" "${PERF_SCRIPT_OUT}" > "${FOLDED_OUT}"

echo "==> Building flamegraph ..."
"${FLAMEGRAPH_DIR}/flamegraph.pl" --title="${TITLE}" "${FOLDED_OUT}" > "${FLAMEGRAPH_OUT}"

echo "==> Done."
echo "    perf.data   : ${PERF_DATA}"
echo "    out.perf    : ${PERF_SCRIPT_OUT}"
echo "    out.folded  : ${FOLDED_OUT}"
echo "    flamegraph  : ${FLAMEGRAPH_OUT}"

read -r -p "Open flamegraph in Firefox? [y/N] " OPEN_CHOICE
case "${OPEN_CHOICE}" in
  [yY]|[yY][eE][sS])
    if command -v firefox >/dev/null 2>&1; then
      firefox "${FLAMEGRAPH_OUT}" >/dev/null 2>&1 &
      disown
    else
      echo "warning: 'firefox' not found on PATH; open it manually: ${FLAMEGRAPH_OUT}" >&2
    fi
    ;;
  *)
    ;;
esac
