From 7a6284be14de7813b92c3e11f276b684199a7a80 Mon Sep 17 00:00:00 2001 From: Guilherme Lampert Date: Wed, 21 Oct 2026 15:48:14 +1100 Subject: [PATCH 1/3] Run the tests with ASan and UBSan in CI Add a job to the CI workflow that builds openvcl and the tests in Debug with -fsanitize=address,undefined and runs them. It needs no PS2 toolchain, so it runs on ubuntu-latest without the ps2sdk container. The integration tests run openvcl as a child process, and a test that expects openvcl to fail would pass over a sanitizer report on its stderr. Reports go to files instead, and any report fails the job. openvcl does not free its register allocation data before it exits, and LeakSanitizer would fail 132 of the 444 tests on that, so leaks are not checked. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/compilation.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 8ddfb07..fc69074 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -43,3 +43,33 @@ jobs: path: | openvcl examples/**/*.elf + + sanitizers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Compile OpenVCL with ASan and UBSan + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=OFF -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" + cmake --build build -j $(getconf _NPROCESSORS_ONLN) + + # Reports go to files, and any report fails the job: the integration tests + # run openvcl as a child process, and a test that expects it to fail would + # pass over a report on its stderr. openvcl does not free its register + # allocation data before it exits, so leaks are not checked. + - name: Run OpenVCL tests + env: + ASAN_OPTIONS: detect_leaks=0:log_path=${{ runner.temp }}/sanitizers/report + UBSAN_OPTIONS: print_stacktrace=1:log_path=${{ runner.temp }}/sanitizers/report + run: | + mkdir -p "$RUNNER_TEMP/sanitizers" + status=0 + ctest --test-dir build --output-on-failure || status=1 + for report in "$RUNNER_TEMP"/sanitizers/report.*; do + if [ -f "$report" ]; then + cat "$report" + status=1 + fi + done + exit $status From 0390b395437bd39a7a323af28210252da45c0b42 Mon Sep 17 00:00:00 2001 From: Guilherme Lampert Date: Wed, 21 Oct 2026 17:26:18 +1100 Subject: [PATCH 2/3] Compile the ps2gl fixtures in the sanitizer job No test compiles the ps2gl programs in test/fixtures, so the sanitizer job never ran them through openvcl. Compile each one after the tests, with the same handling of reports. They take the whole compiler, software pipeliner included, through real programs. Build at -O1, as ASan's documentation suggests for speed: at -O0 the fixtures alone take several minutes to compile, and -O1 cuts that to about a third. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/compilation.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index fc69074..359207a 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -49,16 +49,19 @@ jobs: steps: - uses: actions/checkout@v4 + # -O1, as ASan's documentation suggests for speed: at -O0 the fixtures + # below take several minutes to compile - name: Compile OpenVCL with ASan and UBSan run: | - cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=OFF -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" + cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=OFF -DCMAKE_CXX_FLAGS="-O1 -fsanitize=address,undefined -fno-omit-frame-pointer" cmake --build build -j $(getconf _NPROCESSORS_ONLN) # Reports go to files, and any report fails the job: the integration tests # run openvcl as a child process, and a test that expects it to fail would # pass over a report on its stderr. openvcl does not free its register - # allocation data before it exits, so leaks are not checked. - - name: Run OpenVCL tests + # allocation data before it exits, so leaks are not checked. No test + # compiles the ps2gl programs in test/fixtures, so they are compiled here. + - name: Run OpenVCL tests and compile the fixtures env: ASAN_OPTIONS: detect_leaks=0:log_path=${{ runner.temp }}/sanitizers/report UBSAN_OPTIONS: print_stacktrace=1:log_path=${{ runner.temp }}/sanitizers/report @@ -66,6 +69,12 @@ jobs: mkdir -p "$RUNNER_TEMP/sanitizers" status=0 ctest --test-dir build --output-on-failure || status=1 + for fixture in test/fixtures/*.vcl; do + if ! ./openvcl -o "$RUNNER_TEMP/fixture.vsm" "$fixture"; then + echo "openvcl failed on $fixture" + status=1 + fi + done for report in "$RUNNER_TEMP"/sanitizers/report.*; do if [ -f "$report" ]; then cat "$report" From ca616a76c36619985e25a616f634fb96820fb103 Mon Sep 17 00:00:00 2001 From: Guilherme Lampert Date: Wed, 21 Oct 2026 17:42:57 +1100 Subject: [PATCH 3/3] Compile the fixtures in parallel instead of at -O1 On the CI runner -O1 made the sanitized build take four minutes instead of half a minute, more than it saved on the fixtures, and the job took over seven minutes. Build at -O0 again and compile the twelve fixtures at the same time: the slowest alone takes over a minute, and together they need under 5 GB of memory. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/compilation.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 359207a..7cde172 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -49,18 +49,17 @@ jobs: steps: - uses: actions/checkout@v4 - # -O1, as ASan's documentation suggests for speed: at -O0 the fixtures - # below take several minutes to compile - name: Compile OpenVCL with ASan and UBSan run: | - cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=OFF -DCMAKE_CXX_FLAGS="-O1 -fsanitize=address,undefined -fno-omit-frame-pointer" + cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=OFF -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" cmake --build build -j $(getconf _NPROCESSORS_ONLN) # Reports go to files, and any report fails the job: the integration tests # run openvcl as a child process, and a test that expects it to fail would # pass over a report on its stderr. openvcl does not free its register # allocation data before it exits, so leaks are not checked. No test - # compiles the ps2gl programs in test/fixtures, so they are compiled here. + # compiles the ps2gl programs in test/fixtures, so they are compiled here, + # all at once: with the sanitizers the slowest takes over a minute. - name: Run OpenVCL tests and compile the fixtures env: ASAN_OPTIONS: detect_leaks=0:log_path=${{ runner.temp }}/sanitizers/report @@ -70,11 +69,14 @@ jobs: status=0 ctest --test-dir build --output-on-failure || status=1 for fixture in test/fixtures/*.vcl; do - if ! ./openvcl -o "$RUNNER_TEMP/fixture.vsm" "$fixture"; then - echo "openvcl failed on $fixture" - status=1 - fi + ( ./openvcl -o "$RUNNER_TEMP/$(basename "$fixture").vsm" "$fixture" || + echo "openvcl failed on $fixture" >> "$RUNNER_TEMP/failed" ) & done + wait + if [ -f "$RUNNER_TEMP/failed" ]; then + cat "$RUNNER_TEMP/failed" + status=1 + fi for report in "$RUNNER_TEMP"/sanitizers/report.*; do if [ -f "$report" ]; then cat "$report"