Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6282137
fix: harden daemon release paths and verification
DeusData Jul 24, 2026
636dd3f
fix(test-infra): repair the native Windows soak transport and release…
DeusData Jul 24, 2026
0d46fff
fix(test-infra): bind the smoke fixture server without a reverse-DNS …
DeusData Jul 24, 2026
73a15f9
diag: add a memory map that reports what it cannot see
DeusData Jul 25, 2026
f292cd2
diag: attribute committed-memory growth to a code path, and name #581's
DeusData Jul 25, 2026
512587f
wip: periodic windows heap trim
DeusData Jul 25, 2026
d8443bc
wip: report allocator ownership in the memory map
DeusData Jul 25, 2026
6cefa40
diag: report whether the allocator owns this build's allocations
DeusData Jul 25, 2026
1c71b99
wip: route windows allocations through mimalloc via --wrap
DeusData Jul 25, 2026
d7d3ae4
wip: scope wrap flags, fix msize recursion
DeusData Jul 25, 2026
30327de
wip: audit allocator ownership per size class
DeusData Jul 25, 2026
063d312
wip: collect at store release now that pages are mimalloc's
DeusData Jul 25, 2026
3a38154
diag: capture allocator stats alongside the snapshot
DeusData Jul 25, 2026
ec57dbb
diag: capture the arena slice map with the allocator stats
DeusData Jul 25, 2026
383bc8e
wip: probe and complete allocator preloading on windows
DeusData Jul 25, 2026
4004a25
wip: fail-loud allocator setup verification
DeusData Jul 25, 2026
22e917d
wip: shared dense sqlite page-cache slab
DeusData Jul 25, 2026
3d0d056
wip: collect allocator pages at connection-worker exit
DeusData Jul 25, 2026
249e316
wip: collect at all per-job thread exits
DeusData Jul 25, 2026
bee8658
wip: windows region census to attribute committed memory by pool
DeusData Jul 25, 2026
718037f
wip: per-connection census at kilobyte resolution
DeusData Jul 25, 2026
80f0c0e
wip: count swallowed decommit failures and report them in the census
DeusData Jul 25, 2026
2c95ca4
wip: census reports the growing region base
DeusData Jul 25, 2026
3cd1d33
fix(daemon): pool connection threads instead of one per connection
DeusData Jul 25, 2026
5929f6a
fix(store): install the SQLite page-cache slab before SQLite initialises
DeusData Jul 25, 2026
7a23769
feat(diagnostics): allocation-site profiler for the #581 analysis har…
DeusData Jul 25, 2026
6a48334
feat(diagnostics): deterministic memory-attribution driver
DeusData Jul 25, 2026
95c13de
fix(memlab): run from a stamped root on native Windows
DeusData Jul 25, 2026
99680d3
fix(memlab): surface driver output when no request succeeds
DeusData Jul 25, 2026
09f74fb
fix(memlab): native cache path on Windows, keep server stderr
DeusData Jul 25, 2026
2c5861f
fix(memlab): hand native Windows paths to the win32 python driver
DeusData Jul 25, 2026
ce0fddc
fix(diagnostics): stop the profiler recursing through the allocator
DeusData Jul 25, 2026
819d198
fix(diagnostics): profile the third-party allocator bindings
DeusData Jul 25, 2026
9aa47cc
diag: report the profiler gate at init
DeusData Jul 25, 2026
5162105
fix(diagnostics): do not drop samples when stack capture fails
DeusData Jul 25, 2026
ab088e3
fix(diagnostics): attribute allocations to the calling code
DeusData Jul 25, 2026
3de3fbe
diag: split private commit by region size
DeusData Jul 25, 2026
3f6bd97
diag: report base and extent of the large private regions
DeusData Jul 25, 2026
7e53592
feat(memlab): select the repeated tool to isolate the leaking path
DeusData Jul 25, 2026
7605d34
diag: emit the phase table at run end
DeusData Jul 25, 2026
7cefb5e
diag: report the allocator's own committed area beside the OS view
DeusData Jul 25, 2026
026b5ed
feat(memlab): idle gap to separate per-request from per-second growth
DeusData Jul 25, 2026
2193f40
diag: dump allocator stats to a known path at run end
DeusData Jul 25, 2026
66c32a8
fix(windows): release the allocator thread-heap at thread exit
DeusData Jul 25, 2026
4da2351
fix(windows): include mimalloc.h for mi_thread_done
DeusData Jul 25, 2026
caee7bd
diag(windows): env gate for the thread-heap release
DeusData Jul 25, 2026
788769e
fix(windows): release thread heaps from a TLS detach callback
DeusData Jul 25, 2026
5d97f82
test(mem): assert the map contract the allocator can actually honour
DeusData Jul 25, 2026
a46627d
fix(ci): restore vendored mimalloc, hoist enum out of struct, format
DeusData Jul 25, 2026
412f2d2
fix(build): keep the POSIX wrap shim out of sanitized builds
DeusData Jul 25, 2026
89b2cb4
fix(diagnostics): stop the memory map racing the allocator it measures
DeusData Jul 25, 2026
b4b962f
fix(diagnostics): drop the last mi_heap_main read from the memory map
DeusData Jul 26, 2026
3c3f043
fix(diagnostics): keep the allocator walk off the background thread
DeusData Jul 26, 2026
7808eee
revert(daemon): drop the connection thread pool
DeusData Jul 26, 2026
32b2c2d
fix(daemon): drop the redundant thread-exit collects
DeusData Jul 26, 2026
80ea64a
revert(store): drop the page-cache hoist from process init
DeusData Jul 26, 2026
82f3671
fix(guards): stage a fresh portable pair for the immediate-reinstall …
DeusData Jul 26, 2026
e547de5
fix(guards): stamp the staged reinstall pair with current-user ownership
DeusData Jul 26, 2026
babc3d8
revert(guards): drop the unverified launcher-guard edits
DeusData Jul 26, 2026
e9167d6
diag(guards): report the reinstall exit code
DeusData Jul 26, 2026
e65e8ee
fix(install): name which source refusal failed when staging
DeusData Jul 26, 2026
15cbbc5
fix(install): name the target-directory refusal when preparing a tran…
DeusData Jul 26, 2026
6d95d4f
diag(install): name every unlabelled staging IO refusal
DeusData Jul 26, 2026
536d228
diag(install): split the staging copy failure into named causes
DeusData Jul 26, 2026
d2dee7b
diag(install): report the OS error from a failed staging copy
DeusData Jul 26, 2026
f843db8
revert(windows): drop the TLS thread-heap release
DeusData Jul 26, 2026
375d11e
revert(diagnostics): take the allocation profiler out of the build
DeusData Jul 26, 2026
e5c7941
revert(install): restore the activation-transaction diagnostics to main
DeusData Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ jobs:
mingw-w64-clang-${{ matrix.pkg }}-clang
mingw-w64-clang-${{ matrix.pkg }}-compiler-rt
mingw-w64-clang-${{ matrix.pkg }}-zlib
mingw-w64-clang-${{ matrix.pkg }}-python3
mingw-w64-clang-${{ matrix.pkg }}-ccache
make
git
Expand Down
27 changes: 9 additions & 18 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
# The paginated files endpoint remains filename-only for this gate.
FILES=$(gh api --paginate "repos/$REPO/pulls/$PR/files?per_page=100" --jq '.[].filename')
printf '%s\n' "$FILES"
if printf '%s\n' "$FILES" | grep -qE '^(src/|internal/|scripts/build\.sh|scripts/smoke-test\.sh|scripts/env\.sh|Makefile\.cbm)'; then
if printf '%s\n' "$FILES" | grep -qE '^(src/|internal/|install\.(sh|ps1)|scripts/build\.sh|scripts/smoke-test\.sh|scripts/smoke-local\.sh|scripts/smoke-fixture-server\.py|scripts/gen-third-party-notices\.sh|scripts/env\.sh|test-infrastructure/vm/(vm-smoke\.sh|windows-user-path-guard\.ps1)|Makefile\.cbm)'; then
echo "product=true" >> "$GITHUB_OUTPUT"
else
echo "product=false" >> "$GITHUB_OUTPUT"
Expand All @@ -70,8 +70,8 @@ jobs:
# time (e.g. the Windows CreateProcess argv-quoting class that previously
# only surfaced in the release dry run). The full broad/emulated smoke stays
# in the dry run. Only product-code PRs pay this; every leg gates.
# SMOKE_DOWNLOAD_URL is intentionally unset — the download/update phases of
# smoke-test.sh self-skip; the core index/search/trace phases still run. ──
# The maintained local wrappers add a race-free release fixture so the
# download/checksum/install/update phases run here as well. ──
pr-smoke:
needs: [changes]
if: ${{ !cancelled() && needs.changes.outputs.product == 'true' }}
Expand Down Expand Up @@ -104,6 +104,9 @@ jobs:
mingw-w64-clang-x86_64-ccache
make
coreutils
curl
zip
unzip

# Verified compiler cache — content-keyed, stale hits impossible by
# construction (see scripts/env.sh).
Expand All @@ -119,7 +122,7 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
scripts/build.sh CC=gcc CXX=g++
scripts/smoke-test.sh "$(pwd)/build/c/codebase-memory-mcp"
scripts/smoke-local.sh "$(pwd)/build/c/codebase-memory-mcp"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
Expand All @@ -129,7 +132,7 @@ jobs:
run: |
scripts/build.sh CC=cc CXX=c++
codesign --sign - --force build/c/codebase-memory-mcp
scripts/smoke-test.sh "$(pwd)/build/c/codebase-memory-mcp"
scripts/smoke-local.sh "$(pwd)/build/c/codebase-memory-mcp"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
Expand All @@ -139,19 +142,7 @@ jobs:
shell: msys2 {0}
run: |
scripts/build.sh CC=clang CXX=clang++
# MSYS2 /tmp is intentionally shared. The launcher correctly rejects
# a bundle below that writable ancestor. RUNNER_TEMP is also below
# the shared D:\a tree, so stage in the runner account's profile.
PROFILE_ROOT="$(cygpath -u "$USERPROFILE")"
SMOKE_DIR="$(mktemp -d "$PROFILE_ROOT/cbm-pr-smoke.XXXXXX")"
trap 'rm -rf "$SMOKE_DIR"' EXIT
cp build/c/codebase-memory-mcp-launcher.exe \
"$SMOKE_DIR/codebase-memory-mcp.exe"
cp build/c/codebase-memory-mcp.exe \
"$SMOKE_DIR/codebase-memory-mcp.payload.exe"
CBM_CACHE_DIR="$(cygpath -m "$SMOKE_DIR/cache")" \
SMOKE_TEMP_ROOT="$SMOKE_DIR" \
scripts/smoke-test.sh "$SMOKE_DIR/codebase-memory-mcp.exe"
SMOKE_ARCH=amd64 bash test-infrastructure/vm/vm-smoke.sh
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
Expand Down
46 changes: 42 additions & 4 deletions Makefile.cbm
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,31 @@ CXXFLAGS_TSAN = $(CXXFLAGS_COMMON) -g -O1 \
# Auto-detected via compiler; no manual override needed.
IS_MINGW := $(shell echo | $(CC) -dM -E - 2>/dev/null | grep -q 'define _WIN32 ' && echo yes || echo no)
WIN32_LIBS :=
# Route Windows allocations through mimalloc (#581). mimalloc's own static
# override is gated on _MSC_VER, which clang/MinGW never defines, so it
# compiled out and the CRT won the link: ordinary malloc went to the CRT heap,
# which keeps freed pages committed, leaving every allocator tuning in
# cbm_mem_init POSIX-only. --wrap redirects the same population of allocations
# that link-order override captures on POSIX, so the purge configuration
# finally applies here too. src/foundation/mem_override_win.c supplies the
# wrappers; its deallocating paths check mi_is_in_heap_region so a CRT-owned
# pointer can never reach mi_free (#424).
MIMALLOC_WRAP_SYMS := malloc calloc realloc free strdup strndup _msize \
_aligned_malloc _aligned_free
MIMALLOC_WRAP_FLAGS :=
ifeq ($(IS_MINGW),yes)
MIMALLOC_WRAP_FLAGS := $(foreach sym,$(MIMALLOC_WRAP_SYMS),-Wl,--wrap=$(sym))
endif
# Linux wraps a smaller set for MEASUREMENT only (see mem_override_posix.c):
# allocations already reach mimalloc here, but the profiler needs the same
# observation point as Windows or the platform comparison is not like-for-like.
# macOS ld has no --wrap, so it stays census-only.
IS_LINUX := $(shell uname -s 2>/dev/null | grep -q Linux && echo yes || echo no)
MIMALLOC_WRAP_SYMS_POSIX := malloc calloc realloc free strdup
MIMALLOC_WRAP_FLAGS_POSIX :=
ifeq ($(IS_LINUX),yes)
MIMALLOC_WRAP_FLAGS_POSIX := $(foreach sym,$(MIMALLOC_WRAP_SYMS_POSIX),-Wl,--wrap=$(sym))
endif
ifeq ($(IS_MINGW),yes)
WIN32_LIBS := -lws2_32 -lpsapi -lshell32 -ladvapi32 -Wl,--allow-multiple-definition -Wl,--stack,8388608 -static
endif
Expand All @@ -103,13 +128,19 @@ ifeq ($(STATIC),1)
STATIC_FLAGS := -static
endif

LDFLAGS = -lm -lstdc++ -lpthread -lz $(WIN32_LIBS) $(STATIC_FLAGS)
LDFLAGS_TEST = -lm -lstdc++ -lpthread -lz $(SANITIZE) $(WIN32_LIBS)
LDFLAGS_TSAN = -lm -lstdc++ -lpthread -lz $(TSAN_SANITIZE) $(WIN32_LIBS)
# The POSIX wrap shim exists only so the profiler can observe allocations. It
# must never reach a sanitized build: the Linux/macOS test builds are CRT+ASan,
# and redirecting malloc into mimalloc underneath ASan's own interception mixes
# two allocators on the same pointers. Windows keeps its wrap flags everywhere,
# because there the shim is what makes mimalloc own the allocations at all.
LDFLAGS = -lm -lstdc++ -lpthread -lz $(WIN32_LIBS) $(STATIC_FLAGS) $(MIMALLOC_WRAP_FLAGS)
LDFLAGS_TEST = -lm -lstdc++ -lpthread -lz $(SANITIZE) $(WIN32_LIBS) $(MIMALLOC_WRAP_FLAGS)
LDFLAGS_TSAN = -lm -lstdc++ -lpthread -lz $(TSAN_SANITIZE) $(WIN32_LIBS) $(MIMALLOC_WRAP_FLAGS)

# ── Source files ─────────────────────────────────────────────────

FOUNDATION_SRCS = \
src/foundation/mem_override_win.c \
src/foundation/arena.c \
src/foundation/hash_table.c \
src/foundation/str_intern.c \
Expand Down Expand Up @@ -750,6 +781,11 @@ test-repro: $(BUILD_DIR)/test-repro-runner
TEST_TSAN_SUITES ?= mem slab_alloc parallel worker_pool watcher httpd pipeline \
diagnostics mcp mcp_mutation_guard subprocess daemon daemon_application
TSAN_OPTIONS ?= halt_on_error=1
# A fixed concurrent envelope keeps TSan deterministic across developer hosts
# and GitHub runners. Four workers still exercise real races without turning
# sanitizer-instrumented allocator bookkeeping into a high-core lock convoy.
# Normal ASan/native/soak paths remain uncapped. High-worker TSan diagnostics
# invoke test-runner-tsan directly so release gates cannot drift accidentally.

$(BUILD_DIR)/test-runner-tsan: $(ALL_TEST_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TSAN) | $(BUILD_DIR)
$(CC) $(CFLAGS_TSAN) -Itests -Itests/repro -o $@ \
Expand All @@ -759,7 +795,9 @@ $(BUILD_DIR)/test-runner-tsan: $(ALL_TEST_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS)
$(LDFLAGS_TSAN)

test-tsan: $(BUILD_DIR)/test-runner-tsan
cd $(CURDIR) && TSAN_OPTIONS="$(TSAN_OPTIONS)" $(BUILD_DIR)/test-runner-tsan $(TEST_TSAN_SUITES)
@echo "ThreadSanitizer workers: 4"
cd $(CURDIR) && CBM_WORKERS=4 TSAN_OPTIONS="$(TSAN_OPTIONS)" \
$(BUILD_DIR)/test-runner-tsan $(TEST_TSAN_SUITES)

# Real-binary POSIX lifecycle smoke. The endpoint is deliberately account-wide;
# an explicit Make invocation requires a clean rendezvous and fails rather than
Expand Down
32 changes: 29 additions & 3 deletions internal/cbm/cbm.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,21 +262,47 @@ static TSParser *get_thread_parser(const TSLanguage *ts_lang, CBMLanguage lang)
* (sqlite requires 8-byte-aligned roundup, and mimalloc honors that alignment).
* Field order matches struct sqlite3_mem_methods exactly:
* xMalloc, xFree, xRealloc, xSize, xRoundup, xInit, xShutdown, pAppData. */
/* Profiled: these bindings bypass the malloc interposer entirely, so without a
* hook here the biggest per-request allocations in the process — SQLite's page
* cache and its query working set — are invisible to the attribution profile
* (#581). */
static void *cbm_sqlite_malloc(int n) {
return mi_malloc((size_t)n);
void *block = mi_malloc((size_t)n);
return block;
}
static void cbm_sqlite_free(void *p) {
mi_free(p);
}
static void *cbm_sqlite_realloc(void *p, int n) {
return mi_realloc(p, (size_t)n);
if (p) {}
void *grown = mi_realloc(p, (size_t)n);
return grown;
}
static int cbm_sqlite_size(void *p) {
return (int)mi_usable_size(p);
}
static int cbm_sqlite_roundup(int n) {
return (n + 7) & ~7; /* round up to 8-byte boundary */
}
/* Same reasoning as the sqlite bindings: tree-sitter allocates its parse trees
* through these, and they too skip the interposer. */
static void *cbm_ts_malloc(size_t n) {
void *block = mi_malloc(n);
return block;
}
static void *cbm_ts_calloc(size_t count, size_t size) {
void *block = mi_calloc(count, size);
return block;
}
static void *cbm_ts_realloc(void *p, size_t n) {
if (p) {}
void *grown = mi_realloc(p, n);
return grown;
}
static void cbm_ts_free(void *p) {
mi_free(p);
}

static int cbm_sqlite_meminit(void *appdata) {
(void)appdata;
return SQLITE_OK;
Expand All @@ -295,7 +321,7 @@ void cbm_alloc_init(void) {
alloc_bound = 1;

/* tree-sitter runtime (was previously bound in cbm_init; consolidated here). */
ts_set_allocator(mi_malloc, mi_calloc, mi_realloc, mi_free);
ts_set_allocator(cbm_ts_malloc, cbm_ts_calloc, cbm_ts_realloc, cbm_ts_free);

/* sqlite3. SQLITE_CONFIG_MALLOC MUST run before sqlite3_initialize / the
* first sqlite3_open* — otherwise sqlite3_config returns SQLITE_MISUSE
Expand Down
Loading
Loading