#!/usr/bin/env bash
# Measure Drepper's three mutexes against the real futex system call, and keep
# the evidence where tests/futex.test.mjs can read it.
#
#   scripts/futex-measure.sh [out-dir]     default tests/fixtures/futex
#
# Each run writes three things: the program's own JSON count of every atomic
# operation and futex call it made, the strace log of every futex call in the
# process, and nothing else. The test narrows the log to the one futex word the
# program names, counts it, and requires the two counts to agree; it then holds
# both against the counts the page's model predicts. The kernel the run was
# made on is recorded beside them, because a syscall count is a fact about a
# kernel and not about a program.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUT="${1:-$ROOT/tests/fixtures/futex}"
# Where the binary is built. Not /tmp: on the machine this was first run on,
# /tmp is mounted noexec and the binary could not be started. node_modules is
# ignored by git, so nothing built here can be committed by accident.
PARENT="${FUTEX_WORK:-$ROOT/node_modules/.cache}"
mkdir -p "$PARENT"
WORK="$(mktemp -d "$PARENT/futex-measure.XXXXXX")"
trap 'rm -rf "$WORK"' EXIT

mkdir -p "$OUT"
cc -O2 -Wall -Wextra -pthread -o "$WORK/futex-measure" "$ROOT/scripts/futex-measure.c"

{
  printf '{"uname_r": "%s", "uname_v": "%s", "machine": "%s", "cpus": %s, ' \
    "$(uname -r)" "$(uname -v)" "$(uname -m)" "$(nproc)"
  printf '"strace": "%s", "cc": "%s", "measured": "%s"}\n' \
    "$(strace -V | head -1)" "$(cc --version | head -1)" "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
} > "$OUT/kernel.json"

# The logs are kept whole, compressed, because the test reads every line of
# them; a summary written here would be a second count nobody checks.
run() {
  local name="$1"; shift
  strace -f -qq -e trace=futex -o "$WORK/$name.strace" \
    "$WORK/futex-measure" "$@" > "$OUT/$name.json"
  gzip -n -9 -c "$WORK/$name.strace" > "$OUT/$name.strace.gz"
  echo "  $name: $(head -c 160 "$OUT/$name.json")..."
}

for m in mutex mutex2 mutex3; do
  run "$m-uncontended" "$m" uncontended 1000
  run "$m-contended" "$m" contended 4 1000
  run "$m-held" "$m" held 3 20 20
done
echo "wrote $(ls "$OUT" | wc -l) files to $OUT"
