Swaps oxlint's mimalloc global allocator for one that counts allocations and bytes, and prints the totals to stderr on exit. Used to measure allocation impact of linter changes.
Apply counting-allocator.diff (against apps/oxlint/src/lib.rs and apps/oxlint/src/main.rs).
cargo build --release -p oxlint --no-default-features--no-default-features is required: the allocator feature pulls in mimalloc, and two
#[global_allocator]s will not compile. This means the counting binary is not the same
build profile as a normal release binary — use it for allocation counts only, never for timing.
Counts go to stderr, diagnostics to stdout:
cd <corpus>
oxlint -c config.json --threads=1 --silent --format=default <target> 2>&1 >/dev/null \
| grep -o 'ALLOC_COUNT=[0-9]* ALLOC_BYTES=[0-9]*'--threads=1 for reproducibility. Counts are near-deterministic run to run; the byte total
carries a small constant offset that differs between binaries, so only compare differences
computed within one binary (see below).
The totals include parsing, semantic analysis and everything else. To attribute allocations to one rule or subsystem, run twice with the same binary and subtract:
graph_attributable = (rule_enabled_config) - (parse_only_config)
A parse-only config is the same file with every category off and no rules:
{
"plugins": [],
"categories": { "correctness": "off", "suspicious": "off", "pedantic": "off",
"perf": "off", "style": "off", "restriction": "off", "nursery": "off" },
"rules": {}
}Subtracting within a single binary cancels the constant startup offset. Do not subtract a parse-only baseline taken from a different binary.
- Everything is counted, including reallocs and zeroed allocations.
GlobalAlloc's defaultreallocandalloc_zeroedboth callself.alloc, and this impl overrides neither, so every allocation funnels through the counter. - Absolute totals over-report a production run. The same fact means a
reallocthat a real allocator would satisfy by growing in place instead becomes alloc + copy + dealloc here. Both sides of a comparison carry the same bias, so differences are sound; the absolute numbers are an upper bound, not what a mimalloc build actually does. ALLOC_BYTESis requested bytes (layout.size()), not what the allocator reserved.- Allocations are counted before the underlying
allocis attempted, so a failed allocation still increments. Irrelevant in practice — OOM ends the run anyway. --lspreturns early inmainbefore the report, so language-server runs print nothing.- Allocations after the report (shutdown, final drops) are not counted.
- Peak RSS is a separate question and this does not answer it.
/usr/bin/time -lis blocked in some sandboxes;os.wait4()in Python givesru_maxrssper child with no rebuild.