name: Frozen TENT CPU comparison

on:
  workflow_call:
    inputs:
      release_gate:
        type: boolean
        default: false
  workflow_dispatch:
    inputs:
      profile_only:
        description: Collect slow-path strace and CPU stack profiles without timing sweeps
        type: boolean
        default: false
      tail_profile_only:
        description: Collect per-sample io_uring and scheduler timelines without timing sweeps
        type: boolean
        default: false
      baseline_ref:
        description: Optional base commit for a full before/after sweep on this same runner
        type: string
        default: ''

permissions:
  contents: read

jobs:
  cpu-oracle:
    # Release PRs run this same job through release.yml, once per revision.
    if: ${{ !startsWith(github.head_ref, 'release/') || inputs.release_gate }}
    runs-on: ubuntu-24.04
    timeout-minutes: ${{ inputs.baseline_ref != '' && 240 || 120 }}
    strategy:
      fail-fast: false
      matrix:
        toolchain: [stable, 1.85.0]
    env:
      RUSTUP_TOOLCHAIN: ${{ matrix.toolchain }}
      CC: gcc
      CXX: g++
      LIBCLANG_PATH: /usr/lib/llvm-18/lib
      CLANG_PATH: /usr/bin/clang-18
    steps:
      - uses: actions/checkout@v7.0.1
        with:
          fetch-depth: 0
      - name: Install reference build dependencies
        timeout-minutes: 10
        run: |
          .github/scripts/install-native-deps.sh cmake g++ git curl python3-dev libgoogle-glog-dev libgflags-dev libnuma-dev libyaml-cpp-dev libjsoncpp-dev libibverbs-dev libcurl4-openssl-dev libssl-dev libcli11-dev liburing-dev clang-18 libclang-18-dev
          rustup toolchain install "$RUSTUP_TOOLCHAIN" --profile minimal
      - name: Install profiling tools
        if: inputs.profile_only || inputs.tail_profile_only || inputs.baseline_ref != ''
        run: |
          .github/scripts/install-native-deps.sh strace linux-tools-generic
          sudo sysctl -w kernel.perf_event_paranoid=-1
          sudo sysctl -w kernel.kptr_restrict=0
      - name: Compare frozen native TENT and OrbiTE
        run: python3 tests/reference/run.py --fetch-reference --output "$RUNNER_TEMP/orbite-native-cpu"
      - name: Reject the frozen TENT client at the native listener
        run: |
          native="$RUNNER_TEMP/orbite-native-cpu"
          python3 tests/reference/wire/check.py --source "$native/reference" --build "$native/tent-build" --output "$RUNNER_TEMP/orbite-wire"
      - name: Compare frozen RDMA slice geometry and controls
        run: python3 tests/reference/rdma_geometry.py --tent-source "$RUNNER_TEMP/orbite-native-cpu/reference" --output "$RUNNER_TEMP/orbite-rdma-geometry"
      - name: Preserve RDMA geometry evidence
        if: always()
        uses: actions/upload-artifact@v7.0.2
        with:
          name: frozen-tent-rdma-geometry-${{ matrix.toolchain }}
          path: ${{ runner.temp }}/orbite-rdma-geometry/
      - name: Prepare warmed ext4 and tmpfs benchmark filesystems
        run: |
          if test -e /proc/sys/kernel/io_uring_disabled; then sudo sysctl -w kernel.io_uring_disabled=0; fi
          ext="$RUNNER_TEMP/orbite-bench-ext4"
          mkdir -p "$ext"
          if test "$(findmnt -n -o FSTYPE -T "$ext")" != ext4; then
            truncate -s 1G "$RUNNER_TEMP/bench-ext4.img"
            mkfs.ext4 -q -F "$RUNNER_TEMP/bench-ext4.img"
            sudo mount -o loop "$RUNNER_TEMP/bench-ext4.img" "$ext"
            sudo chown "$(id -u):$(id -g)" "$ext"
          fi
          echo "ORBITE_EXT4_DIR=$ext" >> "$GITHUB_ENV"
      - name: Profile the target owners (traced timings are not performance results)
        if: (inputs.profile_only || inputs.baseline_ref != '') && !inputs.tail_profile_only
        env:
          CARGO_PROFILE_RELEASE_DEBUG: 1
          CARGO_TARGET_DIR: ${{ runner.temp }}/profile-target
        run: python3 benches/run.py --reference "$RUNNER_TEMP/orbite-native-cpu" --output "$RUNNER_TEMP/orbite-profiles" --ext4 "$ORBITE_EXT4_DIR" --samples 100 --warmup 3 --workers 2 --trials 3 --profile-only
      - name: Prepare the matched baseline checkout
        if: inputs.baseline_ref != ''
        env:
          BASELINE_REF: ${{ inputs.baseline_ref }}
        run: git worktree add --detach "$RUNNER_TEMP/orbite-before" "$BASELINE_REF"
      - name: Trace io_uring sample tails (diagnostic only)
        if: inputs.tail_profile_only
        env:
          CARGO_PROFILE_RELEASE_DEBUG: 1
          CARGO_TARGET_DIR: ${{ runner.temp }}/profile-target
          BASELINE_REF: ${{ inputs.baseline_ref }}
        run: |
          if test -n "$BASELINE_REF"; then
            (cd "$RUNNER_TEMP/orbite-before" && CARGO_TARGET_DIR="$RUNNER_TEMP/baseline-profile-target" python3 benches/run.py --reference "$RUNNER_TEMP/orbite-native-cpu" --output "$RUNNER_TEMP/orbite-profiles-before" --ext4 "$ORBITE_EXT4_DIR" --samples 1000 --warmup 3 --workers 2 --trials 3 --tail-profile-only)
            python3 benches/analyze_io_tail.py "$RUNNER_TEMP/orbite-profiles-before/tail"
          fi
          python3 benches/run.py --reference "$RUNNER_TEMP/orbite-native-cpu" --output "$RUNNER_TEMP/orbite-profiles" --ext4 "$ORBITE_EXT4_DIR" --samples 1000 --warmup 3 --workers 2 --trials 3 --tail-profile-only
      - name: Matched baseline sweeps on the same runner
        if: inputs.baseline_ref != '' && !inputs.profile_only && !inputs.tail_profile_only
        env:
          CARGO_TARGET_DIR: ${{ runner.temp }}/baseline-target
        run: |
          cd "$RUNNER_TEMP/orbite-before"
          python3 benches/run.py --reference "$RUNNER_TEMP/orbite-native-cpu" --output "$RUNNER_TEMP/orbite-bench-before" --ext4 "$ORBITE_EXT4_DIR" --samples 100 --warmup 3 --workers 2 --trials 3
      - name: Matched byte-checked CPU sweeps (no hardware)
        if: '!inputs.profile_only && !inputs.tail_profile_only'
        run: python3 benches/run.py --reference "$RUNNER_TEMP/orbite-native-cpu" --output "$RUNNER_TEMP/orbite-bench" --ext4 "$ORBITE_EXT4_DIR" --samples 100 --warmup 3 --workers 2 --trials 3
      - name: Preserve raw measurements outside Git
        if: always()
        uses: actions/upload-artifact@v7.0.2
        with:
          name: cpu-bench-${{ matrix.toolchain }}
          path: |
            ${{ runner.temp }}/orbite-bench/*.json
            ${{ runner.temp }}/orbite-bench/*.jsonl
            ${{ runner.temp }}/orbite-bench/*.stdout
            ${{ runner.temp }}/orbite-bench/*.stderr
            ${{ runner.temp }}/orbite-bench-before/*.json
            ${{ runner.temp }}/orbite-bench-before/*.jsonl
            ${{ runner.temp }}/orbite-bench-before/*.stdout
            ${{ runner.temp }}/orbite-bench-before/*.stderr
            ${{ runner.temp }}/orbite-profiles/**
            ${{ runner.temp }}/orbite-profiles-before/**
      - name: Preserve comparison evidence
        if: always()
        uses: actions/upload-artifact@v7.0.2
        with:
          name: frozen-tent-cpu-${{ matrix.toolchain }}
          path: |
            ${{ runner.temp }}/orbite-wire/*.json
            ${{ runner.temp }}/orbite-native-cpu/manifest.json
            ${{ runner.temp }}/orbite-native-cpu/*.stdout
            ${{ runner.temp }}/orbite-native-cpu/*.stderr
            ${{ runner.temp }}/orbite-native-cpu/tent-build/CMakeCache.txt
            ${{ runner.temp }}/orbite-native-cpu/tent-driver
            ${{ runner.temp }}/orbite-native-cpu/tent-build/**/*.so*
            ${{ runner.temp }}/orbite-native-cpu/cargo/debug/examples/cpu_trace
