From e0b5e5ed476d1edecf46618eff2a062848d5c7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Tue, 20 Oct 2026 13:38:30 +0200 Subject: [PATCH] Look for a source file's address proof in all of its frames findAddressProofForFile took the first func with the file as its source, then that func's first frame, and gave up if that frame had no address. A file can still have other frames with addresses, which the symbolication API would accept just as well. Go through the frames instead and take the first one that has an address and a func with the file as its source. Files that were fetchable but failed now load in the source view and in profiler-cli. The helpers will also be used to find the addresses for all files at once when sharing source code. --- src/profile-logic/profile-data.ts | 63 ++++++++++++++++-------------- src/test/unit/profile-data.test.ts | 19 +++++++++ 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/profile-logic/profile-data.ts b/src/profile-logic/profile-data.ts index 002ac3514d..baed4507e9 100644 --- a/src/profile-logic/profile-data.ts +++ b/src/profile-logic/profile-data.ts @@ -4660,6 +4660,35 @@ export function nudgeReturnAddresses(profile: Profile): Profile { return newProfile; } +/** + * The source of a frame with an address, which is what can prove to the + * symbolication API's /source/v1 that the source belongs to the library. Null + * when the frame has no address or its func no source. + */ +function _getAddressProvenSource( + frameTable: RawFrameTable, + funcTable: RawFuncTable, + frameIndex: IndexIntoFrameTable +): IndexIntoSourceTable | null { + if ((frameTable.flags[frameIndex] & FrameFlag.HasAddress) === 0) { + return null; + } + const funcIndex = frameTable.func[frameIndex]; + if ((funcTable.flags[funcIndex] & FuncFlag.HasSource) === 0) { + return null; + } + return funcTable.source[funcIndex]; +} + +function _getAddressProofForFrame( + profile: Profile, + frameIndex: IndexIntoFrameTable +): AddressProof { + const { frameTable } = profile.shared; + const { debugName, breakpadId } = profile.libs[frameTable.lib[frameIndex]]; + return { debugName, breakpadId, address: frameTable.address[frameIndex] }; +} + /** * Find the address and library (debugName, breakpadId) for any frame which * was symbolicated to the given filename. @@ -4668,41 +4697,15 @@ export function findAddressProofForFile( profile: Profile, sourceIndex: IndexIntoSourceTable ): AddressProof | null { - const { libs } = profile; const { frameTable, funcTable } = profile.shared; - // Scan for the func manually rather than using `funcTable.source.indexOf`: - // the `source` column only carries a meaningful value for funcs which have - // the `HasSource` flag set, and holds an arbitrary value (usually zero) for - // all other funcs. An `indexOf` would happily match one of those. - let func = -1; - for (let i = 0; i < funcTable.length; i++) { + for (let frameIndex = 0; frameIndex < frameTable.length; frameIndex++) { if ( - (funcTable.flags[i] & FuncFlag.HasSource) !== 0 && - funcTable.source[i] === sourceIndex + _getAddressProvenSource(frameTable, funcTable, frameIndex) === sourceIndex ) { - func = i; - break; + return _getAddressProofForFrame(profile, frameIndex); } } - if (func === -1) { - return null; - } - const frame = frameTable.func.indexOf(func); - if (frame === -1) { - return null; - } - if ((frameTable.flags[frame] & FrameFlag.HasAddress) === 0) { - return null; - } - const address = frameTable.address[frame]; - const libIndex = frameTable.lib[frame]; - const lib = libs[libIndex]; - const { debugName, breakpadId } = lib; - return { - debugName, - breakpadId, - address, - }; + return null; } /** diff --git a/src/test/unit/profile-data.test.ts b/src/test/unit/profile-data.test.ts index 0e83649442..d8256f3af6 100644 --- a/src/test/unit/profile-data.test.ts +++ b/src/test/unit/profile-data.test.ts @@ -1546,6 +1546,25 @@ describe('findAddressProofForFile', function () { ); expect(missingAddressProof).toBeNull(); }); + + it('finds an address for a file whose first frame has none', function () { + const { profile } = getProfileFromTextSamples(` + A[lib:XUL][file:a.cpp] + B[lib:XUL][file:a.cpp][address:20] + `); + const stringTable = StringTable.withBackingArray( + profile.shared.stringArray + ); + const sourceIndex = profile.shared.sources.filename.indexOf( + stringTable.indexForString('a.cpp') + ); + + expect(findAddressProofForFile(profile, sourceIndex)).toEqual({ + debugName: 'XUL', + breakpadId: 'SOMETHING_FAKE', + address: 0x20, + }); + }); }); describe('calculateFunctionSizeLowerBound', function () {