Skip to content

Commit b1b910e

Browse files
committed
[TS] One-command measurement pipeline for open-source TS projects
./run-project.sh <git-url | corpus-name | local-path> [--include dir] [--modes ...] [--commit sha] does the whole loop: clone, build the jacodb ts-frontend if missing, run the hybrid analyzer over all ablation modes with per-file fault isolation, aggregate, and write per-mode JSON reports, a summary table and a per-method CSV under results/<name>/. Demo on loiane/javascript-datastructures-algorithms (src/, 307 methods, 670 branch edges): PBT-only 62.5% branches in 1.7s; symbolic-only 57.2% in 427s; hybrid 75.1% in 243s (84/251 residual targets reached).
1 parent 41f0565 commit b1b910e

3 files changed

Lines changed: 191 additions & 35 deletions

File tree

usvm-ts-pbt/benchmarks/README.md

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,78 @@
11
# Benchmark infrastructure: hybrid analysis on open-source TypeScript
22

33
Batch experiments for the hybrid PBT + targeted symbolic execution pipeline
4-
over an open-source TS corpus, using the **native TS parser from jacodb**
4+
over open-source TS projects, using the **native TS parser from jacodb**
55
(`jacodb-ets/ts-frontend`, jacodb PR #361) as the EtsIR provider.
66

7-
## 1. One-time setup
7+
## TL;DR — one command per project
88

99
```bash
10-
# 1. Build the jacodb ts-frontend (from the jacodb checkout on the PR #361 branch):
11-
cd <jacodb>/jacodb-ets/ts-frontend && npm install && npm run build
10+
# by URL: clones into corpus/, analyzes, aggregates, writes reports
11+
./run-project.sh https://github.com/TheAlgorithms/TypeScript.git --include maths
1212

13-
# 2. Fetch the corpus (defined in corpus.json, pinned commits):
14-
./fetch-corpus.sh # requires git + jq; clones into benchmarks/corpus/
13+
# by corpus name (already cloned) or by local path
14+
./run-project.sh typescript-algorithms --include src
15+
./run-project.sh ~/work/my-ts-project
16+
17+
# narrower/faster runs
18+
./run-project.sh <target> --modes PBT_ONLY,HYBRID --max-files 40 --target-timeout 5
1519
```
1620

17-
Until jacodb PR #361 is merged and the usvm dependency pin is bumped, substitute
18-
the local jacodb build via the opt-in composite build flag `-PuseLocalJacodb=<path>`.
21+
The script does everything end-to-end:
22+
23+
1. resolves the project (clone by URL / corpus name / local path; `--commit <sha>`
24+
for reproducibility),
25+
2. builds `ts-frontend` if missing (`JACODB_DIR`, default `~/Programming/jacodb`),
26+
3. runs the hybrid analyzer over all requested ablation modes
27+
(default: `PBT_ONLY,SYMBOLIC_ONLY,HYBRID,HYBRID_WITH_HINTS`) with per-file
28+
fault isolation,
29+
4. aggregates and writes everything under `results/<name>/`:
30+
- `<name>-<MODE>.json` — full machine-readable report per mode
31+
(per-method coverage, coverage timelines, per-target wall time,
32+
hint/fallback attribution, unsupported-execution counters),
33+
- `<name>-summary.md` — the mode-comparison table,
34+
- `<name>.csv` — per-method rows for plotting.
35+
36+
Until jacodb PR #361 is merged and the usvm dependency pin is bumped, the local
37+
jacodb build is substituted automatically via `-PuseLocalJacodb=$JACODB_DIR`.
38+
39+
## Pinned corpus
40+
41+
`corpus.json` pins reference projects to commits; `./fetch-corpus.sh` clones them
42+
(requires `git` + `jq`). Preferred projects are algorithm-style repositories
43+
(self-contained functions, arithmetic- and branch-rich) — analyzable per-file at
44+
the EtsIR level without cross-module resolution. Cross-file imports resolve to
45+
honest `Unsupported` at call sites and are reported, never guessed.
46+
47+
## Manual (advanced) invocation
1948

20-
## 2. Running experiments
49+
The underlying CLI offers finer control:
2150

2251
```bash
2352
export ETS_IR_PROVIDER=ts-frontend
2453
export ETS_FRONTEND_DIR=<jacodb>/jacodb-ets/ts-frontend
25-
export ARKANALYZER_DIR=<pinned-arkanalyzer> # only needed for provider=arkanalyzer
2654

2755
./gradlew -PuseLocalJacodb=<jacodb> :usvm-ts-pbt:runHybrid --args="\
28-
usvm-ts-pbt/benchmarks/corpus/TheAlgorithms-TypeScript/maths \
29-
--recursive \
56+
<dir-or-file.ts> --recursive \
3057
--modes PBT_ONLY,SYMBOLIC_ONLY,HYBRID,HYBRID_WITH_HINTS \
3158
--pbt-iterations 1000 --target-timeout 10 \
32-
--out usvm-ts-pbt/benchmarks/results/maths"
33-
```
34-
35-
This produces one `HybridReport` JSON per mode: `<out>-<MODE>.json`.
36-
Paths are resolved relative to the repository root (the `runHybrid` working dir).
37-
38-
Per-file isolation: frontend/load failures and per-method analysis failures are
39-
counted and logged, never abort the batch (honest-numbers principle: the reports
40-
carry `unsupported`/failure counters).
59+
--out results/my-run"
4160

42-
## 3. Aggregation
43-
44-
```bash
45-
python3 aggregate.py results/maths-*.json --csv results/maths.csv
61+
python3 aggregate.py results/my-run-*.json --csv results/my-run.csv
4662
```
4763

48-
Prints a markdown mode-comparison table (branch/stmt coverage, PBT failures,
49-
targets reached/replay-confirmed/fallbacks, wall time) and optionally a
50-
per-method CSV for plots. Raw JSON reports retain full coverage timelines and
51-
per-target attribution for the paper's ablation analysis.
64+
See `./gradlew :usvm-ts-pbt:runHybrid --args=""` for the full option list
65+
(`--class`, `--method`, `--exclude`, `--seed`, `--no-fallback`, ...).
5266

53-
## 4. Corpus policy
67+
## Interpreting the numbers
5468

55-
`corpus.json` pins each project to a commit. Preferred projects: algorithm-style
56-
repositories (self-contained functions, arithmetic- and branch-rich) — analyzable
57-
per-file at the EtsIR level without cross-module resolution. Cross-file imports
58-
resolve to `Unsupported` at call sites and are reported, not guessed.
69+
- **Branch %** counts covered branch *edges* `(if, successor)` — the handoff
70+
granularity of the hybrid pipeline.
71+
- **Unsupported** counts PBT executions that hit constructs the concrete
72+
interpreter does not model (generators, cross-file imports under per-file
73+
scenes, exotic intrinsics). They are excluded from pass/fail verdicts.
74+
- **Reached / Replay OK** — symbolic targets reached, and those whose
75+
solver-synthesized inputs were *confirmed by concrete replay* (the ground
76+
truth for crediting a branch to the symbolic phase).
77+
- **Fallbacks** — hint-free retries after an unsuccessful hinted run: the
78+
measured price of the (deliberately unsound) type-hint pruning.

usvm-ts-pbt/benchmarks/corpus.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"url": "https://github.com/loiane/javascript-datastructures-algorithms.git",
2323
"commit": "e8ee8f9b8a07589533c4243a210d4cea7b090b10",
2424
"include": [
25-
"src/ts"
25+
"src"
2626
],
2727
"excludePatterns": [
2828
".test.ts",
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/usr/bin/env bash
2+
# End-to-end measurement pipeline for one open-source TypeScript project.
3+
#
4+
# ./run-project.sh <git-url | local-path | corpus-name> [options]
5+
#
6+
# Does everything: clone (if needed) -> build ts-frontend (if needed) ->
7+
# run the hybrid analyzer in all requested ablation modes -> aggregate ->
8+
# write reports and a summary table under results/<name>/.
9+
#
10+
# Examples:
11+
# ./run-project.sh https://github.com/TheAlgorithms/TypeScript.git --include maths
12+
# ./run-project.sh TheAlgorithms-TypeScript --include sorts --modes PBT_ONLY,HYBRID
13+
# ./run-project.sh ~/my/local/ts-project
14+
#
15+
# Options:
16+
# --include <subdir> analyze only this subdirectory of the project (repeatable)
17+
# --modes <M1,M2,...> ablation modes (default: PBT_ONLY,SYMBOLIC_ONLY,HYBRID,HYBRID_WITH_HINTS)
18+
# --pbt-iterations <n> PBT budget per method (default: 1000)
19+
# --target-timeout <sec> per-target symbolic timeout (default: 10)
20+
# --max-files <n> cap the number of files (default: unlimited)
21+
# --seed <n> PBT seed (default: 0)
22+
# --commit <sha> checkout this commit after cloning (reproducibility)
23+
#
24+
# Environment (auto-detected when possible):
25+
# JACODB_DIR jacodb checkout with jacodb-ets/ts-frontend (default: ~/Programming/jacodb)
26+
# ETS_FRONTEND_DIR built ts-frontend (default: $JACODB_DIR/jacodb-ets/ts-frontend)
27+
set -euo pipefail
28+
29+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
30+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
31+
CORPUS_DIR="$SCRIPT_DIR/corpus"
32+
RESULTS_DIR="$SCRIPT_DIR/results"
33+
34+
# ---------------------------------------------------------------- arguments
35+
[[ $# -ge 1 ]] || { grep '^#' "$0" | head -30; exit 1; }
36+
TARGET="$1"; shift
37+
38+
INCLUDES=()
39+
MODES="PBT_ONLY,SYMBOLIC_ONLY,HYBRID,HYBRID_WITH_HINTS"
40+
PBT_ITERATIONS=1000
41+
TARGET_TIMEOUT=10
42+
MAX_FILES=0
43+
SEED=0
44+
COMMIT=""
45+
46+
while [[ $# -gt 0 ]]; do
47+
case "$1" in
48+
--include) INCLUDES+=("$2"); shift 2 ;;
49+
--modes) MODES="$2"; shift 2 ;;
50+
--pbt-iterations) PBT_ITERATIONS="$2"; shift 2 ;;
51+
--target-timeout) TARGET_TIMEOUT="$2"; shift 2 ;;
52+
--max-files) MAX_FILES="$2"; shift 2 ;;
53+
--seed) SEED="$2"; shift 2 ;;
54+
--commit) COMMIT="$2"; shift 2 ;;
55+
*) echo "Unknown option: $1"; exit 1 ;;
56+
esac
57+
done
58+
59+
# ---------------------------------------------------------------- resolve project
60+
if [[ -d "$TARGET" ]]; then
61+
PROJECT_DIR="$(cd "$TARGET" && pwd)"
62+
NAME="$(basename "$PROJECT_DIR")"
63+
elif [[ "$TARGET" == http* || "$TARGET" == git@* ]]; then
64+
NAME="$(basename "$TARGET" .git)"
65+
PROJECT_DIR="$CORPUS_DIR/$NAME"
66+
if [[ ! -d "$PROJECT_DIR" ]]; then
67+
echo "[clone] $TARGET -> $PROJECT_DIR"
68+
git clone --filter=blob:none "$TARGET" "$PROJECT_DIR"
69+
fi
70+
if [[ -n "$COMMIT" ]]; then
71+
git -C "$PROJECT_DIR" checkout --quiet "$COMMIT"
72+
fi
73+
echo "[project] $NAME @ $(git -C "$PROJECT_DIR" rev-parse --short HEAD)"
74+
elif [[ -d "$CORPUS_DIR/$TARGET" ]]; then
75+
NAME="$TARGET"
76+
PROJECT_DIR="$CORPUS_DIR/$NAME"
77+
else
78+
echo "Not a directory, URL, or known corpus name: $TARGET"
79+
exit 1
80+
fi
81+
82+
# ---------------------------------------------------------------- toolchain
83+
JACODB_DIR="${JACODB_DIR:-$HOME/Programming/jacodb}"
84+
ETS_FRONTEND_DIR="${ETS_FRONTEND_DIR:-$JACODB_DIR/jacodb-ets/ts-frontend}"
85+
86+
if [[ ! -f "$ETS_FRONTEND_DIR/dist/index.js" ]]; then
87+
if [[ -f "$ETS_FRONTEND_DIR/package.json" ]]; then
88+
echo "[build] ts-frontend at $ETS_FRONTEND_DIR"
89+
(cd "$ETS_FRONTEND_DIR" && npm install && npm run build)
90+
else
91+
echo "ts-frontend not found: $ETS_FRONTEND_DIR (set ETS_FRONTEND_DIR or JACODB_DIR)"
92+
exit 1
93+
fi
94+
fi
95+
96+
# ---------------------------------------------------------------- inputs
97+
ANALYZE_DIRS=()
98+
if [[ ${#INCLUDES[@]} -gt 0 ]]; then
99+
for inc in "${INCLUDES[@]}"; do ANALYZE_DIRS+=("$PROJECT_DIR/$inc"); done
100+
else
101+
ANALYZE_DIRS=("$PROJECT_DIR")
102+
fi
103+
104+
OUT_DIR="$RESULTS_DIR/$NAME"
105+
mkdir -p "$OUT_DIR"
106+
107+
EXTRA_ARGS=()
108+
[[ "$MAX_FILES" != "0" ]] && EXTRA_ARGS+=("--max-files" "$MAX_FILES")
109+
110+
# ---------------------------------------------------------------- run
111+
export ETS_IR_PROVIDER=ts-frontend
112+
export ETS_FRONTEND_DIR
113+
114+
for dir in "${ANALYZE_DIRS[@]}"; do
115+
suffix=""
116+
[[ ${#ANALYZE_DIRS[@]} -gt 1 || ${#INCLUDES[@]} -gt 0 ]] && suffix="-$(basename "$dir")"
117+
out_prefix="$OUT_DIR/$NAME$suffix"
118+
119+
echo "[analyze] $dir"
120+
(cd "$REPO_ROOT" && ./gradlew -q "-PuseLocalJacodb=$JACODB_DIR" :usvm-ts-pbt:runHybrid --args="\
121+
$dir --recursive \
122+
--modes $MODES \
123+
--pbt-iterations $PBT_ITERATIONS \
124+
--target-timeout $TARGET_TIMEOUT \
125+
--seed $SEED \
126+
${EXTRA_ARGS[*]:-} \
127+
--out $out_prefix" 2>&1 | grep -vE "^\[|WARN|INFO|ERROR| at | org\.|Exception")
128+
129+
echo
130+
echo "== Summary: $NAME$suffix =="
131+
python3 "$SCRIPT_DIR/aggregate.py" "$out_prefix"-*.json --csv "$out_prefix.csv" \
132+
| tee "$out_prefix-summary.md"
133+
echo
134+
done
135+
136+
echo "Reports: $OUT_DIR"

0 commit comments

Comments
 (0)