tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 72aeedf32d5a4f0e43a648aa5143de20e176d07f
parent a8ba6b96ffc435fc9020023b55a45c69834cad92
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date:   Tue, 21 Oct 2025 23:59:08 +0000

Bug 1959977 - Add tests to make sure we get sourceIndexes correctly r=mstange,profiler-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D259261

Diffstat:
Mtools/profiler/tests/browser/browser.toml | 3+++
Atools/profiler/tests/browser/browser_test_feature_js_sourceindex.js | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 213 insertions(+), 0 deletions(-)

diff --git a/tools/profiler/tests/browser/browser.toml b/tools/profiler/tests/browser/browser.toml @@ -8,6 +8,9 @@ skip-if = ["tsan"] # Bug 1804081 - TSan times out on pretty much all of these te ["browser_test_feature_ipcmessages.js"] support-files = ["simple.html"] +["browser_test_feature_js_sourceindex.js"] +support-files = ["simple.html", "tracing.html"] + ["browser_test_feature_jsallocations.js"] support-files = ["do_work_500ms.html"] diff --git a/tools/profiler/tests/browser/browser_test_feature_js_sourceindex.js b/tools/profiler/tests/browser/browser_test_feature_js_sourceindex.js @@ -0,0 +1,210 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test that sourceIndexes are collected properly in profiles. + */ +add_task(async function test_profile_js_sources() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "simple.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + // Start profiling to capture JS sources + await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] }); + + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + // Execute some JavaScript that will create frames with sourceIndexes + await SpecialPowers.spawn(contentBrowser, [], () => { + content.window.eval(` + function testFunction() { + // Simple function to generate JIT frames with 1ms busy loop + const start = performance.now(); + let result = 0; + while (performance.now() - start < 1) { + for (let i = 0; i < 1000; i++) { + result += Math.random(); + } + } + return result; + } + + // Call the function multiple times to trigger JIT compilation + for (let i = 0; i < 100; i++) { + testFunction(); + } + `); + }); + + const { contentThread } = + await waitSamplingAndStopProfilerAndGetThreads(contentPid); + + // Check that we have frames with sourceIndexes in location strings + const { frameTable } = contentThread; + const FRAME_LOCATION_SLOT = frameTable.schema.location; + + // Find frames that have sourceIndexes in their location strings + const framesWithSourceIndexes = []; + for (const frame of frameTable.data) { + const location = contentThread.stringTable[frame[FRAME_LOCATION_SLOT]]; + if (location && location.match(/\[\d+\]$/)) { + framesWithSourceIndexes.push({ frame, location }); + } + } + + Assert.greater( + framesWithSourceIndexes.length, + 0, + "Found frames with sourceIndexes in location strings" + ); + + // Verify that sourceIndexes are valid numbers + for (const { location } of framesWithSourceIndexes) { + const sourceIndexMatch = location.match(/\[(\d+)\]$/); + const sourceIndex = parseInt(sourceIndexMatch[1]); + Assert.greaterOrEqual( + sourceIndex, + 0, + `SourceIndex ${sourceIndex} is a valid number` + ); + } + + info(`Found ${framesWithSourceIndexes.length} frames with sourceIndexes`); + + // Check if testFunction frames have sourceIndexes + let testFunctionFrames = 0; + + for (const frame of frameTable.data) { + const location = contentThread.stringTable[frame[FRAME_LOCATION_SLOT]]; + if (location && location.includes("testFunction")) { + testFunctionFrames++; + if (!location.match(/\[\d+\]$/)) { + Assert.ok(false, `Failed to find sourceIndex for ${location}`); + } + } + } + + Assert.greater( + testFunctionFrames, + 0, + "At least some testFunction frames should have sourceIndexes" + ); + }); +}); + +/** + * Test that JS tracer frames include sourceIndexes when tracing is enabled. + */ +add_task(async function test_profile_js_sources_with_tracing() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "tracing.html"; + await BrowserTestUtils.withNewTab("about:blank", async contentBrowser => { + // Start profiling with tracing to capture JS tracer frames + await ProfilerTestUtils.startProfiler({ + features: ["tracing", "jssources"], + }); + + await BrowserTestUtils.startLoadingURIString(contentBrowser, url); + await BrowserTestUtils.browserLoaded(contentBrowser, false, url); + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { contentThread } = await stopProfilerNowAndGetThreads(contentPid); + + // Check that tracer frames have sourceIndexes in location strings + const { frameTable } = contentThread; + const FRAME_LOCATION_SLOT = frameTable.schema.location; + + // Look for our test functions from tracing.html with sourceIndexes + const tracingFramesWithSourceIndexes = []; + for (const frame of frameTable.data) { + const location = contentThread.stringTable[frame[FRAME_LOCATION_SLOT]]; + if ( + location && + location.includes("tracing.html") && + location.match(/\[\d+\]$/) + ) { + tracingFramesWithSourceIndexes.push({ frame, location }); + } + } + + dump(tracingFramesWithSourceIndexes.map(a => a.location)); + + Assert.greater( + tracingFramesWithSourceIndexes.length, + 0, + "Found tracing frames with sourceIndexes" + ); + + info( + `Found ${tracingFramesWithSourceIndexes.length} tracing frames with sourceIndexes` + ); + }); +}); + +/** + * Test that sourceIndexes work with eval. + */ +add_task(async function test_profile_js_sources_location_format() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "simple.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] }); + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + // Execute JavaScript with eval + await SpecialPowers.spawn(contentBrowser, [], () => { + content.window.eval(` + function sourceIndexTest() { + return 42; + } + sourceIndexTest(); + `); + }); + + const { contentThread } = + await waitSamplingAndStopProfilerAndGetThreads(contentPid); + + const { frameTable } = contentThread; + const FRAME_LOCATION_SLOT = frameTable.schema.location; + + // Find frames with sourceIndex in their location strings + const framesWithSourceIndexes = []; + for (const frame of frameTable.data) { + const location = contentThread.stringTable[frame[FRAME_LOCATION_SLOT]]; + if (location && location.match(/\[\d+\]$/)) { + framesWithSourceIndexes.push({ frame, location }); + info(`Found sourceIndex in location: ${location}`); + } + } + + Assert.greater( + framesWithSourceIndexes.length, + 0, + "Found at least one frame location that includes sourceIndex in brackets" + ); + }); +});