fix(build): give sanitized-build detection one spelling and a backstop - #1615
Conversation
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
Four places ask "is this binary instrumented?" and each asked it differently. One of them, the C# LSP bench, only recognised ASan, so TSan and MSan measured an instrumented parse against the NATIVE 200ms budget. The other three carried a hand-copied list of `__SANITIZE_*__` macros that nobody kept in sync. That drift is what the TSan gap was made of. CFLAGS_TSAN never passed SANITIZED_DEFINE, and the per-site conditions could not cover for it: they test `__SANITIZE_THREAD__`, which is GCC's spelling. Clang — the compiler that leg uses — announces thread instrumentation through `__has_feature(thread_sanitizer)` only, and no site consulted it. The claim in 0a163d4 that compiler probes could not have helped is true of the probes we had, not of the one clang actually offers. src/foundation/sanitized.h now answers the question once, as CBM_SANITIZED, from two sources with distinct jobs: - CBM_SANITIZED_BUILD from the build system stays the source of truth, and is the ONLY thing that can answer for UBSan and trap-UBSan: undefined-behaviour instrumentation leaves no macro and no __has_feature bit to probe. - The clang and GCC probes are the backstop for the three sanitizers that do announce themselves, so a lane that forgets the define still gets correct budgets instead of native ones on an instrumented binary. Deliberately no #error when a probe fires without the define: promoting leaves the binary correct while the lane gets fixed, and it does not break an out-of-tree `make CFLAGS_EXTRA=-fsanitize=address` that never went near Makefile.cbm. __has_feature is defined away where it does not exist rather than guarded with `#elif defined(__has_feature)`. The guarded form compiles everywhere but fails cppcheck, which walks every configuration and rejects the file with "failed to evaluate #if condition, undefined function-like macro invocation". The define-away idiom is what clang documents and what tests/test_mem.c already uses; `defined(...) && __has_feature(...)` is not an option at all, since && does not spare a preprocessor without the builtin from parsing `0 (0)`. Verified the resolution rather than assuming it (clang 22, -dM -E): native CBM_SANITIZED 0 -DCBM_SANITIZED_BUILD=1 CBM_SANITIZED 1 -fsanitize=address CBM_SANITIZED 1 -fsanitize=thread (linux target) CBM_SANITIZED 1 -fsanitize=memory (linux target) CBM_SANITIZED 1 -fsanitize=undefined CBM_SANITIZED 0 <- define-only, as designed The third row is the one that matters: the TSan failure this header is named after would have self-healed. Also wired the define into the instrumented flag sets of our own code that still lacked it — CXXFLAGS_TSAN (preprocessor.cpp is ours, and CXXFLAGS_TEST already had it), GRAMMAR_CFLAGS_TEST and GRAMMAR_CFLAGS_TSAN. Neither tree can include the header today (no -Isrc), so this is the build system keeping its own promise rather than a behaviour change. Vendored flag sets are untouched: mimalloc, sqlite3, tre, zstd, lz4 and tree-sitter read no macro of ours. Behaviour change worth naming: test_cs_lsp_bench now allows 2000ms on the TSan and MSan lanes instead of 200ms. It loosens a bound that was being applied to an instrumented binary by accident; it never tightens one. Not verified locally: this machine has no POSIX-target compiler, so the POSIX half of subprocess.c was not compiled here. The full Windows test-runner builds clean with -Werror and the subprocess suite is green (14 passed, 17 skipped); clang-format clean; macro matrix as above. Signed-off-by: Mauricio Offermann <mauricio.offermann@gocode.cl>
b01e98f to
c059396
Compare
|
Force-pushed one amend: the first push failed the The arm was nested under |
What does this PR do?
Follow-up to #1590/#1595. Those widened the spawn-retry budget for sanitized builds and then had to
chase the define onto the TSan leg. This removes the class of failure rather than the instance.
The four spellings. Four places ask "is this binary instrumented?", each differently:
src/foundation/subprocess.cCBM_SANITIZED_BUILD+ three__SANITIZE_*__tests/test_daemon_frontend.csrc/foundation/compat_thread.cdefined(X) && Xtests/test_cs_lsp_bench.cThe last one means TSan and MSan were measuring an instrumented parse against the native 200 ms budget.
Why the probes could not cover for the missing define.
0a163d4fconcluded that compiler probeswould not have helped, because clang spells thread instrumentation
__has_feature(thread_sanitizer)rather than
__SANITIZE_THREAD__. That is right about the probes we had — and it is exactly theprobe that was missing. No site consulted
__has_feature.What replaces them.
src/foundation/sanitized.hanswers once, asCBM_SANITIZED, from twosources with distinct jobs:
CBM_SANITIZED_BUILDfrom the build system stays the source of truth, and is the only thingthat can answer for UBSan and trap-UBSan — UB instrumentation leaves no macro and no
__has_featurebit behind.a lane that forgets the define still gets correct budgets instead of native ones.
No
#errorwhen a probe fires without the define: promoting leaves the binary correct while the lanegets fixed, and it does not break an out-of-tree
make CFLAGS_EXTRA=-fsanitize=address.Build lanes. The define now also goes on the instrumented flag sets of our own code that lacked
it:
CXXFLAGS_TSAN(preprocessor.cppis ours;CXXFLAGS_TESTalready had it),GRAMMAR_CFLAGS_TEST,GRAMMAR_CFLAGS_TSAN. Neither tree can include the header today (no-Isrc),so this is the build system keeping its own promise, not a behaviour change. Vendored flag sets are
untouched.
Behaviour change worth naming:
test_cs_lsp_benchnow allows 2000 ms on the TSan and MSan lanesinstead of 200 ms. It loosens a bound that was being applied to an instrumented binary by accident.
Verification
Resolution matrix, measured rather than assumed (clang 22,
-dM -E):CBM_SANITIZED-DCBM_SANITIZED_BUILD=1-fsanitize=address-fsanitize=thread(linux target)-fsanitize=memory(linux target)-fsanitize=undefinedThe thread row is the point: the failure this header is named after would have self-healed.
Not verified locally, needs CI: this machine has no POSIX-target compiler, so the POSIX half of
subprocess.cwas compiled only on the Windows leg, and the sanitized lanes (test-tsan, ASan,MSan, trap-UBSan) were not run.
make -f Makefile.cbm test-foundationdoes not link here for anunrelated pre-existing reason —
tests/test_main.creferences the store/cypher/... suitesunconditionally, so that target cannot link against
TEST_FOUNDATION_SRCSalone.Checklist
git commit -s)make -f Makefile.cbm test) — see above, no POSIX toolchain on this boxruntime surface of its own