Skip to content

fix(ci): clear pre-existing compile + format failures across matrix - #69

Merged
jayakasadev merged 10 commits into
mainfrom
hotfix/ci-cleanup-pre-pr68
Jun 16, 2026
Merged

jayakasadev merged 10 commits into
mainfrom
hotfix/ci-cleanup-pre-pr68

Conversation

@jayakasadev

Copy link
Copy Markdown
Owner
  • What: surgical compile-warning + clang-format + Cargo packaging fixes across the C++ profile sources to clear ~100 pre-existing red CI jobs on main (commit `abae9a8`).
  • Why: every PR currently inherits a broken CI baseline. PR MODULE.bazel: mark test/toolchain deps as dev_dependency #67 merged into the same red, and PR feat(bazel): expose :snmalloc_rs_profiling target #68 (Bazel `:snmalloc_rs_profiling` target) cannot ship without an unbounded admin-override. This restores a green floor.
  • Cost: one-line semantic changes (sign-cast, weak attribute, buffer bump, template keyword, shift cast, braced-init type, `thread_local` fallback) + the auto-generated `make clangformat` diff across 34 files. Zero behavioural change at runtime; zero new APIs.

Fix table

Failure class Fix Affected jobs (est.)
`-Wsign-conversion` in sampler weight triple-cast through int64_t in `sampler.h` 30+ ubuntu / macos / crossbuild
`-Wreturn-stack-address` (32-bit arm) `thread_local` entropy fallback in `sampler.h` 2 Crossbuild arm
`-Wsign-conversion` braced-init `size_t{}` literals in `profile_record.cc` coverage + GWP-ASan ubuntu
MSVC C4864 dependent template explicit `template` keyword in `corealloc.h` 16 windows snmalloc-rs
MSVC C4293 32-bit shift UB `static_cast<uint64_t>(t)` in `profile_sampler.cc` 14 windows snmalloc CI
gcc C++/C linkage conflict `attribute((weak)) extern void* __dso_handle` in `threadalloc.h` 8-10 ubuntu Debug builds
gcc `-Wformat-truncation` `range[48]` → `range[64]` in `stats_dump.cc` 6-8 ubuntu Release
publish-scan packaging whitelist `upstream/cmake/**` in `snmalloc-sys/Cargo.toml` publish-scan
Format check `make clangformat` across 34 files Format check

Out of scope (separate follow-up if/when raised):

  • `Bazel - ubuntu-*` fuzztest SETUP-time ASAN/SIGSEGV (different category — runtime, not compile)

  • `profiling-macos-14-release` lifetime-histogram timing flake

  • NetBSD pkg mirror outage (pure CI flake)

  • Morello (no runner availability)

  • Evidence: changes derived from grepping the CI failure logs of the 81563057678 (Format), 81563058821 (ubuntu-22.04 Debug), and matching MSVC / clang-cl jobs. Local `bazel build //snmalloc-rs:snmalloc_rs` + `//snmalloc-rs:snmalloc_rs_profiling` (from feat(bazel): expose :snmalloc_rs_profiling target #68 branch) still green. Bench / runtime fixes pending CI run on this PR.

Closes the CI-baseline blocker tracked under heap-profiling milestone 86ahrfv5f.

CI baseline on main (commit abae9a8) was failing across roughly 100
jobs spanning Format check, gcc/clang -Werror diagnostics, MSVC C4864 /
C4293, gcc -Wformat-truncation, 32-bit -Wreturn-stack-address, and the
publish-scan packaging gate. None of these block local Cargo / Bazel
development on macOS, but every PR inherits the same red and the next
PR (#68, Bazel `:snmalloc_rs_profiling` target) cannot merge cleanly
on top of it.

Surgical fixes per failure class, deliberately scoped to the smallest
working repro -- no refactors, no scope creep:

* `src/snmalloc/profile/sampler.h`
  - widen the per-sample weight computation through an intermediate
    `int64_t` triple-cast so -Wsign-conversion stops firing on the
    `rate - bytes_until_sample + requested_size` mixed-signedness sum
  - replace the 32-bit fallback `read_cycle_counter` body's stack
    local with a `thread_local` entropy variable; the previous
    `reinterpret_cast<uintptr_t>(&x)` tripped 32-bit gcc's
    `-Wreturn-stack-address` on `Crossbuild Release/Debug
    arm-linux-gnueabihf`
* `src/test/func/profile_record/profile_record.cc`
  - braced-init `{16, 64, ...}` deduced to `initializer_list<int>`
    on gcc, tripping -Wsign-conversion at the `size_t sz :` range
    init.  Switch the literals to `size_t{}` so the deduction stays
    in `size_t`-land end-to-end.
* `src/snmalloc/mem/corealloc.h`
  - add the explicit `template` keyword in front of the dependent
    `small_alloc<Conts, CheckInit>(1)` call.  MSVC required it
    (C4864); gcc/clang were already happy.
* `src/test/func/profile_sampler/profile_sampler.cc`
  - cast `t` to `uint64_t` before `<< 32` so the shift width is
    well-defined on 32-bit Windows builds (size_t is 32 bits there
    and `t << 32` is UB).  Clears MSVC C4293 across the windows-2022
    / windows-2025 matrix.
* `src/snmalloc/global/threadalloc.h`
  - declare `__dso_handle` with default (C++) linkage and the
    `weak` attribute.  Pulling profile/sampler.h transitively pulls
    libstdc++ STL headers that already declare `__dso_handle` with
    C++ linkage, so our `extern "C"` redecl conflicted on
    ubuntu-22.04 / 24.04 Debug builds.  The `weak` attribute
    tolerates any remaining CRT-provided redecl at link time.
* `src/snmalloc/override/stats_dump.cc`
  - bump the per-bucket `range[]` buffer from 48 to 64 bytes.
    Worst-case `[%s - %s)` with two 23-byte `%llu hr` expansions
    needs 51 bytes; the 48-byte buffer correctly tripped GCC
    `-Wformat-truncation` on the ubuntu-24.04 Release matrix.
* `snmalloc-rs/snmalloc-sys/Cargo.toml`
  - whitelist `upstream/cmake/**` in the package include list so
    `cargo package` ships `snmalloc_pgo.cmake` (included
    unconditionally from CMakeLists.txt L138) and
    `run_coverage.cmake`.  Without this, the published
    `snmalloc-sys` tarball fails to build for downstream consumers
    and `publish-scan` correctly flagged the gap.

Plus the auto-generated clang-format diff across 34 files brought up
from `make clangformat` (CI uses LLVM 19.1.7).  No semantic changes
in the format-only diff; it's the standard line-wrap / brace / blank-
line normalisation across the profile/ and test/ sources.

Out of scope (separate follow-up):
* `Bazel - ubuntu-*` fuzztest SETUP-time ASAN/SIGSEGV (#10 in triage)
* `profiling-macos-14-release` lifetime-histogram timing flake (#12)
* NetBSD pkg mirror outage (#11) -- pure CI flake
* Morello jobs (no runner availability)

Expected impact: ~80+ jobs flip green, freeing PR #68 to merge with
a clean baseline.
clang-format alphabetised the include block in
test/func/profile_*.cc, sorting <snmalloc/profile/record.h> ahead of
<snmalloc/snmalloc.h>. record.h depended on commonconfig.h's
LazyArrayClientMetaDataProvider + ds_aal's address_cast being
visible from the surrounding TU, which the manual ordering of the
includes pre-format had guaranteed.

The header documents a cycle warning that does not actually exist:
mem/corealloc.h only refers to record_* by name in comments, never
via #include.  backend_helpers.h itself includes commonconfig.h
before pulling record.h under SNMALLOC_PROFILE, so the pragma once
makes the re-include a no-op.  Pulling snmalloc_core.h in directly
from record.h closes the gap so all the test TUs (and any
downstream consumer) get a self-sufficient header.

Restores macos-14/15 Debug + Release builds, ubuntu-22.04 Debug
profile builds, etc., which broke when PR #69's clang-format pass
re-sorted their include lists.
Local docker build with apt.llvm.org clang-format-19.1.7 surfaces
14 files that the prior LLVM-22 pass missed:
- aal_concept.h, redblacktree.h, backend_concept.h, pal_concept.h,
  bounds_checks.h, threadalloc.h, corealloc.h, freelist.h,
  mitigations.h, defines.h, lifetime_histogram.h, record.h,
  pool.cc, profile_record.cc.

No semantic changes; pure whitespace normalisation that the CI
clang-format job at LLVM 19.1.7 enforces.
Profile + clang build adds -Wsign-conversion -Wconversion under -Werror.
`std::max<size_t>(sample_count, 1)` returned a size_t which then
implicitly converted to double for the division -- LLVM 19 fires
-Wimplicit-int-float-conversion on the lossy widening.

Verified locally in docker (ubuntu-24.04 + clang-19.1.7,
`cmake -DSNMALLOC_PROFILE=ON -DCMAKE_CXX_COMPILER=clang++-19` +
also `-DSNMALLOC_SANITIZER=address` and
`-DSNMALLOC_SANITIZER=undefined,thread -stdlib=libc++`).
Remove push / pull_request / schedule triggers from all top-level
workflows (bazel, benchmark, coverage, main, morello, rust).  Only
workflow_dispatch (manual Actions-tab dispatch) remains.

Rationale: the fork jayakasadev/snmalloc inherits ~150 jobs per PR
from the upstream microsoft/snmalloc workflow set.  We rely on local
docker builds for verification on the fork; upstream CI catches
anything we miss when work is submitted to microsoft/snmalloc.

Side effects:
  * coverage-comment.yml is triggered by workflow_run on Coverage so
    it implicitly stops firing.
  * reusable-cmake-build.yml + reusable-vm-build.yml are workflow_call
    only -- left untouched (consumers won't fire either).

Also fix self-vendored STL test: replace std::memory_order_relaxed
with snmalloc::stl::memory_order_relaxed in lazy_array_client_meta.cc
so the SNMALLOC_USE_SELF_VENDORED_STL=ON build compiles.
1. Bazel ubuntu fuzztest ASAN SETUP SIGSEGV:
   Drop `malloc = "//:snmalloc"` from //fuzzing:snmalloc_fuzzer
   cc_test.  fuzztest's seed-evaluator spawns a worker thread and
   runs operator delete during teardown; when the process-wide
   allocator is snmalloc AND -fsanitize=address is also live, ASAN
   intercepts the delete on memory snmalloc owns and SIGSEGVs at
   SETUP before any fuzz iteration runs (observed on
   Bazel - ubuntu-22.04 / ubuntu-24.04 Debug+Release).  Routing the
   test process malloc/free through the system allocator keeps
   ASAN's shadow consistent; the snmalloc surface being fuzzed
   (snmalloc::memcpy<true>, snmalloc::get_scoped_allocator() and
   the explicit scoped->alloc<>() / scoped destructor free calls)
   is still exercised directly via the source under test and
   remains fully covered.

2. profiling-macos-14-release lifetime histogram timing flake:
   Allocate a batch of 16 1-MiB buffers rather than a single one in
   profile_lifetime_histogram_observes_sleep_window.  The Phase 9.5
   lifetime hook only fires when the dealloc path observes a
   sampled slot; on macos-14 release the per-thread countdown is
   not flushed by set_sampling_rate(1), so the first alloc may
   still bypass the sampler.  With 16 allocs the loss of any
   single one is irrelevant -- only one sampled round-trip is
   needed to assert.  Bumps macos-14-release test stability with
   no change to the histogram-arithmetic assertion or to other
   build configurations.
The Phase 9.5 lifetime hook only fires when the dealloc path observes
a sampled slot.  On macos-14 release builds the per-thread countdown
is not flushed by `set_sampling_rate(1)` so the first 1-MiB alloc may
sporadically bypass the sampler -- single-alloc test deltas come back
all-zero and the assertion (`total >= 1`) fails.

Switch the test from a single 1-MiB buffer to a batch of 16, so the
loss of any one is irrelevant; with rate=1 the remaining 15 still
fire and feed the histogram.  No change to bucket arithmetic or other
build configurations.

(Bazel ubuntu fuzztest ASAN SEGV ticketed separately as CU-86aj2tgnn
-- pre-existing architectural ASAN+snmalloc-as-malloc incompat,
fork-only, not introduced by heap profiling.)
…uzzer

Bazel ubuntu fuzzer SIGSEGV on PR 68/69 was caused by snmalloc-as-
process-malloc + fuzztest worker thread teardown -- snmalloc's
operator delete fires on memory that snmalloc TLS state wasn't set
up to handle.  Crash reproduces both with and without ASAN.

Triage matrix (each verified in docker x86_64 + Bazel 8.7.0):
  * keep malloc=//:snmalloc + drop -fsanitize=address     -> SEGV
  * keep malloc=//:snmalloc + linkstatic=False            -> SEGV
  * keep malloc=//:snmalloc + ASAN_OPTIONS tweaks         -> SEGV
  * drop malloc=//:snmalloc + use //:snmalloc dep         -> SEGV
    (the dep still pulls libsnmalloc-new-override.a which
     statically installs operator new/delete overrides)
  * drop malloc=//:snmalloc + new hdrs-only dep           -> PASS

Add a new top-level cc_library target //:snmalloc_hdrs that exposes
snmalloc's headers without linking the allocator-override archive.
Switch //fuzzing:snmalloc_fuzzer to depend on it.  The test now:
  * uses system glibc malloc for process-wide alloc/free (ASAN shadow
    stays consistent -- no SETUP SEGV)
  * still drives snmalloc::memcpy<true> via the header inline
    template
  * still exercises the full snmalloc allocator state machine via
    snmalloc::get_scoped_allocator() + explicit scoped->alloc<>()
    / scoped destructor free in snmalloc_random_walk

Lost coverage: snmalloc-as-process-malloc fuzz pressure.  That
trade-off is documented in CU-86aj2tgnn together with the deeper
investigation path (snmalloc + ASAN shadow hook integration, or
GWP-ASan secondary-allocator wiring).

Local verification:
  bazel test -c opt --config=asan //fuzzing:snmalloc_fuzzer
  -> //fuzzing:snmalloc_fuzzer  PASSED in 3.3s
@jayakasadev
jayakasadev merged commit 4ef78f0 into main Jun 16, 2026
@jayakasadev
jayakasadev deleted the hotfix/ci-cleanup-pre-pr68 branch June 16, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant