[WIP] feat(ci): add asan memory leak test and suppression list#772
[WIP] feat(ci): add asan memory leak test and suppression list#772johnnyshields wants to merge 4 commits intocompio-rs:masterfrom
Conversation
| - '**/Cargo.toml' | ||
| - 'scripts/asan.sh' | ||
| - 'scripts/asan.supp' | ||
| - '.github/workflows/ci_test_asan.yml' |
There was a problem hiding this comment.
only run when .rs or these scripts are touched
|
Why did asan test pass before #769 being merged? |
|
Because I added that case to the suppression list: https://github.com/compio-rs/compio/pull/772/changes#diff-87e3f871062a1c5e918aa8b7bc9e526b1687315e758121cccd0faefb58d822fdR5 |
|
I'll remove it now and we can see it fail. |
|
I'll investigate what's going on here later. |
f87e167 to
aa78366
Compare
|
@Berrysoft this is ready for review. You can see the compio driver leaks clearly in the CI run: |
| extract_patterns_for_crate() { | ||
| local crate="$1" | ||
| [[ -f "$TOML_FILE" ]] || return 0 | ||
| python3 -c " |
There was a problem hiding this comment.
Is if possible to exact the python code to separate files?
There was a problem hiding this comment.
Pull request overview
Adds an AddressSanitizer/LeakSanitizer test runner for the Rust workspace and wires it into CI, with a suppression config to keep known leaks from failing runs.
Changes:
- Introduces
scripts/asan.shto build all test binaries with ASan and run them individually, optionally collecting suppression patterns. - Adds
scripts/asan.tomlfor per-crate (intended per-test) LSan suppression patterns. - Adds a GitHub Actions workflow to run the ASan script on PRs/pushes to
master.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
scripts/asan.toml |
Adds LSan suppression patterns keyed by crate and test file. |
scripts/asan.sh |
New ASan/LSan runner that builds with -Zsanitizer=address and executes each test binary with configured suppressions. |
.github/workflows/ci_test_asan.yml |
New CI workflow invoking ./scripts/asan.sh --ci x86_64-unknown-linux-gnu. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| if '/' in pkg_id: | ||
| pkg = pkg_id.split('#')[0].split('/')[-1] | ||
| else: | ||
| pkg = pkg_id.split(' ')[0] |
| # --- Helper: extract suppression patterns for a crate from asan.toml --- | ||
| # Writes leak:pattern lines to stdout. | ||
| extract_patterns_for_crate() { | ||
| local crate="$1" | ||
| [[ -f "$TOML_FILE" ]] || return 0 | ||
| python3 -c " | ||
| import sys, re | ||
| crate, toml_path = sys.argv[1], sys.argv[2] | ||
| with open(toml_path) as f: | ||
| content = f.read() | ||
| in_crate = in_patterns = False | ||
| for line in content.splitlines(): | ||
| s = line.strip() | ||
| if s.startswith('#'): | ||
| continue | ||
| m = re.match(r'^\[(.+)\]$', s) | ||
| if m: | ||
| parts = m.group(1).split('.', 1) | ||
| in_crate = (parts[0].strip().strip('\"') == crate) | ||
| in_patterns = False | ||
| continue | ||
| if not in_crate: | ||
| continue | ||
| if 'patterns' in s and '=' in s: | ||
| in_patterns = True | ||
| if in_patterns: | ||
| for p in re.findall(r'\"([^\"]+)\"', s): | ||
| if not p.startswith('tests/'): | ||
| print(f'leak:{p}') | ||
| if ']' in s: | ||
| in_patterns = False | ||
| " "$crate" "$TOML_FILE" |
| # Run the test binary. | ||
| if [[ "${ASAN_VERBOSE:-0}" == "1" ]]; then | ||
| "$EXE" --test-threads=1 "${TEST_ARGS[@]}" 2>&1 | tee "$TMPFILE" | ||
| else | ||
| "$EXE" --test-threads=1 "${TEST_ARGS[@]}" > "$TMPFILE" 2>&1 || true | ||
|
|
||
| # Parse results. | ||
| RESULT_LINE=$(grep '^test result:' "$TMPFILE" | tail -1 || true) | ||
| if [[ -z "$RESULT_LINE" ]]; then | ||
| TESTS_RAN=0 | ||
| else | ||
| P=$(echo "$RESULT_LINE" | grep -oP '\d+(?= passed)' || echo 0) | ||
| F=$(echo "$RESULT_LINE" | grep -oP '\d+(?= failed)' || echo 0) | ||
| I=$(echo "$RESULT_LINE" | grep -oP '\d+(?= ignored)' || echo 0) | ||
| TESTS_RAN=$((P + F)) | ||
| fi | ||
|
|
||
| HAS_LEAK=0 | ||
| if grep -q 'LeakSanitizer: detected memory leaks' "$TMPFILE"; then | ||
| HAS_LEAK=1 | ||
| fi | ||
|
|
| BINARY_COUNT=$(echo "$BINARIES" | wc -l) | ||
| echo "Built $BINARY_COUNT test binaries." | ||
| echo "" | ||
|
|
||
| # --- Phase 2: Run each binary with per-crate suppressions --- | ||
| LEAK_DETAILS=() | ||
| FAIL_DETAILS=() | ||
| TMPFILES=() | ||
| trap 'rm -f "${TMPFILES[@]}"' EXIT | ||
|
|
||
| # Group binaries by crate, generate one supp file per crate. | ||
| PREV_CRATE="" | ||
| CRATE_SUPP="" | ||
| BIN_INDEX=0 | ||
|
|
||
| while IFS=$'\t' read -r CRATE NAME EXE; do | ||
| BIN_INDEX=$((BIN_INDEX + 1)) |
| export ASAN_SYMBOLIZER_PATH="$(command -v "$candidate")" | ||
| break | ||
| fi | ||
| done |
|
OK so compio-driver has multiple leaks, and the item I removed on the suppression list seems to have exposed the leaks not fixed by my previous PR 🤦♂️, so it's still failing. I will do a third pass at this whole thing, probably just re-write the whole script in python given the complexity it is evolving into, and address the Copilot comments. This was easier when I was doing it in a single crate 😅 |
Follow-up from #769
There are some legitimate leaks to fix here, particularly in the
compio-quiclib. I've added everything existing to the suppression list for now.