Files
codebase-memory-mcp/tests
Mauricio Offermann c059396eb2 fix(build): give sanitized-build detection one spelling and a backstop
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 0a163d4f 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>
2026-08-14 06:46:05 -04:00
..
2026-06-28 13:05:05 +02:00