Repository navigation
Conversation
test/aot/no-leak-records-at-exit.test.ts gave every leak record of a `Malloc=1` run the name `bmalloc::SystemHeap::malloc`, and every record of Rust the name `alloc`. An executable passes when `bun run` has each of its names, so a record that only the executable has passed. A record now has the name of the first called function that is neither an allocator nor a container. The frames of inlined functions are left out. `bmalloc::`, `__rustc::`, a class `operator new` and a method of a trait `Allocator` are allocators. A method of Rust is a container frame when the language, hashbrown or smallvec has its code. A `requires` clause, a qualifier and the location are not part of a name. Tests of written text hold the parser against records that a symbolizer wrote. They need no compiler and run on every Bun. This is the change of #47443 on top of main after #47942.
|
No actionable comments were generated in the recent review. WalkthroughThe leak-report test parser now selects allocation sites from C++ and Rust stack frames, classifies allocator and container frames, and groups records by the selected site. New tests cover frame parsing and record grouping. ChangesLeak record attribution
Possibly related PRs
Suggested reviewers: Merge Risk:
|
|
Updated 1:34 PM PT - Nov 10th, 2026
✅ @robobun, your commit 4429669b5450d703936230b71f169aa592824e59 passed in 🧪 To try this PR locally: bunx bun-pr 48461That installs a local version of the PR into your bun-48461 --bun |
|
Status: ready for review. This is the change of #47443 on top of main after #47942. A maintainer asked there for a new pull request if the problem still shows, and it does. Reproduced on a release ASAN build of main (89452d2, after #48436). I gave CI for 512f580 (build 139738) ended with 187 of 188 jobs passed, and the changed file passed on every lane. The red job was |
|
A note on the red check, as of 2b25cf9. It runs That command does not pass on a debug build of main (98cda0d) without this change either. The five cases that build a program fail there. Some pass the time limit of 270 s. The build of On a release ASAN build, which the test lanes of CI use, the file ended with |
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Beyond the inline findings, I also checked the semantic change in getLeakRecords (same-function records are now concatenated instead of last-write-wins): the two ASAN-gated callers only use the Map keys via plainRecords.has(...) and print the values on failure, so concatenation does not change what any assertion compares. The parser unit tests were not run in this review.
Extended reasoning...
The change is confined to test/aot/no-leak-records-at-exit.test.ts and rewrites the hand-written LSan report parsers (getFunctionName, getCrateOfMethod, isAllocator, frameLocation, getCalledFunctions, getLeakRecords) plus adds fixture-based unit tests for them; it touches no security-sensitive surface. The inline findings already call for a human look at the heuristics, so this note only records the one adjacent concern that reading settled.
- Take `test` for the tests of written text from `./harness/default-timeout`. The guard in `the-default-time-limit-never-lowers-what-the-runner-gave.test.ts` lists a file that imports `test` from `bun:test` and has a time limit. - A record whose stack ends in allocators and containers has the name of the last function on it. Its name came from the text of the record, which is different with and without debug information. - A record that names no function keeps its text for a name, so that it is like no other record. - An operator that is written with `<` or `>` keeps its signs. `operator>` had a piece of its arguments for a name. - An operator for a literal keeps its whole name, so those of WTF are containers. - A function of Rust with generic arguments has no `::` at its end. - `bun_alloc` and the `OPENSSL_` functions are allocators: BoringSSL asks Bun for its memory.
|
Pushed 512f580. It answers the four review comments on 2b25cf9, and one failure that this PR caused.
On a release ASAN build the file ends with |
There was a problem hiding this comment.
LGTM — the second push addresses everything I raised earlier, each with a fixture test, and I found nothing new.
What was reviewed:
- Traced
getFunctionNameby hand through all tennamesOfFunctionsfixtures (including theoperator<<<int>/operator<<int>backtracking and thecooperatorword-boundary case) andgetAllocatedBythrough both stacks that end in containers — each yields the expected key. - Checked the new
./harness/default-timeoutimport: it exists and wraps bun:test'stest, so.eachworks;.gitattributespins*.tsto LF, so the template-literal fixtures parse the same on Windows. - Not run here: the five ASAN-gated build-and-run cases and the probe described in the PR (no sanitizer build in this review); the pure-text unit tests were checked by reading only.
Extended reasoning...
The change is confined to one test file, test/aot/no-leak-records-at-exit.test.ts, rewriting the LeakSanitizer record parser (operator spellings, Rust crate resolution, allocator/container classification, location stripping, inlined-frame dropping) and adding pure-text unit tests that run on every build. It touches no runtime code and no security-sensitive surface. The four inline findings from my prior review (record-text fallback key, dangling ::, OPENSSL_/bun_alloc shims, operator > depth) are all fixed in commit 512f580 with a regression fixture for each, the bug hunt ran dry, and test/aot is not CODEOWNED, which decided approve.
…on-after-aot-rewrite
Problem
test/aot/no-leak-records-at-exit.test.tsnames a leak record by its first frame that is no allocator. An executable passes whenbun runhas each of its names.Malloc=1record has the namebmalloc::SystemHeap::malloc, becauseisAllocator()does not knowbmalloc::. Each Rust record has the namealloc, the last word of the inlinedstd::sys::alloc::unix::alloc.Fix
getCalledFunctions()leaves out inlined functions.bmalloc::,__rustc::,bun_alloc::,OPENSSL_*and a classoperator neware allocators. A Rust method is a container frame when the language,hashbrownorsmallvechas its code.requiresclause, qualifier or location, and an operator keeps its<or>. A stack that ends in containers gives its last function.test/aot/no-leak-records-at-exit.test.ts, 31 pass on a release ASAN build (89452d2). A probe with four executable-only records passes on main and fails here.Background
<Type as Trait>::method. The crate of the trait or of the type has its code.allocto the allocators. Any function of that name then hides a record.Notes
What main does (98cda0d, after #47942 and #48120, release ASAN build)
Names that
getLeakRecords()gives:workers that end,Malloc=1bmalloc::SystemHeap::mallocfor all 8 records of the executable and all 20 ofbun runWebCore::eventNames,WebCore::EventNames::EventNames,Zig::GlobalObject::setConsole.bun run: those three andJSC::Identifier::add,JSC::CommonIdentifiers::CommonIdentifiers,WebCore::BunBuiltinNames::BunBuiltinNamesworkers that serve their default export, noMallocalloc<bun_runtime::server::NewServer<false, true>>::initThe records and their counts are the same as before #47942.
The probe
In the test body, give
bun runthe programconsole.log("hello", 1 + 2)in place ofentry.ts, and run-t "workers that end".bun runthen has 2 records and the executable 8. Four of them are ofWebCore::eventNamesandWebCore::EventNames::EventNames, whichbun runof that program does not have.How it was run
This branch ends with
31 pass, 0 failin that first attempt setup of CI, and with$Bin place of$B-stripped. main ends with6 pass, 0 fail.After #48436 (89452d2)
The branch has main merged in (4429669). On a release ASAN build of that tree the results are the same. This branch ends with
31 pass, 0 failin both setups. main's file ends with6 pass, 0 fail. The probe passes with main's file and fails with this one, with the same four records.The second commit (512f580)
It answers the review of 2b25cf9. Each change has a record or a function of a real program in the tests of written text.
testAnywherenow comes from./harness/default-timeout.the-default-time-limit-never-lowers-what-the-runner-gave.test.tslists a file that importstestfrombun:testand has a time limit. That guard failed at 2b25cf9. It passes now: 11 pass on the release ASAN build.bun runofworkers that endwithMalloc=1: 724 bytes, 30 functions, all ofbmalloc,pas_andWTF. Its name was/root/.bun/build-cache/.../MakeString.h:156:19with debug information andWTF::makeAtomStringwithout. It isWTF::makeAtomStringfor both now.symbolize=0) keeps its text for a name, as before. It matches no record of another process, so the comparison fails and does not pass on nothing.operator>andoperator>=) hadconst&, eight hadapp::Version::operator. Now they have 29 names. In a second program a lambda inoperator>had the namelong.OPENSSL_*andbun_alloc. No record of the five programs has such a frame. A script that calls BoringSSL throughbun:ffileaves eleven records that have them. Release ASAN build: their names wereOPENSSL_malloc(7),OPENSSL_realloc(2),OPENSSL_callocandOPENSSL_memdup. They are nowBIO_new,BUF_MEM_new,BN_bin2bn,ASN1_STRING_new,ASN1_STRING_set,BUF_MEM_append,BUF_MEM_reserveandcrypto_buffer_new_with_pool.Over the 266,193 function names of the release ASAN build, against 2b25cf9:
::at their end.<or>, which had...::operator, and 4 literal operators of WTF, which had_s,_str,_spanand_span8.bun_alloc, 54OPENSSL_*and those 4 of WTF. None stops being one.The 80 records of the five programs (executable and
bun run, as built and withMalloc=1) have the same names as at 2b25cf9.69 changes to single clauses of the parser were tried, one at a time. The 25 tests of written text catch each of them.
On a debug build
workers that endfailed because the build ended with 1. This branch: the same five cases failed, four by the time limit andworkers that endby the build.bun-debug build --compile --aot entry.ts child.tsforworkers that endended with a SEGV under AddressSanitizer in 2 of 3 runs. The stack is an add to aWTF::HashTablefromJSC::AOT::Typed::LinkFlow::typeOfFunctions(AOTLinkFlow.h:316), on a helper offorEachIndexInParallel. The release ASAN build did not show it in any run of the file.Limits
bssl::New<T>is a called function. Six of the eleven BoringSSL records above then have the namebssl::New.sqlite3Malloc,ares_malloc),strdup, and Bun'sbun_collectionshave no entry.operator<whose first argument begins with(reads asoperator<<. The release ASAN build has none.From #47443
Allocator,->in a function pointer, a lambda of a constrained function. Each has a record or a method in the tests of written text.Not in this PR
bun runhas too. The test accepts a record thatbun runalso prints.scripts/runner.node.tsand the list of suppressions do not change.