commit 65007e8566b88457a96096561eb59bda4cec3c46 parent d43da37916a3a4a8f9fb540f31a736afae1d32b6 Author: iulian moraru <imoraru@mozilla.com> Date: Wed, 22 Oct 2025 01:03:52 +0300 Revert "Bug 1916785, Bug 1979114, Bug 1959977 - Add tests for the new webchannel request r=mstange,profiler-reviewers" for causing Spidermonkey build bustages due to assertions on Interpreter.cpp. This reverts commit 3ed520b4183281fd1e282e762ae6bb97779d3989. Revert "Bug 1916785 - Add a GET_JS_SOURCES WebChannel request for frontend to fetch sources r=mstange,profiler-reviewers" This reverts commit db9f6ed845665dfb7ccd59be4c5e2d668350ada7. Revert "Bug 1916785 - Register the JS sources for the browser tab r=mstange,profiler-reviewers" This reverts commit fe9016075612070249a70edd5f9bb06570318c33. Revert "Bug 1979114 - Add some tests for jsSource retrieval r=mstange,profiler-reviewers" This reverts commit e088da1ad9e21512a456549e9de205c666e092df. Revert "Bug 1979114 - Fetch retrievable sources while converting sources to JS objects inside the parent process r=mstange,profiler-reviewers,iain" This reverts commit 0f8c64c34a94618e416cd8589fc791604fef89bb. Revert "Bug 1979114 - Convert the JS sources into JS objects in additionalInfo r=mstange,profiler-reviewers,iain" This reverts commit ae7ec10a5d3a1cd52d0fc6051d92573d1749e6ec. Revert "Bug 1959977 - Add tests to make sure we get sourceIndexes correctly r=mstange,profiler-reviewers" This reverts commit 805b956f6fdc25da671e567a178cf405313ee46d. Revert "Bug 1959977 - Bump the gecko profiler format version after adding sourceIndexes r=mstange,profiler-reviewers" This reverts commit 3df63cfa1128ef72058529fef5e5744564f58eb5. Revert "Bug 1959977 - Collect sourceId of JIT frames r=iain,mstange,profiler-reviewers" This reverts commit 0847f7cb202d2b631fc926d9251b649ec9a3b03b. Revert "Bug 1959977 - Collect sourceId of JS interpreter and JS tracer frames r=iain,mstange,profiler-reviewers" This reverts commit 0b34e3e412936107678e91977f1a953d75db1256. Revert "Bug 1979114 - Stream SourceTable for each process r=mstange,profiler-reviewers" This reverts commit 0d00dcebfc31aec0a6b157700029528ba2806b97. Revert "Bug 1979114 - Collect the JS sources during profile serialization r=mstange,profiler-reviewers" This reverts commit 821c617b840ccd2fee25fef090fcf6045e8e1f0f. Revert "Bug 1979114 - Add a new JS sources feature to the profiler r=mstange,profiler-reviewers" This reverts commit 9ffc93a74c33a792e9ed5574ad6e8c3a1418caf1. Revert "Bug 1979114 - Include JS sources in the profile additional information r=mstange,profiler-reviewers" This reverts commit 69d9edab72b850256b85d3e51f1d15a2b6559407. Revert "Bug 1979114 - Add IPC serialization and deserialization methods for the JS sources r=mstange,profiler-reviewers" This reverts commit 74799a8be169c8745dc7189b74f5d18643fbf873. Revert "Bug 1979114 - Add a JS API to get JS script sources r=iain,arai" This reverts commit 4157a13ac228a5fcd0490475edb35c71c835c327. Revert "Bug 1979114 - Make ProfileGenerationAdditionalInformation MoveOnly and remove the copy constructors of additional information structs r=mstange,profiler-reviewers" This reverts commit 8cfac1e837d98af7dd1ea4f3c7a7379243d0834f. Revert "Bug 1979114 - Propagate the profile generation information for child processes instead of duplicating them r=mstange,profiler-reviewers" This reverts commit be499e88c865f308755b115ee8265126a7866b22. Revert "Bug 1959977 - Remove the unused profiler strings mutex ID r=iain" This reverts commit 7dae6c49915a5eccb3c29b13238ccba445a916c9. Revert "Bug 1959977 - Insert ScriptSource RefPtr to keep it alive until the end of the profiling session r=iain,mstange" This reverts commit 53634e2553b9c7a4454105bc4f4ff526a659db80. Diffstat:
55 files changed, 209 insertions(+), 2248 deletions(-)
diff --git a/devtools/client/performance-new/@types/gecko.d.ts b/devtools/client/performance-new/@types/gecko.d.ts @@ -160,13 +160,8 @@ declare namespace MockedExports { arch: string; } - type JSSources = Partial<{ - [sourceUuid: string]: string; - }>; - interface ProfileGenerationAdditionalInformation { sharedLibraries: SharedLibrary[]; - jsSources: JSSources; } interface ProfileAndAdditionalInformation { diff --git a/devtools/client/performance-new/@types/perf.d.ts b/devtools/client/performance-new/@types/perf.d.ts @@ -481,8 +481,7 @@ export type RequestFromFrontend = | GetSymbolTableRequest | QuerySymbolicationApiRequest | GetPageFaviconsRequest - | OpenScriptInTabDebuggerRequest - | GetJSSourcesRequest; + | OpenScriptInTabDebuggerRequest; type StatusQueryRequest = { type: "STATUS_QUERY" }; type EnableMenuButtonRequest = { type: "ENABLE_MENU_BUTTON" }; @@ -518,10 +517,6 @@ type OpenScriptInTabDebuggerRequest = { line: number; column: number; }; -type GetJSSourcesRequest = { - type: "GET_JS_SOURCES"; - sourceUuids: Array<string>; -}; export type MessageToFrontend<R> = | OutOfBandErrorMessageToFrontend @@ -554,8 +549,7 @@ export type ResponseToFrontend = | GetSymbolTableResponse | QuerySymbolicationApiResponse | GetPageFaviconsResponse - | OpenScriptInTabDebuggerResponse - | GetJSSourcesResponse; + | OpenScriptInTabDebuggerResponse; type StatusQueryResponse = { menuButtonIsEnabled: boolean; @@ -586,8 +580,6 @@ type GetSymbolTableResponse = SymbolTableAsTuple; type QuerySymbolicationApiResponse = string; type GetPageFaviconsResponse = Array<ProfilerFaviconData | null>; type OpenScriptInTabDebuggerResponse = void; -type GetJSSourceReponseItem = { sourceText: string } | { error: string }; -type GetJSSourcesResponse = Array<GetJSSourceReponseItem>; /** * This represents an event channel that can talk to a content page on the web. @@ -612,10 +604,6 @@ export class ProfilerWebChannel { ) => void; } -type JSSources = Partial<{ - [sourceUuid: string]: string; -}>; - /** * The per-tab information that is stored when a new profile is captured * and a profiler tab is opened, to serve the correct profile to the tab @@ -624,7 +612,6 @@ type JSSources = Partial<{ export type ProfilerBrowserInfo = { profileCaptureResult: ProfileCaptureResult; symbolicationService: SymbolicationService | null; - jsSources: JSSources | null; }; export type ProfileCaptureResult = diff --git a/devtools/client/performance-new/panel/initializer.js b/devtools/client/performance-new/panel/initializer.js @@ -151,12 +151,7 @@ async function gInit(perfFront, traits, pageContext, openAboutProfiling) { type: "ERROR", error: typeof error === "string" ? new Error(error) : error, }; - registerProfileCaptureForBrowser( - browser, - profileCaptureResult, - null, - null - ); + registerProfileCaptureForBrowser(browser, profileCaptureResult, null); return; } @@ -181,8 +176,7 @@ async function gInit(perfFront, traits, pageContext, openAboutProfiling) { registerProfileCaptureForBrowser( browser, profileCaptureResult, - symbolicationService, - additionalInformation?.jsSources ?? null + symbolicationService ); }; diff --git a/devtools/client/performance-new/shared/background.sys.mjs b/devtools/client/performance-new/shared/background.sys.mjs @@ -25,7 +25,6 @@ import { createLazyLoaders } from "resource://devtools/client/performance-new/sh * @typedef {import("../@types/perf").ProfilerBrowserInfo} ProfilerBrowserInfo * @typedef {import("../@types/perf").ProfileCaptureResult} ProfileCaptureResult * @typedef {import("../@types/perf").ProfilerFaviconData} ProfilerFaviconData - * @typedef {import("../@types/perf").JSSources} JSSources */ /** @type {PerformancePref["PopupFeatureFlag"]} */ @@ -36,7 +35,7 @@ const POPUP_FEATURE_FLAG_PREF = "devtools.performance.popup.feature-flag"; // capabilities of the WebChannel. The front-end can handle old WebChannel // versions and has a full list of versions and capabilities here: // https://github.com/firefox-devtools/profiler/blob/main/src/app-logic/web-channel.js -const CURRENT_WEBCHANNEL_VERSION = 6; +const CURRENT_WEBCHANNEL_VERSION = 5; const lazyRequire = {}; // eslint-disable-next-line mozilla/lazy-getter-object-name @@ -128,8 +127,7 @@ export async function captureProfile(pageContext) { registerProfileCaptureForBrowser( browser, profileCaptureResult, - symbolicationService, - additionalInformation?.jsSources ?? null + symbolicationService ); } @@ -344,33 +342,7 @@ async function getResponseForMessage(request, browser) { const { openScriptInDebugger } = lazy.BrowserModule(); return openScriptInDebugger(tabId, scriptUrl, line, column); } - case "GET_JS_SOURCES": { - const { sourceUuids } = request; - if (!Array.isArray(sourceUuids)) { - throw new Error("sourceUuids must be an array"); - } - - const infoForBrowser = infoForBrowserMap.get(browser); - if (infoForBrowser === undefined) { - throw new Error("No JS source data found for this tab"); - } - const jsSources = infoForBrowser.jsSources; - if (jsSources === null) { - return sourceUuids.map(() => ({ - error: "Source not found in the browser", - })); - } - - return sourceUuids.map(uuid => { - const sourceText = jsSources[uuid]; - if (!sourceText) { - return { error: "Source not found in the browser" }; - } - - return { sourceText }; - }); - } default: { console.error( "An unknown message type was received by the profiler's WebChannel handler.", @@ -470,18 +442,15 @@ export async function handleWebChannelMessage(channel, id, message, target) { * when profiler.firefox.com sends GET_SYMBOL_TABLE WebChannel messages to us. This * method should obtain a symbol table for the requested binary and resolve the * returned promise with it. - * @param {JSSources | null} jsSources - JS sources from the profile collection. */ export function registerProfileCaptureForBrowser( browser, profileCaptureResult, - symbolicationService, - jsSources + symbolicationService ) { infoForBrowserMap.set(browser, { profileCaptureResult, symbolicationService, - jsSources, }); } diff --git a/devtools/client/performance-new/shared/utils.js b/devtools/client/performance-new/shared/utils.js @@ -573,12 +573,6 @@ const featureDescriptions = [ "Include all flow-related markers. These markers show the program flow better but " + "can cause more overhead in some places than normal.", }, - { - name: "JavaScript Sources", - value: "jssources", - title: "Collect JavaScript source code information for profiled scripts.", - experimental: true, - }, ]; module.exports = { diff --git a/devtools/client/performance-new/test/browser/browser.toml b/devtools/client/performance-new/test/browser/browser.toml @@ -9,7 +9,6 @@ support-files = [ "webchannel.html", "webchannel-favicons.html", "webchannel-open-script-in-debugger_assets/*", - "webchannel-js-sources.html", ] skip-if = [ "os == 'linux' && os_version == '18.04' && processor == 'x86_64' && tsan", # Bug 1804081, timeouts and data races in various tests @@ -66,8 +65,6 @@ skip-if = [ ["browser_webchannel-enable-menu-button.js"] -["browser_webchannel-get-js-sources.js"] - ["browser_webchannel-open-script-in-debugger.js"] ["browser_webchannel-page-favicon-data.js"] diff --git a/devtools/client/performance-new/test/browser/browser_webchannel-get-js-sources.js b/devtools/client/performance-new/test/browser/browser_webchannel-get-js-sources.js @@ -1,178 +0,0 @@ -/* 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/. */ - -"use strict"; - -const FRONTEND_URL = - "https://example.com/browser/devtools/client/performance-new/test/browser/webchannel-js-sources.html"; - -add_task(async function test_webchannel_get_js_sources() { - info("Test the WebChannel GET_JS_SOURCES request functionality"); - - // Register some mock JS sources for testing. We already test the logic that - // retrieves the sources in other tests. This test focuses on the webchannel - // itself only. - const mockJSSources = { - "uuid-12345-1": "function testFunction() { return 42; }", - "uuid-12345-2": "console.log('Hello from source 2');", - "uuid-67890-5": "function anotherFunction() { return 'test'; }", - "uuid-67890-6": "alert('Hello world');", - }; - - const sourcesToRequest = ["uuid-12345-1", "uuid-12345-2", "uuid-67890-5"]; - - const expectedResponse = [ - { sourceText: "function testFunction() { return 42; }" }, - { sourceText: "console.log('Hello from source 2');" }, - { sourceText: "function anotherFunction() { return 'test'; }" }, - ]; - - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: "about:blank", - }, - async browser => { - // Register the mock sources for the current browser - BackgroundJSM.registerProfileCaptureForBrowser( - browser, - Promise.resolve(new ArrayBuffer(0)), // Mock profile result - null, // No symbolication service - mockJSSources - ); - - // Now navigate to the test HTML page - const url = - FRONTEND_URL + - "?testType=basic" + - "&sourceUuids=" + - encodeURIComponent(JSON.stringify(sourcesToRequest)) + - "&expectedResponse=" + - encodeURIComponent(JSON.stringify(expectedResponse)); - - const loaded = BrowserTestUtils.browserLoaded(browser); - BrowserTestUtils.startLoadingURIString(browser, url); - await loaded; - - await waitForTabTitle("JS sources received"); - ok(true, "The JS sources are successfully retrieved by the WebChannel."); - } - ); -}); - -add_task(async function test_webchannel_get_js_sources_nonexistent() { - info("Test GET_JS_SOURCES WebChannel request with non-existent sources"); - - // Register some mock JS sources - const mockJSSources = { - "uuid-12345-1": "function testFunction() { return 42; }", - }; - - // Request non-existent sources (should return null) - const sourcesToRequest = [ - "uuid-12345-999", // Non-existent UUID - "uuid-99999-1", // Non-existent UUID - ]; - - const expectedResponse = [ - { error: "Source not found in the browser" }, - { error: "Source not found in the browser" }, - ]; - - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: "about:blank", - }, - async browser => { - // Register the mock sources for the current browser - BackgroundJSM.registerProfileCaptureForBrowser( - browser, - Promise.resolve(new ArrayBuffer(0)), - null, - mockJSSources - ); - - // Now navigate to the test HTML page - const url = - FRONTEND_URL + - "?testType=nonexistent" + - "&sourceUuids=" + - encodeURIComponent(JSON.stringify(sourcesToRequest)) + - "&expectedResponse=" + - encodeURIComponent(JSON.stringify(expectedResponse)); - - const loaded = BrowserTestUtils.browserLoaded(browser); - BrowserTestUtils.startLoadingURIString(browser, url); - await loaded; - - await waitForTabTitle("JS sources received"); - ok( - true, - "Successfully verified GET_JS_SOURCES with non-existent sources" - ); - } - ); -}); - -add_task(async function test_webchannel_get_js_sources_no_data() { - info("Test GET_JS_SOURCES WebChannel request when no data is registered"); - - const sourcesToRequest = ["uuid-12345-1"]; - - // Open a new tab without registering JS sources - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: "about:blank", - }, - async browser => { - // Don't register any JS sources - this should cause an error - - // Now navigate to the test HTML page - const url = - FRONTEND_URL + - "?testType=no-data" + - "&sourceUuids=" + - encodeURIComponent(JSON.stringify(sourcesToRequest)) + - "&expectError=true"; - - const loaded = BrowserTestUtils.browserLoaded(browser); - BrowserTestUtils.startLoadingURIString(browser, url); - await loaded; - - await waitForTabTitle("JS sources received"); - ok(true, "Successfully verified GET_JS_SOURCES error handling"); - } - ); -}); - -add_task(async function test_webchannel_get_js_sources_invalid_request() { - info("Test GET_JS_SOURCES WebChannel request with invalid sources parameter"); - - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: "about:blank", - }, - async browser => { - // Don't need to register JS sources for this test - - // For this test, we need to pass invalid sourceUuids directly in the HTML - // We'll use a special URL parameter that the HTML will use directly - const url = - FRONTEND_URL + - "?testType=invalid-request" + - "&sourceUuids=invalid_not_array" + // This is intentionally not JSON - "&expectError=true"; - - const loaded = BrowserTestUtils.browserLoaded(browser); - BrowserTestUtils.startLoadingURIString(browser, url); - await loaded; - - await waitForTabTitle("JS sources received"); - ok(true, "Successfully verified GET_JS_SOURCES invalid request handling"); - } - ); -}); diff --git a/devtools/client/performance-new/test/browser/webchannel-js-sources.html b/devtools/client/performance-new/test/browser/webchannel-js-sources.html @@ -1,186 +0,0 @@ -<!DOCTYPE html> -<!-- 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/. --> -<html> - <head> - <meta charset="utf-8"/> - <title></title> - </head> - <body> - <script> - "use strict"; - // This file is used to test the retrieval of JS sources from a front-end. - // Rather than using some kind of complicated message passing scheme to - // talk to the test harness, modify the title of the page. The tests can - // easily read the window title to see if things worked as expected. - - // The following are the titles used to communicate the page's state to the tests. - // Keep these in sync with any tests that read them. - const initialTitle = "Waiting for the JS sources"; - const successTitle = "JS sources received"; - const errorTitle = "Error" - - document.title = initialTitle; - - // A function which requests the JS sources from the browser using the GET_JS_SOURCES - // WebChannel message. - function getJSSources(sourceUuids, expectError = false) { - return new Promise((resolve, reject) => { - const requestId = Date.now(); - - function listener(event) { - window.removeEventListener( - "WebChannelMessageToContent", - listener, - true - ); - - const { id, message } = event.detail; - - dump(JSON.stringify(message)); - dump("\n"); - if (id !== "profiler.firefox.com" || - !message || - typeof message !== "object" - ) { - console.error(message); - reject(new Error("A malformed WebChannel event was received.")); - return; - } - - if (!message.type) { - console.error(message); - reject(new Error("The WebChannel event indicates an error.")); - return; - } - - if (message.requestId === requestId) { - if (message.type === "SUCCESS_RESPONSE") { - resolve(message.response); - } else if (message.type === "ERROR_RESPONSE") { - if (expectError) { - resolve({ error: message.error }); - } else { - reject(new Error(message.error)); - } - } else { - reject(new Error("Unknown response type: " + message.type)); - } - } - } - - window.addEventListener("WebChannelMessageToContent", listener, true); - - window.dispatchEvent( - new CustomEvent("WebChannelMessageToChrome", { - detail: JSON.stringify({ - id: "profiler.firefox.com", - message: { type: "GET_JS_SOURCES", requestId, sourceUuids }, - }), - }) - ); - }) - } - - async function runTest() { - try { - // Get test configuration from URL parameters - const params = new URLSearchParams(document.location.search); - const testType = params.get("testType") || "basic"; - const expectError = params.get("expectError") === "true"; - - let sourcesToRequest; - let expectedResponse; - - // Handle invalid-request test case where sourceUuids is intentionally not JSON - if (testType === "invalid-request") { - sourcesToRequest = params.get("sourceUuids"); // Keep as string for invalid test - expectedResponse = null; - } else { - sourcesToRequest = JSON.parse(params.get("sourceUuids") || "[]"); - expectedResponse = JSON.parse(params.get("expectedResponse") || "null"); - } - - let response; - if (expectError) { - response = await getJSSources(sourcesToRequest, true); - } else { - response = await getJSSources(sourcesToRequest, false); - } - - dump("response\n") - dump(JSON.stringify(response)); - - let testPassed = false; - - function deepEqual(obj1, obj2) { - return JSON.stringify(obj1) === JSON.stringify(obj2); - } - - switch (testType) { - case "basic": - // Test case: Request existing JS sources - testPassed = response && - Array.isArray(response) && - response.length === expectedResponse.length && - response.every((item, index) => deepEqual(item, expectedResponse[index])); - break; - - case "nonexistent": - // Test case: Request non-existent sources (should return error objects) - testPassed = response && - Array.isArray(response) && - response.length === expectedResponse.length && - response.every((item, index) => deepEqual(item, expectedResponse[index])); - break; - - case "no-data": - // Test case: No JS source data registered (should return error) - testPassed = response && - response.error && - response.error.includes("No JS source data found for this tab"); - break; - - case "invalid-request": - // Test case: Invalid sources parameter (should return error) - testPassed = response && - response.error && - response.error.includes("sourceUuids must be an array"); - break; - - default: - throw new Error("Unknown test type: " + testType); - } - - if (testPassed) { - document.title = successTitle; - } else { - dump('Test failed in webchannel-js-sources.html\n'); - dump(`Test type: ${testType}\n`); - dump(`Expected: ${JSON.stringify(expectedResponse)}\n`); - dump(`Actual: ${JSON.stringify(response)}\n`); - - console.error( - "Test failed in webchannel-js-sources.html", - { testType, expectedResponse, response } - ); - - document.title = errorTitle; - } - } catch (error) { - // Catch any error and notify the test. - document.title = errorTitle; - dump('An error was caught in webchannel-js-sources.html\n'); - dump(`${error}\n`); - console.error( - "An error was caught in webchannel-js-sources.html", - error - ); - } - } - - runTest(); - </script> - </body> -</html> diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h @@ -161,7 +161,6 @@ class MOZ_NON_PARAM JS_PUBLIC_API ProfilingFrameIterator { const char* label; JSScript* interpreterScript; uint64_t realmID; - uint32_t sourceId; public: void* returnAddress() const { @@ -238,12 +237,10 @@ class MOZ_STACK_CLASS ProfiledFrameHandle { void* addr_; void* canonicalAddr_; const char* label_; - uint32_t sourceId_; uint32_t depth_; ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, - void* addr, const char* label, uint32_t sourceId, - uint32_t depth); + void* addr, const char* label, uint32_t depth); public: const char* label() const { return label_; } @@ -253,8 +250,6 @@ class MOZ_STACK_CLASS ProfiledFrameHandle { JS_PUBLIC_API ProfilingFrameIterator::FrameKind frameKind() const; JS_PUBLIC_API uint64_t realmID() const; - - JS_PUBLIC_API uint32_t sourceId() const; }; class ProfiledFrameRange { @@ -296,7 +291,6 @@ class ProfiledFrameRange { js::jit::JitcodeGlobalEntry* entry_; // Assume maximum inlining depth is <64 const char* labels_[64]; - uint32_t sourceIds_[64]; uint32_t depth_; }; diff --git a/js/public/ProfilingSources.h b/js/public/ProfilingSources.h @@ -1,180 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * 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/. */ - -#ifndef js_ProfilingSources_h -#define js_ProfilingSources_h - -#include "mozilla/Variant.h" - -#include <stdint.h> - -#include "jstypes.h" - -#include "js/TypeDecls.h" -#include "js/Utility.h" -#include "js/Vector.h" - -/* - * Struct to pass JS source data with content type information for profiler use. - */ -struct JS_PUBLIC_API ProfilerJSSourceData { - public: - struct SourceTextUTF16 { - public: - SourceTextUTF16(JS::UniqueTwoByteChars&& c, size_t l) - : chars_(std::move(c)), length_(l) {} - - const JS::UniqueTwoByteChars& chars() const { return chars_; } - size_t length() const { return length_; } - - private: - // Not null-terminated string for source text. Always use it with the - // length. - JS::UniqueTwoByteChars chars_; - size_t length_; - }; - - struct SourceTextUTF8 { - public: - SourceTextUTF8(JS::UniqueChars&& c, size_t l) - : chars_(std::move(c)), length_(l) {} - - const JS::UniqueChars& chars() const { return chars_; } - size_t length() const { return length_; } - - private: - // Not null-terminated string for source text. Always use it with the - // length. - JS::UniqueChars chars_; - size_t length_; - }; - - /* - * Represents a source file that can be retrieved later in the parent process. - * Used when source text is not immediately available in the current process - * but can be fetched using the file path information. - */ - struct RetrievableFile {}; - - struct Unavailable {}; - - using ProfilerSourceVariant = - mozilla::Variant<SourceTextUTF16, SourceTextUTF8, RetrievableFile, - Unavailable>; - - // Constructors - ProfilerJSSourceData(uint32_t sourceId, JS::UniqueChars&& filePath, - size_t pathLen) - : sourceId_(sourceId), - filePath_(std::move(filePath)), - filePathLength_(pathLen), - data_(Unavailable{}) {} - - // UTF-8 source text with filePath - ProfilerJSSourceData(uint32_t sourceId, JS::UniqueChars&& chars, - size_t length, JS::UniqueChars&& filePath, - size_t pathLen) - : sourceId_(sourceId), - filePath_(std::move(filePath)), - filePathLength_(pathLen), - data_(SourceTextUTF8{std::move(chars), length}) {} - - // UTF-16 source text with filePath - ProfilerJSSourceData(uint32_t sourceId, JS::UniqueTwoByteChars&& chars, - size_t length, JS::UniqueChars&& filePath, - size_t pathLen) - : sourceId_(sourceId), - filePath_(std::move(filePath)), - filePathLength_(pathLen), - data_(SourceTextUTF16{std::move(chars), length}) {} - - // For the cases where no sourceId and filepath are needed. - ProfilerJSSourceData(JS::UniqueChars&& chars, size_t length) - : sourceId_(0), - filePath_(nullptr), - filePathLength_(0), - data_(SourceTextUTF8{std::move(chars), length}) {} - - ProfilerJSSourceData() - : sourceId_(0), filePathLength_(0), data_(Unavailable{}) {} - - static ProfilerJSSourceData CreateRetrievableFile(uint32_t sourceId, - JS::UniqueChars&& filePath, - size_t pathLength) { - ProfilerJSSourceData result(sourceId, std::move(filePath), pathLength); - result.data_.emplace<RetrievableFile>(); - return result; - } - - ProfilerJSSourceData(ProfilerJSSourceData&&) = default; - ProfilerJSSourceData& operator=(ProfilerJSSourceData&&) = default; - - // No copy constructors as this class owns its string storage. - ProfilerJSSourceData(const ProfilerJSSourceData& other) = delete; - ProfilerJSSourceData& operator=(const ProfilerJSSourceData&) = delete; - - uint32_t sourceId() const { return sourceId_; } - // Consumer should always check for filePathLength before calling this. - const char* filePath() const { - MOZ_ASSERT(filePath_); - return filePath_.get(); - } - size_t filePathLength() const { return filePathLength_; } - const ProfilerSourceVariant& data() const { return data_; } - - // Used only for memory reporting. - size_t SizeOf() const { - // Size of sourceId + filepath - size_t size = sizeof(uint32_t) + filePathLength_ * sizeof(char); - - data_.match( - [&](const SourceTextUTF16& srcText) { - size += srcText.length() * sizeof(char16_t); - }, - [&](const SourceTextUTF8& srcText) { - size += srcText.length() * sizeof(char); - }, - [](const RetrievableFile&) {}, [](const Unavailable&) {}); - - return size; - } - - private: - // Unique identifier for this source across the process. This can be used - // to refer to this source from places that don't want to hold a strong - // reference on the source itself. - // Generated by ScriptSource and retrieved via ScriptSource::id(). See - // ScriptSource::id_ for more details. - uint32_t sourceId_; - // Null-terminated file path for the source. - // It can be nullptr if: - // - The source has no filename. - // - filename allocation fails during copy. - JS::UniqueChars filePath_; - size_t filePathLength_; - ProfilerSourceVariant data_; -}; - -namespace js { - -using ProfilerJSSources = - js::Vector<ProfilerJSSourceData, 0, js::SystemAllocPolicy>; - -/* - * Main API for getting the profiled JS sources. - */ -JS_PUBLIC_API ProfilerJSSources GetProfilerScriptSources(JSRuntime* rt); - -/** - * Retrieve the JS sources that are only retrievable from the parent process. - * See RetrievableFile struct for more information. - * */ -JS_PUBLIC_API ProfilerJSSourceData -RetrieveProfilerSourceContent(JSContext* cx, const char* filename); - -} // namespace js - -#endif /* js_ProfilingSources_h */ diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h @@ -141,12 +141,6 @@ class ProfilingStackFrame { // ProfilingStackFrame object. mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> realmID_; - // ID of the script source for JS stack frames. - // Must not be used on non-JS frames; it'll contain either the default 0, - // or a leftover value from a previous JS stack frame that was using this - // ProfilingStackFrame object. - mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> sourceId_; - // The bytecode offset for JS stack frames. // Must not be used on non-JS frames; it'll contain either the default 0, // or a leftover value from a previous JS stack frame that was using this @@ -169,8 +163,6 @@ class ProfilingStackFrame { pcOffsetIfJS_ = offsetIfJS; uint64_t realmID = other.realmID_; realmID_ = realmID; - uint32_t sourceId = other.sourceId_; - sourceId_ = sourceId; uint32_t flagsAndCategory = other.flagsAndCategoryPair_; flagsAndCategoryPair_ = flagsAndCategory; return *this; @@ -298,7 +290,6 @@ class ProfilingStackFrame { flagsAndCategoryPair_ = uint32_t(Flags::IS_LABEL_FRAME) | (uint32_t(aCategoryPair) << uint32_t(Flags::FLAGS_BITCOUNT)) | aFlags; - sourceId_ = 0; MOZ_ASSERT(isLabelFrame()); } @@ -315,14 +306,12 @@ class ProfilingStackFrame { template <JS::ProfilingCategoryPair Category, uint32_t ExtraFlags = 0> void initJsFrame(const char* aLabel, const char* aDynamicString, - JSScript* aScript, jsbytecode* aPc, uint64_t aRealmID, - uint32_t aSourceId) { + JSScript* aScript, jsbytecode* aPc, uint64_t aRealmID) { label_ = aLabel; dynamicString_ = aDynamicString; spOrScript = aScript; pcOffsetIfJS_ = pcToOffset(aScript, aPc); realmID_ = aRealmID; - sourceId_ = aSourceId; flagsAndCategoryPair_ = (uint32_t(Category) << uint32_t(Flags::FLAGS_BITCOUNT)) | uint32_t(Flags::IS_JS_FRAME) | ExtraFlags; @@ -362,8 +351,6 @@ class ProfilingStackFrame { void trace(JSTracer* trc); - JS_PUBLIC_API uint32_t sourceId() const; - // The offset of a pc into a script's code can actually be 0, so to // signify a nullptr pc, use a -1 index. This is checked against in // pc() and setPC() to set/get the right pc. @@ -469,8 +456,7 @@ class JS_PUBLIC_API ProfilingStack final { } void pushJsFrame(const char* label, const char* dynamicString, - JSScript* script, jsbytecode* pc, uint64_t aRealmID, - uint32_t aSourceId = 0) { + JSScript* script, jsbytecode* pc, uint64_t aRealmID) { // This thread is the only one that ever changes the value of // stackPointer. Only load the atomic once. uint32_t oldStackPointer = stackPointer; @@ -480,7 +466,7 @@ class JS_PUBLIC_API ProfilingStack final { } frames[oldStackPointer] .initJsFrame<JS::ProfilingCategoryPair::JS_Interpreter>( - label, dynamicString, script, pc, aRealmID, aSourceId); + label, dynamicString, script, pc, aRealmID); // This must happen at the end, see the comment in pushLabelFrame. stackPointer = stackPointer + 1; diff --git a/js/src/debugger/Source.cpp b/js/src/debugger/Source.cpp @@ -217,7 +217,16 @@ class DebuggerSourceGetTextMatcher { return NewStringCopyZ<CanGC>(cx_, "[no source]"); } - if (ss->shouldUnwrapEventHandlerBody()) { + // In case of DOM event handler like <div onclick="foo()" the JS code is + // wrapped into + // function onclick() {foo()} + // We want to only return `foo()` here. + // But only for event handlers, for `new Function("foo()")`, we want to + // return: + // function anonymous() {foo()} + if (ss->hasIntroductionType() && + strcmp(ss->introductionType(), "eventHandler") == 0 && + ss->isFunctionBody()) { return ss->functionBodyString(cx_); } diff --git a/js/src/jit/JSJitFrameIter.cpp b/js/src/jit/JSJitFrameIter.cpp @@ -598,8 +598,7 @@ const char* JSJitProfilingFrameIterator::baselineInterpreterLabel() const { } void JSJitProfilingFrameIterator::baselineInterpreterScriptPC( - JSScript** script, jsbytecode** pc, uint64_t* realmID, - uint32_t* sourceId) const { + JSScript** script, jsbytecode** pc, uint64_t* realmID) const { MOZ_ASSERT(type_ == FrameType::BaselineJS); BaselineFrame* blFrame = (BaselineFrame*)(fp_ - BaselineFrame::Size()); *script = frameScript(); @@ -613,7 +612,6 @@ void JSJitProfilingFrameIterator::baselineInterpreterScriptPC( } *realmID = (*script)->realm()->creationOptions().profilerRealmID(); - *sourceId = (*script)->scriptSource()->id(); } } diff --git a/js/src/jit/JSJitFrameIter.h b/js/src/jit/JSJitFrameIter.h @@ -272,7 +272,7 @@ class JSJitProfilingFrameIterator { const char* baselineInterpreterLabel() const; void baselineInterpreterScriptPC(JSScript** script, jsbytecode** pc, - uint64_t* realmID, uint32_t* sourceId) const; + uint64_t* realmID) const; void* fp() const { MOZ_ASSERT(!done()); diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp @@ -90,10 +90,6 @@ bool JSScript::createJitScript(JSContext* cx) { if (!profileString) { return false; } - - if (!cx->runtime()->geckoProfiler().insertScriptSource(scriptSource())) { - return false; - } } static_assert(sizeof(JitScript) % sizeof(uintptr_t) == 0, @@ -351,10 +347,6 @@ void JitScript::ensureProfileString(JSContext* cx, JSScript* script) { if (!profileString_) { oomUnsafe.crash("Failed to allocate profile string"); } - if (!cx->runtime()->geckoProfiler().insertScriptSource( - script->scriptSource())) { - oomUnsafe.crash("Failed to insert profiled script source"); - } } /* static */ diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp @@ -47,8 +47,7 @@ void* IonEntry::canonicalNativeAddrFor(void* ptr) const { return (void*)(((uint8_t*)nativeStartAddr()) + region.nativeOffset()); } -uint32_t IonEntry::callStackAtAddr(void* ptr, const char** labelResults, - uint32_t* sourceIdResults, +uint32_t IonEntry::callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const { MOZ_ASSERT(maxResults >= 1); @@ -64,9 +63,7 @@ uint32_t IonEntry::callStackAtAddr(void* ptr, const char** labelResults, locationIter.readNext(&scriptIdx, &pcOffset); MOZ_ASSERT(getStr(scriptIdx)); - labelResults[count] = getStr(scriptIdx); - sourceIdResults[count] = getScriptSource(scriptIdx).scriptSource->id(); - count++; + results[count++] = getStr(scriptIdx); if (count >= maxResults) { break; } @@ -98,12 +95,10 @@ static IonEntry& IonEntryForIonIC(JSRuntime* rt, const IonICEntry* icEntry) { void* IonICEntry::canonicalNativeAddrFor(void* ptr) const { return ptr; } uint32_t IonICEntry::callStackAtAddr(JSRuntime* rt, void* ptr, - const char** labelResults, - uint32_t* sourceIdResults, + const char** results, uint32_t maxResults) const { const IonEntry& entry = IonEntryForIonIC(rt, this); - return entry.callStackAtAddr(rejoinAddr(), labelResults, sourceIdResults, - maxResults); + return entry.callStackAtAddr(rejoinAddr(), results, maxResults); } uint64_t IonICEntry::realmID(JSRuntime* rt) const { @@ -117,14 +112,12 @@ void* BaselineEntry::canonicalNativeAddrFor(void* ptr) const { return ptr; } -uint32_t BaselineEntry::callStackAtAddr(void* ptr, const char** labelResults, - uint32_t* sourceIdResults, +uint32_t BaselineEntry::callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const { MOZ_ASSERT(containsPointer(ptr)); MOZ_ASSERT(maxResults >= 1); - labelResults[0] = str(); - sourceIdResults[0] = scriptSource().scriptSource->id(); + results[0] = str(); return 1; } @@ -133,8 +126,7 @@ void* BaselineInterpreterEntry::canonicalNativeAddrFor(void* ptr) const { } uint32_t BaselineInterpreterEntry::callStackAtAddr(void* ptr, - const char** labelResults, - uint32_t* sourceIdResults, + const char** results, uint32_t maxResults) const { MOZ_CRASH("shouldn't be called for BaselineInterpreter entries"); } @@ -157,13 +149,11 @@ bool RealmIndependentSharedEntry::callStackAtAddr( } uint32_t RealmIndependentSharedEntry::callStackAtAddr( - void* ptr, const char** labelResults, uint32_t* sourceIdResults, - uint32_t maxResults) const { + void* ptr, const char** results, uint32_t maxResults) const { MOZ_ASSERT(containsPointer(ptr)); MOZ_ASSERT(maxResults >= 1); - labelResults[0] = str(); - sourceIdResults[0] = 0; + results[0] = str(); return 1; } @@ -339,28 +329,22 @@ bool JitcodeGlobalEntry::isJitcodeMarkedFromAnyThread(JSRuntime* rt) { } uint32_t JitcodeGlobalEntry::callStackAtAddr(JSRuntime* rt, void* ptr, - const char** labelResults, - uint32_t* sourceIdResults, + const char** results, uint32_t maxResults) const { switch (kind()) { case Kind::Ion: - return asIon().callStackAtAddr(ptr, labelResults, sourceIdResults, - maxResults); + return asIon().callStackAtAddr(ptr, results, maxResults); case Kind::IonIC: - return asIonIC().callStackAtAddr(rt, ptr, labelResults, sourceIdResults, - maxResults); + return asIonIC().callStackAtAddr(rt, ptr, results, maxResults); case Kind::Baseline: - return asBaseline().callStackAtAddr(ptr, labelResults, sourceIdResults, - maxResults); + return asBaseline().callStackAtAddr(ptr, results, maxResults); case Kind::BaselineInterpreter: - return asBaselineInterpreter().callStackAtAddr( - ptr, labelResults, sourceIdResults, maxResults); + return asBaselineInterpreter().callStackAtAddr(ptr, results, maxResults); case Kind::Dummy: - return asDummy().callStackAtAddr(rt, ptr, labelResults, sourceIdResults, - maxResults); + return asDummy().callStackAtAddr(rt, ptr, results, maxResults); case Kind::RealmIndependentShared: - return asRealmIndependentShared().callStackAtAddr( - ptr, labelResults, sourceIdResults, maxResults); + return asRealmIndependentShared().callStackAtAddr(ptr, results, + maxResults); } MOZ_CRASH("Invalid kind"); } @@ -973,13 +957,12 @@ bool JitcodeIonTable::WriteIonTable(CompactBufferWriter& writer, JS::ProfiledFrameHandle::ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, void* addr, const char* label, - uint32_t sourceId, uint32_t depth) + uint32_t depth) : rt_(rt), entry_(entry), addr_(addr), canonicalAddr_(nullptr), label_(label), - sourceId_(sourceId), depth_(depth) { if (!canonicalAddr_) { canonicalAddr_ = entry_.canonicalNativeAddrFor(rt_, addr_); @@ -1004,10 +987,6 @@ JS_PUBLIC_API uint64_t JS::ProfiledFrameHandle::realmID() const { return entry_.realmID(rt_); } -JS_PUBLIC_API uint32_t JS::ProfiledFrameHandle::sourceId() const { - return sourceId_; -} - JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx, void* addr) { JSRuntime* rt = cx->runtime(); @@ -1018,8 +997,8 @@ JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx, ProfiledFrameRange result(rt, addr, entry); if (entry) { - result.depth_ = entry->callStackAtAddr( - rt, addr, result.labels_, result.sourceIds_, std::size(result.labels_)); + result.depth_ = entry->callStackAtAddr(rt, addr, result.labels_, + std::size(result.labels_)); } return result; } @@ -1029,6 +1008,5 @@ JS::ProfiledFrameHandle JS::ProfiledFrameRange::Iter::operator*() const { // and the depth we need to pass to ProfiledFrameHandle goes down. uint32_t depth = range_.depth_ - 1 - index_; return ProfiledFrameHandle(range_.rt_, *range_.entry_, range_.addr_, - range_.labels_[depth], range_.sourceIds_[depth], - depth); + range_.labels_[depth], depth); } diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h @@ -195,10 +195,9 @@ class JitcodeGlobalEntry : public JitCodeRange { void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const; // Read the inline call stack at a given point in the native code and append - // into the given results buffer. Innermost script will be appended first, and - // outermost appended last. - uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + // into the given results buffer. Innermost (script,pc) pair will be appended + // first, and outermost appended last. + uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const; }; @@ -286,8 +285,7 @@ class IonEntry : public JitcodeGlobalEntry { void* canonicalNativeAddrFor(void* ptr) const; - uint32_t callStackAtAddr(void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t realmID() const { return realmId_; } @@ -312,8 +310,7 @@ class IonICEntry : public JitcodeGlobalEntry { void* canonicalNativeAddrFor(void* ptr) const; - uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const; uint64_t realmID(JSRuntime* rt) const; @@ -343,8 +340,7 @@ class BaselineEntry : public JitcodeGlobalEntry { void* canonicalNativeAddrFor(void* ptr) const; - uint32_t callStackAtAddr(void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t realmID() const { return realmId_; } @@ -371,8 +367,7 @@ class RealmIndependentSharedEntry : public JitcodeGlobalEntry { [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, uint32_t* depth) const; - uint32_t callStackAtAddr(void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t realmID() const; @@ -387,8 +382,7 @@ class BaselineInterpreterEntry : public JitcodeGlobalEntry { void* canonicalNativeAddrFor(void* ptr) const; - uint32_t callStackAtAddr(void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t realmID() const; @@ -406,8 +400,7 @@ class DummyEntry : public JitcodeGlobalEntry { return nullptr; } - uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** labelResults, - uint32_t* sourceIdResults, + uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const { return 0; } diff --git a/js/src/moz.build b/js/src/moz.build @@ -180,7 +180,6 @@ EXPORTS.js += [ "../public/Printf.h", "../public/ProfilingCategory.h", "../public/ProfilingFrameIterator.h", - "../public/ProfilingSources.h", "../public/ProfilingStack.h", "../public/Promise.h", "../public/PropertyAndElement.h", diff --git a/js/src/vm/GeckoProfiler.cpp b/js/src/vm/GeckoProfiler.cpp @@ -16,24 +16,21 @@ #include "jit/JitRuntime.h" #include "jit/JSJitFrameIter.h" #include "jit/PerfSpewer.h" -#include "js/experimental/SourceHook.h" +#include "js/ProfilingStack.h" #include "vm/FrameIter.h" // js::OnlyJSJitFrameIter #include "vm/JitActivation.h" #include "vm/JSScript.h" -#include "vm/MutexIDs.h" #include "gc/Marking-inl.h" #include "jit/JSJitFrameIter-inl.h" using namespace js; -using mozilla::Utf8Unit; GeckoProfilerThread::GeckoProfilerThread() : profilingStack_(nullptr), profilingStackIfEnabled_(nullptr) {} GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt) : rt(rt), - scriptSources_(mutexid::GeckoProfilerScriptSources), slowAssertions(false), enabled_(false), eventMarker_(nullptr), @@ -125,8 +122,6 @@ void GeckoProfilerRuntime::enable(bool enabled) { enabled_ = enabled; - scriptSources_.writeLock()->clear(); - /* Toggle Gecko Profiler-related jumps on baseline jitcode. * The call to |ReleaseAllJITCode| above will release most baseline jitcode, * but not jitcode for scripts with active frames on the stack. These scripts @@ -260,11 +255,6 @@ bool GeckoProfilerThread::enter(JSContext* cx, JSScript* script) { return false; } - if (!cx->runtime()->geckoProfiler().insertScriptSource( - script->scriptSource())) { - return false; - } - #ifdef DEBUG // In debug builds, assert the JS profiling stack frames already on the // stack have a non-null pc. Only look at the top frames to avoid quadratic @@ -281,8 +271,7 @@ bool GeckoProfilerThread::enter(JSContext* cx, JSScript* script) { profilingStack_->pushJsFrame( "", dynamicString, script, script->code(), - script->realm()->creationOptions().profilerRealmID(), - script->scriptSource()->id()); + script->realm()->creationOptions().profilerRealmID()); return true; } @@ -448,96 +437,6 @@ void GeckoProfilerRuntime::checkStringsMapAfterMovingGC() { } #endif -// Get all script sources as a list of ProfilerJSSourceData. -js::ProfilerJSSources GeckoProfilerRuntime::getProfilerScriptSources() { - js::ProfilerJSSources result; - - auto guard = scriptSources_.readLock(); - for (auto iter = guard->iter(); !iter.done(); iter.next()) { - const RefPtr<ScriptSource>& scriptSource = iter.get(); - MOZ_ASSERT(scriptSource); - - bool hasSourceText; - bool retrievableSource; - ScriptSource::getSourceProperties(scriptSource, &hasSourceText, - &retrievableSource); - - uint32_t sourceId = scriptSource->id(); - - // Get filename for all source types. Create single copy to be moved. - const char* filename = scriptSource->filename(); - size_t filenameLen = 0; - JS::UniqueChars filenameCopy; - if (filename) { - filenameLen = strlen(filename); - filenameCopy.reset(static_cast<char*>(js_malloc(filenameLen + 1))); - if (filenameCopy) { - strcpy(filenameCopy.get(), filename); - } - } - - if (retrievableSource) { - (void)result.append(ProfilerJSSourceData::CreateRetrievableFile( - sourceId, std::move(filenameCopy), filenameLen)); - continue; - } - - if (!hasSourceText) { - (void)result.append( - ProfilerJSSourceData(sourceId, std::move(filenameCopy), filenameLen)); - continue; - } - - size_t sourceLength = scriptSource->length(); - if (sourceLength == 0) { - (void)result.append( - ProfilerJSSourceData(sourceId, JS::UniqueTwoByteChars(), 0, - std::move(filenameCopy), filenameLen)); - continue; - } - - SubstringCharsResult sourceResult(JS::UniqueChars(nullptr)); - size_t charsLength = 0; - - if (scriptSource->shouldUnwrapEventHandlerBody()) { - sourceResult = scriptSource->functionBodyStringChars(&charsLength); - - if (charsLength == 0) { - (void)result.append( - ProfilerJSSourceData(sourceId, JS::UniqueTwoByteChars(), 0, - std::move(filenameCopy), filenameLen)); - continue; - } - } else { - sourceResult = scriptSource->substringChars(0, sourceLength); - charsLength = sourceLength; - } - - // Convert SubstringCharsResult to ProfilerJSSourceData. - // Note: The returned buffers are NOT null-terminated. The length is - // tracked separately in charsLength and passed to ProfilerJSSourceData. - if (sourceResult.is<JS::UniqueChars>()) { - auto& utf8Chars = sourceResult.as<JS::UniqueChars>(); - if (!utf8Chars) { - continue; - } - (void)result.append( - ProfilerJSSourceData(sourceId, std::move(utf8Chars), charsLength, - std::move(filenameCopy), filenameLen)); - } else { - auto& utf16Chars = sourceResult.as<JS::UniqueTwoByteChars>(); - if (!utf16Chars) { - continue; - } - (void)result.append( - ProfilerJSSourceData(sourceId, std::move(utf16Chars), charsLength, - std::move(filenameCopy), filenameLen)); - } - } - - return result; -} - void ProfilingStackFrame::trace(JSTracer* trc) { if (isJsFrame()) { JSScript* s = rawScript(); @@ -633,10 +532,6 @@ void ProfilingStackFrame::setPC(jsbytecode* pc) { pcOffsetIfJS_ = pcToOffset(script, pc); } -JS_PUBLIC_API uint32_t ProfilingStackFrame::sourceId() const { - return sourceId_; -} - JS_PUBLIC_API void js::SetContextProfilingStack( JSContext* cx, ProfilingStack* profilingStack) { cx->geckoProfiler().setProfilingStack( @@ -665,43 +560,6 @@ JS_PUBLIC_API void js::RegisterContextProfilerMarkers( terminatingFlowMarker); } -JS_PUBLIC_API js::ProfilerJSSources js::GetProfilerScriptSources( - JSRuntime* rt) { - return rt->geckoProfiler().getProfilerScriptSources(); -} - -JS_PUBLIC_API ProfilerJSSourceData -js::RetrieveProfilerSourceContent(JSContext* cx, const char* filename) { - MOZ_ASSERT(filename && strlen(filename)); - if (!cx) { - return ProfilerJSSourceData(); // Return unavailable - } - - // Check if source hook is available - if (!cx->runtime()->sourceHook.ref()) { - return ProfilerJSSourceData(); // Return unavailable - } - - size_t sourceLength = 0; - char* utf8Source = nullptr; - - bool loadSuccess = cx->runtime()->sourceHook->load( - cx, filename, nullptr, &utf8Source, &sourceLength); - - if (!loadSuccess) { - // Clear the pending exception that have been set by the source hook. - JS_ClearPendingException(cx); - return ProfilerJSSourceData(); // Return unavailable - } - - if (utf8Source) { - return ProfilerJSSourceData(JS::UniqueChars(utf8Source), sourceLength); - } - - // Hook returned success but no source data. Return unavailable. - return ProfilerJSSourceData(); -} - AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx) : cx_(cx), previouslyEnabled_(cx->isProfilerSamplingEnabled()) { if (previouslyEnabled_) { diff --git a/js/src/vm/GeckoProfiler.h b/js/src/vm/GeckoProfiler.h @@ -20,10 +20,8 @@ #include "js/AllocPolicy.h" #include "js/HashTable.h" #include "js/ProfilingCategory.h" -#include "js/ProfilingSources.h" #include "js/TypeDecls.h" #include "js/Utility.h" -#include "threading/ExclusiveData.h" #include "threading/ProtectedData.h" /* @@ -114,7 +112,6 @@ namespace js { class BaseScript; class GeckoProfilerThread; -class ScriptSource; // The `ProfileStringMap` weakly holds its `BaseScript*` keys and owns its // string values. Entries are removed when the `BaseScript` is finalized; see @@ -122,14 +119,9 @@ class ScriptSource; using ProfileStringMap = HashMap<BaseScript*, JS::UniqueChars, DefaultHasher<BaseScript*>, SystemAllocPolicy>; -using ProfilerScriptSourceSet = - HashSet<RefPtr<ScriptSource>, PointerHasher<ScriptSource*>, - SystemAllocPolicy>; - class GeckoProfilerRuntime { JSRuntime* rt; MainThreadData<ProfileStringMap> strings_; - RWExclusiveData<ProfilerScriptSourceSet> scriptSources_; bool slowAssertions; uint32_t enabled_; void (*eventMarker_)(mozilla::MarkerCategory, const char*, const char*); @@ -187,18 +179,6 @@ class GeckoProfilerRuntime { size_t stringsCount(); void stringsReset(); - bool insertScriptSource(ScriptSource* scriptSource) { - MOZ_ASSERT(scriptSource); - auto guard = scriptSources_.writeLock(); - if (!enabled_) { - return true; - } - - return guard->put(scriptSource); - } - - js::ProfilerJSSources getProfilerScriptSources(); - const uint32_t* addressOfEnabled() const { return &enabled_; } void fixupStringsMapAfterMovingGC(); diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp @@ -834,7 +834,6 @@ void ScriptSourceObject::clearPrivate(JSRuntime* rt) { getSlotRef(PRIVATE_SLOT).setUndefinedUnchecked(); } -// Main-thread source loader that can retrieve sources via the source hook. class ScriptSource::LoadSourceMatcher { JSContext* const cx_; ScriptSource* const ss_; @@ -856,11 +855,6 @@ class ScriptSource::LoadSourceMatcher { return true; } - bool operator()(const Missing&) const { - *loaded_ = false; - return true; - } - template <typename Unit> bool operator()(const Retrievable<Unit>&) { if (!cx_->runtime()->sourceHook.ref()) { @@ -878,6 +872,11 @@ class ScriptSource::LoadSourceMatcher { return true; } + bool operator()(const Missing&) const { + *loaded_ = false; + return true; + } + private: bool tryLoadAndSetSource(const Utf8Unit&, size_t* length) const { char* utf8Source; @@ -928,46 +927,6 @@ bool ScriptSource::loadSource(JSContext* cx, ScriptSource* ss, bool* loaded) { return ss->data.match(LoadSourceMatcher(cx, ss, loaded)); } -// Matcher to get source properties: whether source is present and whether -// it is retrievable. -class ScriptSource::SourcePropertiesGetter { - bool* const hasSourceText_; - bool* const retrievable_; - - public: - explicit SourcePropertiesGetter(bool* hasSourceText, bool* retrievable) - : hasSourceText_(hasSourceText), retrievable_(retrievable) {} - - template <typename Unit, SourceRetrievable CanRetrieve> - void operator()(const Compressed<Unit, CanRetrieve>&) const { - *hasSourceText_ = true; - *retrievable_ = false; - } - - template <typename Unit, SourceRetrievable CanRetrieve> - void operator()(const Uncompressed<Unit, CanRetrieve>&) const { - *hasSourceText_ = true; - *retrievable_ = false; - } - - template <typename Unit> - void operator()(const Retrievable<Unit>&) { - // Retrievable requires the main thread. Do not attempt to retrieve it. - *hasSourceText_ = false; - *retrievable_ = true; - } - - void operator()(const Missing&) const { - *hasSourceText_ = false; - *retrievable_ = false; - } -}; - -void ScriptSource::getSourceProperties(ScriptSource* ss, bool* hasSourceText, - bool* retrievable) { - ss->data.match(SourcePropertiesGetter(hasSourceText, retrievable)); -} - /* static */ JSLinearString* JSScript::sourceData(JSContext* cx, HandleScript script) { MOZ_ASSERT(script->scriptSource()->hasSourceText()); @@ -1060,18 +1019,14 @@ size_t UncompressedSourceCache::sizeOfExcludingThis( template <typename Unit> const Unit* ScriptSource::chunkUnits( - JSContext* maybeCx, UncompressedSourceCache::AutoHoldEntry& holder, + JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder, size_t chunk) { const CompressedData<Unit>& c = *compressedData<Unit>(); - // Try cache lookup only if we have a JSContext - if (maybeCx) { - ScriptSourceChunk ssc(this, chunk); - if (const Unit* decompressed = - maybeCx->caches().uncompressedSourceCache.lookup<Unit>(ssc, - holder)) { - return decompressed; - } + ScriptSourceChunk ssc(this, chunk); + if (const Unit* decompressed = + cx->caches().uncompressedSourceCache.lookup<Unit>(ssc, holder)) { + return decompressed; } size_t totalLengthInBytes = length() * sizeof(Unit); @@ -1081,9 +1036,7 @@ const Unit* ScriptSource::chunkUnits( const size_t chunkLength = chunkBytes / sizeof(Unit); EntryUnits<Unit> decompressed(js_pod_malloc<Unit>(chunkLength)); if (!decompressed) { - if (maybeCx) { - JS_ReportOutOfMemory(maybeCx); - } + JS_ReportOutOfMemory(cx); return nullptr; } @@ -1092,27 +1045,16 @@ const Unit* ScriptSource::chunkUnits( if (!DecompressStringChunk( reinterpret_cast<const unsigned char*>(c.raw.chars()), chunk, reinterpret_cast<unsigned char*>(decompressed.get()), chunkBytes)) { - if (maybeCx) { - JS_ReportOutOfMemory(maybeCx); - } + JS_ReportOutOfMemory(cx); return nullptr; } const Unit* ret = decompressed.get(); - - // Try to cache the result only if we have a JSContext - if (maybeCx) { - ScriptSourceChunk ssc(this, chunk); - if (!maybeCx->caches().uncompressedSourceCache.put( - ssc, ToSourceData(std::move(decompressed)), holder)) { - JS_ReportOutOfMemory(maybeCx); - return nullptr; - } - } else { - // Without caching, transfer ownership to holder for memory management - holder.holdUnits(std::move(decompressed)); + if (!cx->caches().uncompressedSourceCache.put( + ssc, ToSourceData(std::move(decompressed)), holder)) { + JS_ReportOutOfMemory(cx); + return nullptr; } - return ret; } @@ -1185,7 +1127,7 @@ ScriptSource::PinnedUnitsIfUncompressed<Unit>::~PinnedUnitsIfUncompressed() { } template <typename Unit> -const Unit* ScriptSource::units(JSContext* maybeCx, +const Unit* ScriptSource::units(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder, size_t begin, size_t len) { MOZ_ASSERT(begin <= length()); @@ -1228,7 +1170,7 @@ const Unit* ScriptSource::units(JSContext* maybeCx, // Directly return units within a single chunk. UncompressedSourceCache // and |holder| will hold the units alive past function return. if (firstChunk == lastChunk) { - const Unit* units = chunkUnits<Unit>(maybeCx, holder, firstChunk); + const Unit* units = chunkUnits<Unit>(cx, holder, firstChunk); if (!units) { return nullptr; } @@ -1240,9 +1182,7 @@ const Unit* ScriptSource::units(JSContext* maybeCx, // decompressed units into freshly-allocated memory to return. EntryUnits<Unit> decompressed(js_pod_malloc<Unit>(len)); if (!decompressed) { - if (maybeCx) { - JS_ReportOutOfMemory(maybeCx); - } + JS_ReportOutOfMemory(cx); return nullptr; } @@ -1255,7 +1195,7 @@ const Unit* ScriptSource::units(JSContext* maybeCx, // with multiple chunks, and we must use and destroy distinct, fresh // holders for each chunk. UncompressedSourceCache::AutoHoldEntry firstHolder; - const Unit* units = chunkUnits<Unit>(maybeCx, firstHolder, firstChunk); + const Unit* units = chunkUnits<Unit>(cx, firstHolder, firstChunk); if (!units) { return nullptr; } @@ -1266,7 +1206,7 @@ const Unit* ScriptSource::units(JSContext* maybeCx, for (size_t i = firstChunk + 1; i < lastChunk; i++) { UncompressedSourceCache::AutoHoldEntry chunkHolder; - const Unit* units = chunkUnits<Unit>(maybeCx, chunkHolder, i); + const Unit* units = chunkUnits<Unit>(cx, chunkHolder, i); if (!units) { return nullptr; } @@ -1276,7 +1216,7 @@ const Unit* ScriptSource::units(JSContext* maybeCx, { UncompressedSourceCache::AutoHoldEntry lastHolder; - const Unit* units = chunkUnits<Unit>(maybeCx, lastHolder, lastChunk); + const Unit* units = chunkUnits<Unit>(cx, lastHolder, lastChunk); if (!units) { return nullptr; } @@ -1310,14 +1250,14 @@ const Unit* ScriptSource::uncompressedUnits(size_t begin, size_t len) { template <typename Unit> ScriptSource::PinnedUnits<Unit>::PinnedUnits( - JSContext* maybeCx, ScriptSource* source, + JSContext* cx, ScriptSource* source, UncompressedSourceCache::AutoHoldEntry& holder, size_t begin, size_t len) : PinnedUnitsBase(source) { MOZ_ASSERT(source->hasSourceType<Unit>(), "must pin units of source's type"); addReader(); - units_ = source->units<Unit>(maybeCx, holder, begin, len); + units_ = source->units<Unit>(cx, holder, begin, len); if (!units_) { removeReader<Unit>(); } @@ -1407,62 +1347,6 @@ JSLinearString* ScriptSource::substringDontDeflate(JSContext* cx, size_t start, return NewStringCopyNDontDeflate<CanGC>(cx, units.asChars(), len); } -SubstringCharsResult ScriptSource::substringChars(size_t start, size_t stop) { - MOZ_ASSERT(start <= stop); - - size_t len = stop - start; - MOZ_ASSERT(len > 0, "Callers must handle empty sources before calling this"); - - UncompressedSourceCache::AutoHoldEntry holder; - - // UTF-8 source text. - if (hasSourceType<Utf8Unit>()) { - // Pass nullptr JSContext - this method is designed to be called - // off-main-thread where JSContext is not available. Decompression still - // works but without caching. - PinnedUnits<Utf8Unit> units(nullptr, this, holder, start, len); - if (!units.asChars()) { - // Allocation failure or decompression error. - return SubstringCharsResult(JS::UniqueChars(nullptr)); - } - - const char* str = units.asChars(); - // For UTF-8 source, create a copy of the char data. - // Note: We allocate exactly `len` bytes without a null terminator. - // Callers must track the length separately. - char* copy = static_cast<char*>(js_malloc(len * sizeof(char))); - if (!copy) { - // Allocation failure. - return SubstringCharsResult(JS::UniqueChars(nullptr)); - } - - mozilla::PodCopy(copy, str, len); - return SubstringCharsResult(JS::UniqueChars(copy)); - } - - // UTF-16 source text. - // Pass nullptr JSContext - this method is designed to be called - // off-main-thread where JSContext is not available. Decompression still works - // but without caching. - PinnedUnits<char16_t> units(nullptr, this, holder, start, len); - if (!units.asChars()) { - // Allocation failure or decompression error. - return SubstringCharsResult(JS::UniqueTwoByteChars(nullptr)); - } - - // For UTF-16 source, create a copy of the char16_t data. - // Note: We allocate exactly `len` char16_t elements without a null - // terminator. Callers must track the length separately. - char16_t* copy = static_cast<char16_t*>(js_malloc(len * sizeof(char16_t))); - if (!copy) { - // Allocation failure. - return SubstringCharsResult(JS::UniqueTwoByteChars(nullptr)); - } - - mozilla::PodCopy(copy, units.asChars(), len); - return SubstringCharsResult(JS::UniqueTwoByteChars(copy)); -} - bool ScriptSource::appendSubstring(JSContext* cx, StringBuilder& buf, size_t start, size_t stop) { MOZ_ASSERT(start <= stop); @@ -1503,23 +1387,6 @@ JSLinearString* ScriptSource::functionBodyString(JSContext* cx) { return substring(cx, start, stop); } -SubstringCharsResult ScriptSource::functionBodyStringChars(size_t* outLength) { - MOZ_ASSERT(isFunctionBody()); - MOZ_ASSERT(outLength); - - size_t start = parameterListEnd_ + FunctionConstructorMedialSigils.length(); - size_t stop = length() - FunctionConstructorFinalBrace.length(); - *outLength = stop - start; - - // Handle empty function body. Return nullptr to indicate empty result. - // This is distinct from substringChars which asserts non-empty length. - if (*outLength == 0) { - return SubstringCharsResult(JS::UniqueChars(nullptr)); - } - - return substringChars(start, stop); -} - template <typename ContextT, typename Unit> [[nodiscard]] bool ScriptSource::setUncompressedSourceHelper( ContextT* cx, EntryUnits<Unit>&& source, size_t length, diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h @@ -374,11 +374,6 @@ struct SourceTypeTraits<char16_t> { [[nodiscard]] extern bool SynchronouslyCompressSource( JSContext* cx, JS::Handle<BaseScript*> script); -// Variant return type for ScriptSource::substringChars to support both UTF-8 -// and UTF-16. -using SubstringCharsResult = - mozilla::Variant<JS::UniqueChars, JS::UniqueTwoByteChars>; - // [SMDOC] ScriptSource // // This class abstracts over the source we used to compile from. The current @@ -423,10 +418,7 @@ class ScriptSource { const Unit* units_; public: - // If maybeCx is nullptr, compressed sources will still be decompressed but - // the result will not be cached. This allows off-main-thread use without - // a JSContext. - PinnedUnits(JSContext* maybeCx, ScriptSource* source, + PinnedUnits(JSContext* cx, ScriptSource* source, UncompressedSourceCache::AutoHoldEntry& holder, size_t begin, size_t len); @@ -623,13 +615,8 @@ class ScriptSource { // How many ids have been handed out to sources. static mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> idCount_; - // Decompress and return the specified chunk of source code. - // If maybeCx is nullptr, decompression still works but the uncompressed - // result will not be cached. This allows off-main-thread callers to - // decompress source without a JSContext, at the cost of potentially - // decompressing the same chunk multiple times. template <typename Unit> - const Unit* chunkUnits(JSContext* maybeCx, + const Unit* chunkUnits(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder, size_t chunk); @@ -638,13 +625,9 @@ class ScriptSource { // // Warning: this is *not* GC-safe! Any chars to be handed out must use // PinnedUnits. See comment below. - // - // If maybeCx is nullptr, compressed sources will still be decompressed but - // the result will not be cached. See chunkUnits comment above. template <typename Unit> - const Unit* units(JSContext* maybeCx, - UncompressedSourceCache::AutoHoldEntry& asp, size_t begin, - size_t len); + const Unit* units(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp, + size_t begin, size_t len); template <typename Unit> const Unit* uncompressedUnits(size_t begin, size_t len); @@ -680,9 +663,7 @@ class ScriptSource { UniqueTwoByteChars&& str); private: - class LoadSourceMatcherBase; class LoadSourceMatcher; - class SourcePropertiesGetter; public: // Attempt to load usable source for |ss| -- source text on which substring @@ -691,16 +672,6 @@ class ScriptSource { // return false. static bool loadSource(JSContext* cx, ScriptSource* ss, bool* loaded); - // This is similar to loadSource, but it is designed to be used outside of the - // main thread. This is done by removing the need of JSContext for the - // Retrievable sources that require sourceHook. For retrievable cases, it - // sets retrievable to true and sets the isUT16 depending on the encoding. - // - // *loaded indicates whether source text is available, *retrievable indicates - // whether the source can be retrieved later via source hook. - static void getSourceProperties(ScriptSource* ss, bool* hasSourceText, - bool* retrievable); - // Assign source data from |srcBuf| to this recently-created |ScriptSource|. template <typename Unit> [[nodiscard]] bool assignSource(FrontendContext* fc, @@ -910,18 +881,6 @@ class ScriptSource { JSLinearString* substring(JSContext* cx, size_t start, size_t stop); JSLinearString* substringDontDeflate(JSContext* cx, size_t start, size_t stop); - // Get substring characters without creating a JSString. Returns a variant - // containing either UniqueChars (UTF-8) or UniqueTwoByteChars (UTF-16). - // - // IMPORTANT: The returned buffer is NOT null-terminated. Callers must track - // the length separately (stop - start). This is designed for consumers that - // store length explicitly (e.g., ProfilerJSSourceData). - // - // Callers must handle empty sources before calling this (the function asserts - // non-empty length). Returns nullptr only on allocation failures. Designed - // for off-main-thread use where JSContext is not available for error - // reporting. - SubstringCharsResult substringChars(size_t start, size_t stop); [[nodiscard]] bool appendSubstring(JSContext* cx, js::StringBuilder& buf, size_t start, size_t stop); @@ -930,27 +889,9 @@ class ScriptSource { parameterListEnd_ = parameterListEnd; } - bool isFunctionBody() const { return parameterListEnd_ != 0; } + bool isFunctionBody() { return parameterListEnd_ != 0; } JSLinearString* functionBodyString(JSContext* cx); - // Returns the function body substring. Unlike substringChars, this can return - // an empty result (nullptr with *outLength == 0) for empty function bodies. - // The caller doesn't need to check the length before calling. - SubstringCharsResult functionBodyStringChars(size_t* outLength); - - // Returns true if this source should display only the function body. - // In case of DOM event handler like <div onclick="foo()" the JS code is - // wrapped into - // function onclick() {foo()} - // We want to only return `foo()` here. - // But only for event handlers, for `new Function("foo()")`, we want to - // return: - // function anonymous() {foo()} - bool shouldUnwrapEventHandlerBody() const { - return hasIntroductionType() && - strcmp(introductionType(), "eventHandler") == 0 && isFunctionBody(); - } - void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ScriptSourceInfo* info) const; diff --git a/js/src/vm/MutexIDs.h b/js/src/vm/MutexIDs.h @@ -36,6 +36,7 @@ _(WasmInitBuiltinThunks, 451) \ _(StringsCache, 500) \ _(FutexThread, 500) \ + _(GeckoProfilerStrings, 500) \ _(ProtectedRegionTree, 500) \ _(ShellOffThreadState, 500) \ _(ShellStreamCacheEntryState, 500) \ @@ -61,7 +62,6 @@ _(SourceCompression, 500) \ _(GCDelayedMarkingLock, 500) \ _(BufferAllocator, 500) \ - _(GeckoProfilerScriptSources, 500) \ \ _(SharedImmutableStringsCache, 600) \ _(IrregexpLazyStatic, 600) \ diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp @@ -667,7 +667,6 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( frame.interpreterScript = nullptr; // TODO: get the realm ID of wasm frames. Bug 1596235. frame.realmID = 0; - frame.sourceId = 0; return mozilla::Some(frame); } @@ -719,9 +718,8 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( frame.stackAddress = stackAddr; if ((*entry)->isBaselineInterpreter()) { frame.label = jsJitIter().baselineInterpreterLabel(); - jsJitIter().baselineInterpreterScriptPC(&frame.interpreterScript, - &frame.interpreterPC_, - &frame.realmID, &frame.sourceId); + jsJitIter().baselineInterpreterScriptPC( + &frame.interpreterScript, &frame.interpreterPC_, &frame.realmID); MOZ_ASSERT(frame.interpreterScript); MOZ_ASSERT(frame.interpreterPC_); } else { @@ -729,7 +727,6 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( frame.returnAddress_ = returnAddr; frame.label = nullptr; frame.realmID = 0; - frame.sourceId = 0; } frame.activation = activation_; frame.endStackAddress = endStackAddress_; @@ -764,10 +761,9 @@ uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames, // Extract the stack for the entry. Assume maximum inlining depth is <64 const char* labels[64]; - uint32_t sourceIds[64]; uint32_t depth = entry->callStackAtAddr(cx_->runtime(), jsJitIter().resumePCinCurrentFrame(), - labels, sourceIds, std::size(labels)); + labels, std::size(labels)); MOZ_ASSERT(depth < std::size(labels)); for (uint32_t i = 0; i < depth; i++) { if (offset + i >= end) { @@ -775,7 +771,6 @@ uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames, } frames[offset + i] = physicalFrame.value(); frames[offset + i].label = labels[i]; - frames[offset + i].sourceId = sourceIds[i]; } return depth; diff --git a/mozglue/baseprofiler/core/ProfileBuffer.cpp b/mozglue/baseprofiler/core/ProfileBuffer.cpp @@ -58,8 +58,8 @@ uint64_t ProfileBuffer::AddThreadIdEntry(BaseProfilerThreadId aThreadId) { void ProfileBuffer::CollectCodeLocation( const char* aLabel, const char* aStr, uint32_t aFrameFlags, - uint64_t aInnerWindowID, uint32_t aSourceId, - const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, + uint64_t aInnerWindowID, const Maybe<uint32_t>& aLineNumber, + const Maybe<uint32_t>& aColumnNumber, const Maybe<ProfilingCategoryPair>& aCategoryPair) { AddEntry(ProfileBufferEntry::Label(aLabel)); AddEntry(ProfileBufferEntry::FrameFlags(uint64_t(aFrameFlags))); @@ -105,10 +105,6 @@ void ProfileBuffer::CollectCodeLocation( AddEntry(ProfileBufferEntry::InnerWindowID(aInnerWindowID)); } - if (aSourceId) { - AddEntry(ProfileBufferEntry::SourceId(aSourceId)); - } - if (aLineNumber) { AddEntry(ProfileBufferEntry::LineNumber(*aLineNumber)); } @@ -213,7 +209,7 @@ void ProfileBufferCollector::CollectProfilingStackFrame( MOZ_ASSERT(aFrame.isLabelFrame()); mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), - aFrame.realmID(), 0, line, column, + aFrame.realmID(), line, column, Some(aFrame.categoryPair())); } diff --git a/mozglue/baseprofiler/core/ProfileBuffer.h b/mozglue/baseprofiler/core/ProfileBuffer.h @@ -42,7 +42,6 @@ class ProfileBuffer final { void CollectCodeLocation(const char* aLabel, const char* aStr, uint32_t aFrameFlags, uint64_t aInnerWindowID, - uint32_t aSourceId, const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, const Maybe<ProfilingCategoryPair>& aCategoryPair); diff --git a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp @@ -63,11 +63,6 @@ ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uint64_t aUint64) memcpy(mStorage, &aUint64, sizeof(aUint64)); } -ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uint32_t aUint32) - : mKind(aKind) { - memcpy(mStorage, &aUint32, sizeof(aUint32)); -} - ProfileBufferEntry::ProfileBufferEntry(Kind aKind, BaseProfilerThreadId aThreadId) : mKind(aKind) { diff --git a/mozglue/baseprofiler/core/ProfileBufferEntry.h b/mozglue/baseprofiler/core/ProfileBufferEntry.h @@ -42,7 +42,6 @@ class ProfileBufferEntry { ProfileBufferEntry(Kind aKind, double aDouble); ProfileBufferEntry(Kind aKind, int64_t aInt64); ProfileBufferEntry(Kind aKind, uint64_t aUint64); - ProfileBufferEntry(Kind aKind, uint32_t aUint32); ProfileBufferEntry(Kind aKind, int aInt); ProfileBufferEntry(Kind aKind, BaseProfilerThreadId aThreadId); diff --git a/mozglue/baseprofiler/public/BaseAndGeckoProfilerDetail.h b/mozglue/baseprofiler/public/BaseAndGeckoProfilerDetail.h @@ -24,7 +24,7 @@ namespace mozilla { class ProfileBufferChunkManagerWithLocalLimit; // Centrally defines the version of the gecko profiler JSON format. -const int GECKO_PROFILER_FORMAT_VERSION = 32; +const int GECKO_PROFILER_FORMAT_VERSION = 31; namespace baseprofiler::detail { diff --git a/mozglue/baseprofiler/public/BaseProfilerState.h b/mozglue/baseprofiler/public/BaseProfilerState.h @@ -254,10 +254,7 @@ class MOZ_RAII AutoProfilerStats { \ MACRO(27, "flows", Flows, \ "Include all flow-related markers. These markers show the program" \ - "better but can cause more overhead in some places than normal.") \ - \ - MACRO(28, "jssources", JSSources, \ - "Collect JavaScript source code information for profiled scripts.") + "better but can cause more overhead in some places than normal.") // *** Synchronize with lists in ProfilerState.h and geckoProfiler.json *** diff --git a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h @@ -29,7 +29,6 @@ static constexpr size_t ProfileBufferEntryNumChars = 8; MACRO(DynamicStringFragment, char*, ProfileBufferEntryNumChars) \ MACRO(JitReturnAddr, void*, sizeof(void*)) \ MACRO(InnerWindowID, uint64_t, sizeof(uint64_t)) \ - MACRO(SourceId, uint32_t, sizeof(uint32_t)) \ MACRO(LineNumber, int, sizeof(int)) \ MACRO(ColumnNumber, int, sizeof(int)) \ MACRO(NativeLeafAddr, void*, sizeof(void*)) \ diff --git a/toolkit/components/extensions/schemas/geckoProfiler.json b/toolkit/components/extensions/schemas/geckoProfiler.json @@ -51,8 +51,7 @@ "memory", "tracing", "sandbox", - "flows", - "jssources" + "flows" ] }, { diff --git a/tools/profiler/core/ProfileAdditionalInformation.cpp b/tools/profiler/core/ProfileAdditionalInformation.cpp @@ -10,39 +10,12 @@ #include "js/JSON.h" #include "js/PropertyAndElement.h" #include "js/Value.h" -#include "mozilla/Assertions.h" #include "mozilla/JSONStringWriteFuncs.h" #include "mozilla/ipc/IPDLParamTraits.h" #ifdef MOZ_GECKO_PROFILER # include "platform.h" -JSString* -mozilla::ProfileGenerationAdditionalInformation::CreateJSStringFromSourceData( - JSContext* aCx, const ProfilerJSSourceData& aSourceData) const { - return aSourceData.data().match( - [&](const ProfilerJSSourceData::SourceTextUTF16& srcText) -> JSString* { - return JS_NewUCStringCopyN(aCx, srcText.chars().get(), - srcText.length()); - }, - [&](const ProfilerJSSourceData::SourceTextUTF8& srcText) -> JSString* { - return JS_NewStringCopyN(aCx, srcText.chars().get(), srcText.length()); - }, - [&](const ProfilerJSSourceData::RetrievableFile&) -> JSString* { - ProfilerJSSourceData retrievedData = - js::RetrieveProfilerSourceContent(aCx, aSourceData.filePath()); - const auto& data = retrievedData.data(); - MOZ_RELEASE_ASSERT(data.is<ProfilerJSSourceData::SourceTextUTF8>(), - "Retrieved JS source has to be utf-8"); - - const auto& srcText = data.as<ProfilerJSSourceData::SourceTextUTF8>(); - return JS_NewStringCopyN(aCx, srcText.chars().get(), srcText.length()); - }, - [&](const ProfilerJSSourceData::Unavailable&) -> JSString* { - return JS_NewStringCopyZ(aCx, "[unavailable]"); - }); -} - void mozilla::ProfileGenerationAdditionalInformation::ToJSValue( JSContext* aCx, JS::MutableHandle<JS::Value> aRetVal) const { // Get the shared libraries array. @@ -59,24 +32,8 @@ void mozilla::ProfileGenerationAdditionalInformation::ToJSValue( buffer16.Length(), &sharedLibrariesVal)); } - // Create jsSources object, which is UUID to source text mapping for - // WebChannel. - JS::Rooted<JSObject*> jsSourcesObj(aCx, JS_NewPlainObject(aCx)); - if (jsSourcesObj) { - for (const auto& entry : mJSSourceEntries) { - JSString* sourceStr = CreateJSStringFromSourceData(aCx, entry.sourceData); - if (sourceStr) { - JS::Rooted<JS::Value> sourceVal(aCx, JS::StringValue(sourceStr)); - JS_SetProperty(aCx, jsSourcesObj, PromiseFlatCString(entry.uuid).get(), - sourceVal); - } - } - } - JS::Rooted<JSObject*> additionalInfoObj(aCx, JS_NewPlainObject(aCx)); - JS::Rooted<JS::Value> jsSourcesVal(aCx, JS::ObjectValue(*jsSourcesObj)); JS_SetProperty(aCx, additionalInfoObj, "sharedLibraries", sharedLibrariesVal); - JS_SetProperty(aCx, additionalInfoObj, "jsSources", jsSourcesVal); aRetVal.setObject(*additionalInfoObj); } #endif // MOZ_GECKO_PROFILER @@ -99,22 +56,6 @@ struct ParamTraits<SharedLibraryInfo> { static bool Read(MessageReader* aReader, paramType* aResult); }; -template <> -struct ParamTraits<ProfilerJSSourceData> { - typedef ProfilerJSSourceData paramType; - - static void Write(MessageWriter* aWriter, const paramType& aParam); - static bool Read(MessageReader* aReader, paramType* aResult); -}; - -template <> -struct ParamTraits<mozilla::JSSourceEntry> { - typedef mozilla::JSSourceEntry paramType; - - static void Write(MessageWriter* aWriter, const paramType& aParam); - static bool Read(MessageReader* aReader, paramType* aResult); -}; - void IPC::ParamTraits<SharedLibrary>::Write(MessageWriter* aWriter, const paramType& aParam) { WriteParam(aWriter, aParam.mStart); @@ -156,170 +97,14 @@ bool IPC::ParamTraits<SharedLibraryInfo>::Read(MessageReader* aReader, return ReadParam(aReader, &aResult->mEntries); } -// Type tags for ProfilerJSSourceData IPC serialization -constexpr uint8_t kSourceTextUTF16Tag = 0; -constexpr uint8_t kSourceTextUTF8Tag = 1; -constexpr uint8_t kRetrievableFileTag = 2; -constexpr uint8_t kUnavailableTag = 3; - -void IPC::ParamTraits<ProfilerJSSourceData>::Write(MessageWriter* aWriter, - const paramType& aParam) { - // Write sourceId and filePath first - WriteParam(aWriter, aParam.sourceId()); - WriteParam(aWriter, aParam.filePathLength()); - if (aParam.filePathLength() > 0) { - aWriter->WriteBytes(aParam.filePath(), - aParam.filePathLength() * sizeof(char)); - } - - // Then write the specific data type - aParam.data().match( - [&](const ProfilerJSSourceData::SourceTextUTF16& srcText) { - WriteParam(aWriter, kSourceTextUTF16Tag); - WriteParam(aWriter, srcText.length()); - if (srcText.length() > 0) { - aWriter->WriteBytes(srcText.chars().get(), - srcText.length() * sizeof(char16_t)); - } - }, - [&](const ProfilerJSSourceData::SourceTextUTF8& srcText) { - WriteParam(aWriter, kSourceTextUTF8Tag); - WriteParam(aWriter, srcText.length()); - if (srcText.length() > 0) { - aWriter->WriteBytes(srcText.chars().get(), - srcText.length() * sizeof(char)); - } - }, - [&](const ProfilerJSSourceData::RetrievableFile&) { - WriteParam(aWriter, kRetrievableFileTag); - }, - [&](const ProfilerJSSourceData::Unavailable&) { - WriteParam(aWriter, kUnavailableTag); - }); -} - -bool IPC::ParamTraits<ProfilerJSSourceData>::Read(MessageReader* aReader, - paramType* aResult) { - // Read sourceId and filePath first - uint32_t sourceId; - size_t pathLength; - if (!ReadParam(aReader, &sourceId) || !ReadParam(aReader, &pathLength)) { - return false; - } - - // Read filePath if present - JS::UniqueChars filePath; - if (pathLength > 0) { - char* chars = - static_cast<char*>(js_malloc((pathLength + 1) * sizeof(char))); - if (!chars || !aReader->ReadBytesInto(chars, pathLength * sizeof(char))) { - js_free(chars); - return false; - } - chars[pathLength] = '\0'; - filePath.reset(chars); - } - - // Then read the specific data type - uint8_t typeTag; - if (!ReadParam(aReader, &typeTag)) { - return false; - } - - switch (typeTag) { - case kSourceTextUTF16Tag: { - size_t length; - if (!ReadParam(aReader, &length)) { - return false; - } - if (length > 0) { - // Allocate one extra element for null terminator - char16_t* chars = - static_cast<char16_t*>(js_malloc((length + 1) * sizeof(char16_t))); - if (!chars || - !aReader->ReadBytesInto(chars, length * sizeof(char16_t))) { - js_free(chars); - return false; - } - // Ensure null termination - chars[length] = u'\0'; - *aResult = - ProfilerJSSourceData(sourceId, JS::UniqueTwoByteChars(chars), - length, std::move(filePath), pathLength); - } else { - *aResult = ProfilerJSSourceData(sourceId, JS::UniqueTwoByteChars(), 0, - std::move(filePath), pathLength); - } - return true; - } - case kSourceTextUTF8Tag: { - size_t length; - if (!ReadParam(aReader, &length)) { - return false; - } - if (length > 0) { - // Allocate one extra byte for null terminator - char* chars = - static_cast<char*>(js_malloc((length + 1) * sizeof(char))); - if (!chars || !aReader->ReadBytesInto(chars, length * sizeof(char))) { - js_free(chars); - return false; - } - // Ensure null termination - chars[length] = '\0'; - *aResult = - ProfilerJSSourceData(sourceId, JS::UniqueChars(chars), length, - std::move(filePath), pathLength); - } else { - *aResult = ProfilerJSSourceData(sourceId, JS::UniqueChars(), 0, - std::move(filePath), pathLength); - } - return true; - } - case kRetrievableFileTag: { - *aResult = ProfilerJSSourceData::CreateRetrievableFile( - sourceId, std::move(filePath), pathLength); - return true; - } - case kUnavailableTag: { - *aResult = - ProfilerJSSourceData(sourceId, std::move(filePath), pathLength); - return true; - } - default: - return false; - } -} - -void IPC::ParamTraits<mozilla::JSSourceEntry>::Write(MessageWriter* aWriter, - const paramType& aParam) { - WriteParam(aWriter, aParam.uuid); - WriteParam(aWriter, aParam.sourceData); -} - -bool IPC::ParamTraits<mozilla::JSSourceEntry>::Read(MessageReader* aReader, - paramType* aResult) { - return (ReadParam(aReader, &aResult->uuid) && - ReadParam(aReader, &aResult->sourceData)); -} - void IPC::ParamTraits<mozilla::ProfileGenerationAdditionalInformation>::Write( MessageWriter* aWriter, const paramType& aParam) { WriteParam(aWriter, aParam.mSharedLibraries); - WriteParam(aWriter, aParam.mJSSourceEntries); } bool IPC::ParamTraits<mozilla::ProfileGenerationAdditionalInformation>::Read( MessageReader* aReader, paramType* aResult) { - if (!ReadParam(aReader, &aResult->mSharedLibraries)) { - return false; - } - - if (!ReadParam(aReader, &aResult->mJSSourceEntries)) { - return false; - } - - return true; + return ReadParam(aReader, &aResult->mSharedLibraries); } } // namespace IPC diff --git a/tools/profiler/core/ProfileBuffer.cpp b/tools/profiler/core/ProfileBuffer.cpp @@ -59,8 +59,8 @@ uint64_t ProfileBuffer::AddThreadIdEntry(ProfilerThreadId aThreadId) { void ProfileBuffer::CollectCodeLocation( const char* aLabel, const char* aStr, uint32_t aFrameFlags, - uint64_t aInnerWindowID, uint32_t aSourceId, - const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, + uint64_t aInnerWindowID, const Maybe<uint32_t>& aLineNumber, + const Maybe<uint32_t>& aColumnNumber, const Maybe<JS::ProfilingCategoryPair>& aCategoryPair) { AddEntry(ProfileBufferEntry::Label(aLabel)); AddEntry(ProfileBufferEntry::FrameFlags(uint64_t(aFrameFlags))); @@ -106,10 +106,6 @@ void ProfileBuffer::CollectCodeLocation( AddEntry(ProfileBufferEntry::InnerWindowID(aInnerWindowID)); } - if (aSourceId) { - AddEntry(ProfileBufferEntry::SourceId(aSourceId)); - } - if (aLineNumber) { AddEntry(ProfileBufferEntry::LineNumber(*aLineNumber)); } @@ -199,7 +195,7 @@ void ProfileBufferCollector::CollectJitReturnAddr(void* aAddr) { void ProfileBufferCollector::CollectWasmFrame( JS::ProfilingCategoryPair aCategory, const char* aLabel) { - mBuf.CollectCodeLocation("", aLabel, 0, 0, 0, Nothing(), Nothing(), + mBuf.CollectCodeLocation("", aLabel, 0, 0, Nothing(), Nothing(), Some(aCategory)); } @@ -214,7 +210,6 @@ void ProfileBufferCollector::CollectProfilingStackFrame( const char* dynamicString = aFrame.dynamicString(); Maybe<uint32_t> line; Maybe<uint32_t> column; - uint32_t sourceId = aFrame.sourceId(); if (aFrame.isJsFrame()) { // There are two kinds of JS frames that get pushed onto the ProfilingStack. @@ -245,6 +240,6 @@ void ProfileBufferCollector::CollectProfilingStackFrame( } mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), - aFrame.realmID(), sourceId, line, column, + aFrame.realmID(), line, column, Some(aFrame.categoryPair())); } diff --git a/tools/profiler/core/ProfileBuffer.h b/tools/profiler/core/ProfileBuffer.h @@ -13,13 +13,10 @@ #include "mozilla/PowerOfTwo.h" #include "mozilla/ProfileBufferChunkManagerSingle.h" #include "mozilla/ProfileChunkedBuffer.h" -#include "nsTHashMap.h" class ProcessStreamingContext; class RunningTimes; -struct ProfilerJSSourceData; - // Class storing most profiling data in a ProfileChunkedBuffer. // // This class is used as a queue of entries which, after construction, never @@ -46,8 +43,7 @@ class ProfileBuffer final { void CollectCodeLocation( const char* aLabel, const char* aStr, uint32_t aFrameFlags, - uint64_t aInnerWindowID, uint32_t aSourceId, - const mozilla::Maybe<uint32_t>& aLineNumber, + uint64_t aInnerWindowID, const mozilla::Maybe<uint32_t>& aLineNumber, const mozilla::Maybe<uint32_t>& aColumnNumber, const mozilla::Maybe<JS::ProfilingCategoryPair>& aCategoryPair); @@ -59,9 +55,7 @@ class ProfileBuffer final { // for the given thread. void AddJITInfoForRange(uint64_t aRangeStart, ProfilerThreadId aThreadId, JSContext* aContext, JITFrameInfo& aJITFrameInfo, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* - aSourceIdToIndexMap = nullptr) const; + mozilla::ProgressLogger aProgressLogger) const; // Stream JSON for samples in the buffer to aWriter, using the supplied // UniqueStacks object. @@ -102,12 +96,6 @@ class ProfileBuffer final { double aSinceTime, mozilla::ProgressLogger aProgressLogger) const; - // Stream JavaScript source table to JSON and return mapping from sourceId - // to index into source table. - nsTHashMap<SourceId, IndexIntoSourceTable> StreamSourceTableToJSON( - SpliceableJSONWriter& aWriter, - const nsTArray<mozilla::JSSourceEntry>& aJSSourceEntries) const; - // Find (via |aLastSample|) the most recent sample for the thread denoted by // |aThreadId| and clone it, patching in the current time as appropriate. // Mutate |aLastSample| to point to the newly inserted sample. diff --git a/tools/profiler/core/ProfileBufferEntry.cpp b/tools/profiler/core/ProfileBufferEntry.cpp @@ -75,11 +75,6 @@ ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uint64_t aUint64) memcpy(mStorage, &aUint64, sizeof(aUint64)); } -ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uint32_t aUint32) - : mKind(aKind) { - memcpy(mStorage, &aUint32, sizeof(aUint32)); -} - ProfileBufferEntry::ProfileBufferEntry(Kind aKind, ProfilerThreadId aThreadId) : mKind(aKind) { static_assert(std::is_trivially_copyable_v<ProfilerThreadId>); @@ -123,12 +118,6 @@ uint64_t ProfileBufferEntry::GetUint64() const { return result; } -uint32_t ProfileBufferEntry::GetUint32() const { - uint32_t result; - memcpy(&result, mStorage, sizeof(result)); - return result; -} - ProfilerThreadId ProfileBufferEntry::GetThreadId() const { ProfilerThreadId result; static_assert(std::is_trivially_copyable_v<ProfilerThreadId>); @@ -332,8 +321,7 @@ bool UniqueStacks::FrameKey::NormalFrameData::operator==( return mLocation == aOther.mLocation && mRelevantForJS == aOther.mRelevantForJS && mBaselineInterp == aOther.mBaselineInterp && - mInnerWindowID == aOther.mInnerWindowID && - mSourceId == aOther.mSourceId && mLine == aOther.mLine && + mInnerWindowID == aOther.mInnerWindowID && mLine == aOther.mLine && mColumn == aOther.mColumn && mCategoryPair == aOther.mCategoryPair; } @@ -349,17 +337,14 @@ bool UniqueStacks::FrameKey::JITFrameData::operator==( // strings at the same indices. UniqueStacks::UniqueStacks( FailureLatch& aFailureLatch, JITFrameInfo&& aJITFrameInfo, - ProfilerCodeAddressService* aCodeAddressService /* = nullptr */, - const nsTHashMap<SourceId, IndexIntoSourceTable>* - aSourceIdToIndexMap /* = nullptr */) + ProfilerCodeAddressService* aCodeAddressService /* = nullptr */) : mUniqueStrings(std::move(aJITFrameInfo) .MoveUniqueStringsWithNewFailureLatch(aFailureLatch)), mCodeAddressService(aCodeAddressService), mFrameTableWriter(aFailureLatch), mStackTableWriter(aFailureLatch), mJITInfoRanges(std::move(aJITFrameInfo) - .MoveRangesWithNewFailureLatch(aFailureLatch)), - mSourceIdToIndexMap(aSourceIdToIndexMap) { + .MoveRangesWithNewFailureLatch(aFailureLatch)) { if (!mUniqueStrings) { SetFailure("Did not get mUniqueStrings from JITFrameInfo"); return; @@ -519,8 +504,7 @@ void UniqueStacks::StreamNonJITFrame(const FrameKey& aFrame) { AutoArraySchemaWithStringsWriter writer(mFrameTableWriter, *mUniqueStrings); const NormalFrameData& data = aFrame.mData.as<NormalFrameData>(); - writer.StringElement(LOCATION, - data.GetLocationWithSourceIndex(mSourceIdToIndexMap)); + writer.StringElement(LOCATION, data.mLocation); writer.BoolElement(RELEVANT_FOR_JS, data.mRelevantForJS); // It's okay to convert uint64_t to double here because DOM always creates IDs @@ -549,9 +533,7 @@ void UniqueStacks::StreamNonJITFrame(const FrameKey& aFrame) { static void StreamJITFrame(JSContext* aContext, SpliceableJSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings, - const JS::ProfiledFrameHandle& aJITFrame, - const nsTHashMap<SourceId, IndexIntoSourceTable>* - aSourceIdToIndexMap = nullptr) { + const JS::ProfiledFrameHandle& aJITFrame) { enum Schema : uint32_t { LOCATION = 0, RELEVANT_FOR_JS = 1, @@ -565,18 +547,7 @@ static void StreamJITFrame(JSContext* aContext, SpliceableJSONWriter& aWriter, AutoArraySchemaWithStringsWriter writer(aWriter, aUniqueStrings); - uint32_t sourceId = aJITFrame.sourceId(); - nsCString labelWithSourceIndex(aJITFrame.label()); - if (sourceId && aSourceIdToIndexMap) { - auto index = aSourceIdToIndexMap->MaybeGet(sourceId); - if (index) { - labelWithSourceIndex.AppendLiteral("["); - labelWithSourceIndex.AppendInt(*index); - labelWithSourceIndex.AppendLiteral("]"); - } - } - writer.StringElement(LOCATION, labelWithSourceIndex); - + writer.StringElement(LOCATION, MakeStringSpan(aJITFrame.label())); writer.BoolElement(RELEVANT_FOR_JS, false); // It's okay to convert uint64_t to double here because DOM always creates IDs @@ -600,24 +571,20 @@ static void StreamJITFrame(JSContext* aContext, SpliceableJSONWriter& aWriter, writer.IntElement(SUBCATEGORY, info.mSubcategoryIndex); } -static nsCString JSONForJITFrame( - JSContext* aContext, const JS::ProfiledFrameHandle& aJITFrame, - UniqueJSONStrings& aUniqueStrings, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap = - nullptr) { +static nsCString JSONForJITFrame(JSContext* aContext, + const JS::ProfiledFrameHandle& aJITFrame, + UniqueJSONStrings& aUniqueStrings) { nsCString json; JSONStringRefWriteFunc jw(json); SpliceableJSONWriter writer(jw, aUniqueStrings.SourceFailureLatch()); - StreamJITFrame(aContext, writer, aUniqueStrings, aJITFrame, - aSourceIdToIndexMap); + StreamJITFrame(aContext, writer, aUniqueStrings, aJITFrame); return json; } void JITFrameInfo::AddInfoForRange( uint64_t aRangeStart, uint64_t aRangeEnd, JSContext* aCx, const std::function<void(const std::function<void(void*)>&)>& - aJITAddressProvider, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) { + aJITAddressProvider) { if (mLocalFailureLatchSource.Failed()) { return; } @@ -652,8 +619,7 @@ void JITFrameInfo::AddInfoForRange( if (!frameEntry) { if (!jitFrameToFrameJSONMap.add( frameEntry, jitFrameKey, - JSONForJITFrame(aCx, handle, *mUniqueStrings, - aSourceIdToIndexMap))) { + JSONForJITFrame(aCx, handle, *mUniqueStrings))) { mLocalFailureLatchSource.SetFailure( "OOM in JITFrameInfo::AddInfoForRange adding jit->frame map"); return; @@ -1177,10 +1143,7 @@ void ProfileBuffer::MaybeStreamExecutionTraceToJSON( } UniqueStacks::FrameKey newFrame(nsCString(name.get()), true, false, - event.functionEvent.realmID, - // Even though it says scriptId, this is - // actually sourceId. See bug 1980369. - event.functionEvent.scriptId, Nothing{}, + event.functionEvent.realmID, Nothing{}, Nothing{}, Some(categoryPair)); maybeStack = uniqueStacks.AppendFrame(stack, newFrame); if (!maybeStack) { @@ -1197,7 +1160,7 @@ void ProfileBuffer::MaybeStreamExecutionTraceToJSON( } else if (event.kind == JS::ExecutionTrace::EventKind::LabelEnter) { UniqueStacks::FrameKey newFrame( nsCString(&trace.stringBuffer[event.labelEvent.label]), true, false, - 0, 0, Nothing{}, Nothing{}, Some(JS::ProfilingCategoryPair::DOM)); + 0, Nothing{}, Nothing{}, Some(JS::ProfilingCategoryPair::DOM)); maybeStack = uniqueStacks.AppendFrame(stack, newFrame); if (!maybeStack) { writer.SetFailure("AppendFrame failure"); @@ -1446,12 +1409,6 @@ ProfilerThreadId ProfileBuffer::DoStreamSamplesAndMarkersToJSON( e.Next(); } - uint32_t sourceId = 0; - if (e.Has() && e.Get().IsSourceId()) { - sourceId = uint64_t(e.Get().GetUint32()); - e.Next(); - } - Maybe<unsigned> line; if (e.Has() && e.Get().IsLineNumber()) { line = Some(unsigned(e.Get().GetInt())); @@ -1474,8 +1431,8 @@ ProfilerThreadId ProfileBuffer::DoStreamSamplesAndMarkersToJSON( maybeStack = uniqueStacks.AppendFrame( stack, UniqueStacks::FrameKey(std::move(frameLabel), relevantForJS, - isBaselineInterp, innerWindowID, - sourceId, line, column, categoryPair)); + isBaselineInterp, innerWindowID, line, + column, categoryPair)); if (!maybeStack) { writer.SetFailure("AppendFrame failure"); return; @@ -1760,9 +1717,8 @@ void ProfileBuffer::StreamSamplesAndMarkersToJSON( void ProfileBuffer::AddJITInfoForRange( uint64_t aRangeStart, ProfilerThreadId aThreadId, JSContext* aContext, - JITFrameInfo& aJITFrameInfo, mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) - const { + JITFrameInfo& aJITFrameInfo, + mozilla::ProgressLogger aProgressLogger) const { // We can only process JitReturnAddr entries if we have a JSContext. MOZ_RELEASE_ASSERT(aContext); @@ -1856,8 +1812,7 @@ void ProfileBuffer::AddJITInfoForRange( } } }); - }, - aSourceIdToIndexMap); + }); } void ProfileBuffer::StreamMarkersToJSON( @@ -2557,53 +2512,5 @@ void ProfileBuffer::DiscardSamplesBeforeTime(double aTime) { (void)aTime; } -nsTHashMap<SourceId, IndexIntoSourceTable> -ProfileBuffer::StreamSourceTableToJSON( - SpliceableJSONWriter& aWriter, - const nsTArray<mozilla::JSSourceEntry>& aJSSourceEntries) const { - enum Schema : uint32_t { UUID = 0, FILENAME = 1 }; - nsTHashMap<SourceId, IndexIntoSourceTable> sourceIdToIndexMap; - - aWriter.StartObjectProperty("sources"); - { - // Write the schema - { - JSONSchemaWriter schema(aWriter); - schema.WriteField("uuid"); - schema.WriteField("filename"); - } - - // Write data array and build sourceId-to-index mapping - aWriter.StartArrayProperty("data"); - uint32_t index = 0; - for (const auto& entry : aJSSourceEntries) { - // Build sourceId-to-index mapping - if (entry.sourceData.sourceId() != 0) { - MOZ_ASSERT(!sourceIdToIndexMap.Contains(entry.sourceData.sourceId()), - "Duplicate sourceId detected! This indicates sourceId " - "collision between different sources."); - sourceIdToIndexMap.InsertOrUpdate(entry.sourceData.sourceId(), index); - } - - // Write [uuid, filename] entry - aWriter.StartArrayElement(); - { - // TODO: Use AutoArraySchemaWithStringsWriter to write string indexes - // into string table once we have "process global" string table. - // Currently string tables are per-thread. - aWriter.StringElement(MakeStringSpan(entry.uuid.get())); - aWriter.StringElement(MakeStringSpan(entry.sourceData.filePath())); - } - aWriter.EndArray(); - - index++; - } - aWriter.EndArray(); - } - aWriter.EndObject(); - - return sourceIdToIndexMap; -} - // END ProfileBuffer //////////////////////////////////////////////////////////////////////// diff --git a/tools/profiler/core/ProfileBufferEntry.h b/tools/profiler/core/ProfileBufferEntry.h @@ -25,17 +25,10 @@ #include "mozilla/Variant.h" #include "mozilla/Vector.h" #include "nsString.h" -#include "nsStringFwd.h" -#include "nsTHashMap.h" class ProfilerCodeAddressService; struct JSContext; -// Typedef for process-global identifiers for JS script sources. -using SourceId = uint32_t; -// Typedef for indexes into the source table array. -using IndexIntoSourceTable = uint32_t; - class ProfileBufferEntry { public: using KindUnderlyingType = @@ -54,7 +47,6 @@ class ProfileBufferEntry { ProfileBufferEntry(Kind aKind, double aDouble); ProfileBufferEntry(Kind aKind, int64_t aInt64); ProfileBufferEntry(Kind aKind, uint64_t aUint64); - ProfileBufferEntry(Kind aKind, uint32_t aUint32); ProfileBufferEntry(Kind aKind, int aInt); ProfileBufferEntry(Kind aKind, ProfilerThreadId aThreadId); @@ -90,7 +82,6 @@ class ProfileBufferEntry { int GetInt() const; int64_t GetInt64() const; uint64_t GetUint64() const; - uint32_t GetUint32() const; ProfilerThreadId GetThreadId() const; void CopyCharsInto(char (&aOutArray)[kNumChars]) const; }; @@ -177,9 +168,7 @@ class JITFrameInfo final { void AddInfoForRange( uint64_t aRangeStart, uint64_t aRangeEnd, JSContext* aCx, const std::function<void(const std::function<void(void*)>&)>& - aJITAddressProvider, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap = - nullptr); + aJITAddressProvider); // Returns whether the information stored in this object is still relevant // for any entries in the buffer. @@ -224,16 +213,15 @@ class UniqueStacks final : public mozilla::FailureLatch { public: struct FrameKey { explicit FrameKey(const char* aLocation) - : mData(NormalFrameData{nsCString(aLocation), false, false, 0, 0, + : mData(NormalFrameData{nsCString(aLocation), false, false, 0, mozilla::Nothing(), mozilla::Nothing()}) {} FrameKey(nsCString&& aLocation, bool aRelevantForJS, bool aBaselineInterp, - uint64_t aInnerWindowID, uint32_t aSourceId, - const mozilla::Maybe<unsigned>& aLine, + uint64_t aInnerWindowID, const mozilla::Maybe<unsigned>& aLine, const mozilla::Maybe<unsigned>& aColumn, const mozilla::Maybe<JS::ProfilingCategoryPair>& aCategoryPair) : mData(NormalFrameData{aLocation, aRelevantForJS, aBaselineInterp, - aInnerWindowID, aSourceId, aLine, aColumn, + aInnerWindowID, aLine, aColumn, aCategoryPair}) {} FrameKey(void* aJITAddress, uint32_t aJITDepth, uint32_t aRangeIndex) @@ -249,26 +237,10 @@ class UniqueStacks final : public mozilla::FailureLatch { struct NormalFrameData { bool operator==(const NormalFrameData& aOther) const; - nsCString GetLocationWithSourceIndex( - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) - const { - nsCString result = mLocation; - if (mSourceId && aSourceIdToIndexMap) { - auto index = aSourceIdToIndexMap->MaybeGet(mSourceId); - if (index) { - result.AppendLiteral("["); - result.AppendInt(*index); - result.AppendLiteral("]"); - } - } - return result; - } - nsCString mLocation; bool mRelevantForJS; bool mBaselineInterp; uint64_t mInnerWindowID; - uint32_t mSourceId; mozilla::Maybe<unsigned> mLine; mozilla::Maybe<unsigned> mColumn; mozilla::Maybe<JS::ProfilingCategoryPair> mCategoryPair; @@ -298,7 +270,6 @@ class UniqueStacks final : public mozilla::FailureLatch { hash = mozilla::AddToHash(hash, data.mRelevantForJS); hash = mozilla::AddToHash(hash, data.mBaselineInterp); hash = mozilla::AddToHash(hash, data.mInnerWindowID); - hash = mozilla::AddToHash(hash, data.mSourceId); if (data.mLine.isSome()) { hash = mozilla::AddToHash(hash, *data.mLine); } @@ -370,9 +341,7 @@ class UniqueStacks final : public mozilla::FailureLatch { UniqueStacks(mozilla::FailureLatch& aFailureLatch, JITFrameInfo&& aJITFrameInfo, - ProfilerCodeAddressService* aCodeAddressService = nullptr, - const nsTHashMap<SourceId, IndexIntoSourceTable>* - aSourceIdToIndexMap = nullptr); + ProfilerCodeAddressService* aCodeAddressService = nullptr); // Return a StackKey for aFrame as the stack's root frame (no prefix). [[nodiscard]] mozilla::Maybe<StackKey> BeginStack(const FrameKey& aFrame); @@ -427,8 +396,6 @@ class UniqueStacks final : public mozilla::FailureLatch { mozilla::HashMap<StackKey, uint32_t, StackKeyHasher> mStackToIndexMap; mozilla::Vector<JITFrameInfoForBufferRange> mJITInfoRanges; - - const nsTHashMap<SourceId, IndexIntoSourceTable>* mSourceIdToIndexMap; }; // diff --git a/tools/profiler/core/ProfiledThreadData.cpp b/tools/profiler/core/ProfiledThreadData.cpp @@ -104,8 +104,7 @@ mozilla::NotNull<mozilla::UniquePtr<UniqueStacks>> ProfiledThreadData::PrepareUniqueStacks( const ProfileBuffer& aBuffer, JSContext* aCx, mozilla::FailureLatch& aFailureLatch, ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) { + mozilla::ProgressLogger aProgressLogger) { if (mJITFrameInfoForPreviousJSContexts && mJITFrameInfoForPreviousJSContexts->HasExpired( aBuffer.BufferRangeStart())) { @@ -128,14 +127,13 @@ ProfiledThreadData::PrepareUniqueStacks( *mBufferPositionWhenReceivedJSContext, mThreadInfo.ThreadId(), aCx, jitFrameInfo, aProgressLogger.CreateSubLoggerTo("Adding JIT info...", 90_pc, - "Added JIT info"), - aSourceIdToIndexMap); + "Added JIT info")); } else { aProgressLogger.SetLocalProgress(90_pc, "No JIT info"); } return mozilla::MakeNotNull<mozilla::UniquePtr<UniqueStacks>>( - aFailureLatch, std::move(jitFrameInfo), aService, aSourceIdToIndexMap); + aFailureLatch, std::move(jitFrameInfo), aService); } void ProfiledThreadData::StreamJSON( @@ -395,8 +393,7 @@ ThreadStreamingContext::ThreadStreamingContext( ProfiledThreadData& aProfiledThreadData, const ProfileBuffer& aBuffer, JSContext* aCx, mozilla::FailureLatch& aFailureLatch, ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) + mozilla::ProgressLogger aProgressLogger) : mProfiledThreadData(aProfiledThreadData), mJSContext(aCx), mSamplesDataWriter(aFailureLatch), @@ -406,8 +403,7 @@ ThreadStreamingContext::ThreadStreamingContext( aBuffer, aCx, aFailureLatch, aService, aProgressLogger.CreateSubLoggerFromTo( 0_pc, "Preparing thread streaming context unique stacks...", - 99_pc, "Prepared thread streaming context Unique stacks"), - aSourceIdToIndexMap)) { + 99_pc, "Prepared thread streaming context Unique stacks"))) { if (aFailureLatch.Failed()) { return; } @@ -458,8 +454,7 @@ ProcessStreamingContext::~ProcessStreamingContext() { void ProcessStreamingContext::AddThreadStreamingContext( ProfiledThreadData& aProfiledThreadData, const ProfileBuffer& aBuffer, JSContext* aCx, ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) { + mozilla::ProgressLogger aProgressLogger) { if (mFailureLatch.Failed()) { return; } @@ -471,6 +466,5 @@ void ProcessStreamingContext::AddThreadStreamingContext( aProfiledThreadData, aBuffer, aCx, mFailureLatch, aService, aProgressLogger.CreateSubLoggerFromTo( 1_pc, "Prepared streaming thread id", 100_pc, - "Added thread streaming context"), - aSourceIdToIndexMap); + "Added thread streaming context")); } diff --git a/tools/profiler/core/ProfiledThreadData.h b/tools/profiler/core/ProfiledThreadData.h @@ -77,9 +77,7 @@ class ProfiledThreadData final { const ProfileBuffer& aBuffer, JSContext* aCx, mozilla::FailureLatch& aFailureLatch, ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap = - nullptr); + mozilla::ProgressLogger aProgressLogger); void StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx, SpliceableJSONWriter& aWriter, const nsACString& aProcessName, @@ -168,9 +166,7 @@ struct ThreadStreamingContext { const ProfileBuffer& aBuffer, JSContext* aCx, mozilla::FailureLatch& aFailureLatch, ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* - aSourceIdToIndexMap = nullptr); + mozilla::ProgressLogger aProgressLogger); void FinalizeWriter(); }; @@ -188,12 +184,10 @@ class ProcessStreamingContext final : public mozilla::FailureLatch { // Add the streaming context corresponding to each profiled thread. This // should be called exactly the number of times specified in the constructor. - void AddThreadStreamingContext( - ProfiledThreadData& aProfiledThreadData, const ProfileBuffer& aBuffer, - JSContext* aCx, ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger, - const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap = - nullptr); + void AddThreadStreamingContext(ProfiledThreadData& aProfiledThreadData, + const ProfileBuffer& aBuffer, JSContext* aCx, + ProfilerCodeAddressService* aService, + mozilla::ProgressLogger aProgressLogger); // Retrieve the ThreadStreamingContext for a given thread id. // Returns null if that thread id doesn't correspond to any profiled thread. diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp @@ -49,9 +49,6 @@ #include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" #include "mozilla/Perfetto.h" -#include "nsID.h" -#include "nsIDUtils.h" -#include "nsString.h" #include "nsCExternalHandlerService.h" #include "nsCOMPtr.h" #include "nsDebug.h" @@ -1600,44 +1597,6 @@ class ActivePS { return array; } - // Collect JS sources from the main thread, since script source storage is - // shared between all threads. - static nsTArray<mozilla::JSSourceEntry> GatherJSSources(PSLockRef aLock) { - nsTArray<mozilla::JSSourceEntry> jsSourceEntries; - if (!ProfilerFeature::HasJSSources(ActivePS::Features(aLock))) { - return jsSourceEntries; - } - - ThreadRegistry::LockedRegistry lockedRegistry; - ActivePS::ProfiledThreadList threads = - ActivePS::ProfiledThreads(lockedRegistry, aLock); - - // Get the JS context of the main thread. We don't need to get the - // JSContext of others because the script source storage is shared between - // threads. - auto* mainThread = - std::find_if(threads.begin(), threads.end(), [](const auto& thread) { - return thread.mProfiledThreadData->Info().IsMainThread(); - }); - - if (mainThread == threads.end() || !mainThread->mJSContext) { - return jsSourceEntries; - } - JSContext* jsContext = mainThread->mJSContext; - - js::ProfilerJSSources threadSources = - js::GetProfilerScriptSources(JS_GetRuntime(jsContext)); - - // Generate UUIDs and build mappings for each source - for (ProfilerJSSourceData& sourceData : threadSources) { - // Generate UUID for this source and store it in the global array. - jsSourceEntries.AppendElement(JSSourceEntry( - NSID_TrimBracketsASCII(nsID::GenerateUUID()), std::move(sourceData))); - } - - return jsSourceEntries; - } - static ProfiledThreadData* AddLiveProfiledThread( PSLockRef, UniquePtr<ProfiledThreadData>&& aProfiledThreadData) { MOZ_ASSERT(sInstance); @@ -2486,7 +2445,7 @@ static void MergeStacks( uint32_t(js::ProfilingStackFrame::Flags::IS_BLINTERP_FRAME); stackFrame.initJsFrame<JS::ProfilingCategoryPair::JS_BaselineInterpret, ExtraFlags>("", jsFrame.label, script, pc, - jsFrame.realmID, jsFrame.sourceId); + jsFrame.realmID); aCollector.CollectProfilingStackFrame(stackFrame); } else { MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion || @@ -3671,7 +3630,7 @@ static void CollectJavaThreadProfileData( nsCString frameNameString = frameName->ToCString(); auto categoryPair = InferJavaCategory(frameNameString); - aProfileBuffer.CollectCodeLocation("", frameNameString.get(), 0, 0, 0, + aProfileBuffer.CollectCodeLocation("", frameNameString.get(), 0, 0, Nothing(), Nothing(), Some(categoryPair)); } @@ -3869,19 +3828,6 @@ locked_profiler_stream_json_for_this_process( } SLOW_DOWN_FOR_TESTING(); - // Collect JS sources from the main thread, since script source storage is - // shared between all threads. - nsTArray<mozilla::JSSourceEntry> jsSourceEntries = - ActivePS::GatherJSSources(aLock); - - // If there are sources, stream the sources table that is shared between the - // threads, and get the UUID to index mappings needed for frame serialization. - Maybe<nsTHashMap<SourceId, IndexIntoSourceTable>> sourceIdToIndexMap; - if (!jsSourceEntries.IsEmpty()) { - sourceIdToIndexMap.emplace( - buffer.StreamSourceTableToJSON(aWriter, jsSourceEntries)); - } - // Lists the samples for each thread profile aWriter.StartArrayProperty("threads"); { @@ -3909,8 +3855,7 @@ locked_profiler_stream_json_for_this_process( MOZ_RELEASE_ASSERT(thread.mProfiledThreadData); processStreamingContext.AddThreadStreamingContext( *thread.mProfiledThreadData, buffer, thread.mJSContext, aService, - std::move(progressLogger), - sourceIdToIndexMap.isSome() ? sourceIdToIndexMap.ptr() : nullptr); + std::move(progressLogger)); if (aWriter.Failed()) { return Err(ProfilerError::JsonGenerationFailed); } @@ -4032,8 +3977,7 @@ locked_profiler_stream_json_for_this_process( } #endif // DEBUG - return ProfileGenerationAdditionalInformation{std::move(sharedLibraryInfo), - std::move(jsSourceEntries)}; + return ProfileGenerationAdditionalInformation{std::move(sharedLibraryInfo)}; } // Keep this internal function non-static, so it may be used by tests. @@ -6308,42 +6252,37 @@ void profiler_shutdown(IsFastShutdown aIsFastShutdown) { ThreadRegistration::UnregisterThread(); } -static ProfilerResult<ProfileGenerationAdditionalInformation> -WriteProfileToJSONWriter(SpliceableChunkedJSONWriter& aWriter, - double aSinceTime, bool aIsShuttingDown, - ProfilerCodeAddressService* aService, - mozilla::ProgressLogger aProgressLogger) { +static bool WriteProfileToJSONWriter(SpliceableChunkedJSONWriter& aWriter, + double aSinceTime, bool aIsShuttingDown, + ProfilerCodeAddressService* aService, + mozilla::ProgressLogger aProgressLogger) { LOG("WriteProfileToJSONWriter"); MOZ_RELEASE_ASSERT(CorePS::Exists()); aWriter.Start(); - auto rv = profiler_stream_json_for_this_process( - aWriter, aSinceTime, aIsShuttingDown, aService, - aProgressLogger.CreateSubLoggerFromTo( - 0_pc, - "WriteProfileToJSONWriter: " - "profiler_stream_json_for_this_process started", - 100_pc, - "WriteProfileToJSONWriter: " - "profiler_stream_json_for_this_process done")); + { + auto rv = profiler_stream_json_for_this_process( + aWriter, aSinceTime, aIsShuttingDown, aService, + aProgressLogger.CreateSubLoggerFromTo( + 0_pc, + "WriteProfileToJSONWriter: " + "profiler_stream_json_for_this_process started", + 100_pc, + "WriteProfileToJSONWriter: " + "profiler_stream_json_for_this_process done")); + + if (rv.isErr()) { + return false; + } - if (rv.isErr()) { - return rv; + // Don't include profiles from other processes because this is a + // synchronous function. + aWriter.StartArrayProperty("processes"); + aWriter.EndArray(); } - - // Don't include profiles from other processes because this is a - // synchronous function. - aWriter.StartArrayProperty("processes"); - aWriter.EndArray(); - aWriter.End(); - - if (aWriter.Failed()) { - return Err(ProfilerError::JsonGenerationFailed); - } - - return rv; + return !aWriter.Failed(); } void profiler_set_process_name(const nsACString& aProcessName, @@ -6366,16 +6305,14 @@ UniquePtr<char[]> profiler_get_profile(double aSinceTime, FailureLatchSource failureLatch; SpliceableChunkedJSONWriter b{failureLatch}; - if (WriteProfileToJSONWriter(b, aSinceTime, aIsShuttingDown, service.get(), - ProgressLogger{}) - .isErr()) { + if (!WriteProfileToJSONWriter(b, aSinceTime, aIsShuttingDown, service.get(), + ProgressLogger{})) { return nullptr; } return b.ChunkedWriteFunc().CopyData(); } -[[nodiscard]] ProfilerResult<ProfileGenerationAdditionalInformation> -profiler_get_profile_json( +[[nodiscard]] bool profiler_get_profile_json( SpliceableChunkedJSONWriter& aSpliceableChunkedJSONWriter, double aSinceTime, bool aIsShuttingDown, mozilla::ProgressLogger aProgressLogger) { diff --git a/tools/profiler/core/platform.h b/tools/profiler/core/platform.h @@ -45,7 +45,6 @@ #include "mozilla/Vector.h" #include "nsString.h" #include "SharedLibraries.h" -#include "ProfileAdditionalInformation.h" #include <cstddef> #include <cstdint> @@ -159,8 +158,7 @@ void profiler_mark_thread_awake(); void profiler_mark_thread_asleep(); -[[nodiscard]] ProfilerResult<mozilla::ProfileGenerationAdditionalInformation> -profiler_get_profile_json( +[[nodiscard]] bool profiler_get_profile_json( SpliceableChunkedJSONWriter& aSpliceableChunkedJSONWriter, double aSinceTime, bool aIsShuttingDown, mozilla::ProgressLogger aProgressLogger); diff --git a/tools/profiler/gecko/ProfilerChild.cpp b/tools/profiler/gecko/ProfilerChild.cpp @@ -335,14 +335,13 @@ void ProfilerChild::GatherProfileThreadFunction( auto writer = MakeUnique<SpliceableChunkedJSONWriter>(parameters->failureLatchSource); - auto rv = - profiler_get_profile_json(*writer, - /* aSinceTime */ 0, - /* aIsShuttingDown */ false, - progressLogger.CreateSubLoggerFromTo( - 1_pc, "profiler_get_profile_json started", - 99_pc, "profiler_get_profile_json done")); - if (rv.isErr()) { + if (!profiler_get_profile_json( + *writer, + /* aSinceTime */ 0, + /* aIsShuttingDown */ false, + progressLogger.CreateSubLoggerFromTo( + 1_pc, "profiler_get_profile_json started", 99_pc, + "profiler_get_profile_json done"))) { // Failed to get a profile, reset the writer pointer, so that we'll send a // failure message. writer.reset(); @@ -356,7 +355,7 @@ void ProfilerChild::GatherProfileThreadFunction( // that it doesn't get marked as 100% done when this off-thread // function ends. progressLogger = std::move(progressLogger), - writer = std::move(writer), rv = std::move(rv)]() mutable { + writer = std::move(writer)]() mutable { // We are now on the ProfilerChild thread, about to send the // completed profile. Any incoming progress request will now be // handled after this task ends, so updating the progress is now @@ -425,10 +424,12 @@ void ProfilerChild::GatherProfileThreadFunction( } } - Maybe<ProfileGenerationAdditionalInformation> additionalInfo = - rv.isOk() ? Some(rv.unwrap()) : Nothing(); + SharedLibraryInfo sharedLibraryInfo = + SharedLibraryInfo::GetInfoForSelf(); parameters->resolver(IPCProfileAndAdditionalInformation{ - std::move(shmem), std::move(additionalInfo)}); + std::move(shmem), + Some(ProfileGenerationAdditionalInformation{ + std::move(sharedLibraryInfo)})}); // Let's join the gather profile thread now since it's done. // Note that this gets called inside the ProfilerChild thread // and not inside the gather profile thread itself. diff --git a/tools/profiler/gecko/ProfilerTypes.ipdlh b/tools/profiler/gecko/ProfilerTypes.ipdlh @@ -4,8 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; -[MoveOnly] using struct mozilla::ProfileGenerationAdditionalInformation from - "ProfileAdditionalInformation.h"; +using struct mozilla::ProfileGenerationAdditionalInformation from "ProfileAdditionalInformation.h"; namespace mozilla { diff --git a/tools/profiler/gecko/nsProfiler.cpp b/tools/profiler/gecko/nsProfiler.cpp @@ -1178,7 +1178,8 @@ RefPtr<nsProfiler::GatheringPromise> nsProfiler::StartGathering( } // Start building shared library info starting from the current process. - mProfileGenerationAdditionalInformation.emplace(); + mProfileGenerationAdditionalInformation.emplace( + SharedLibraryInfo::GetInfoForSelf()); // Request profiles from the other processes. This will trigger asynchronous // calls to ProfileGatherer::GatheredOOPProfile as the profiles arrive. @@ -1215,8 +1216,6 @@ RefPtr<nsProfiler::GatheringPromise> nsProfiler::StartGathering( return GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); } - mProfileGenerationAdditionalInformation->Append(rv.unwrap()); - LogEvent([&](Json::Value& aEvent) { aEvent.append( Json::StaticString{"Generated parent process profile, size:"}); diff --git a/tools/profiler/gecko/nsProfiler.h b/tools/profiler/gecko/nsProfiler.h @@ -52,7 +52,7 @@ class nsProfiler final : public nsIProfiler { mozilla::MozPromise<mozilla::void_t, nsresult, true>; using GatheringPromise = mozilla::MozPromise<mozilla::ProfileAndAdditionalInformation, nsresult, - true>; + false>; using SymbolTablePromise = mozilla::MozPromise<mozilla::SymbolTable, nsresult, true>; diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h @@ -29,8 +29,9 @@ #include "mozilla/ProfilerState.h" #include "mozilla/ProfilerThreadSleep.h" #include "mozilla/ProfilerThreadState.h" -#include "mozilla/ProfilerUtils.h" #include "mozilla/ProgressLogger.h" +#include "mozilla/Result.h" +#include "mozilla/ResultVariant.h" #ifndef MOZ_GECKO_PROFILER @@ -135,6 +136,14 @@ class SpliceableJSONWriter; } // namespace mozilla class nsIURI; +enum class ProfilerError { + IsInactive, + JsonGenerationFailed, +}; + +template <typename T> +using ProfilerResult = mozilla::Result<T, ProfilerError>; + //--------------------------------------------------------------------------- // Give information to the profiler //--------------------------------------------------------------------------- diff --git a/tools/profiler/public/ProfileAdditionalInformation.h b/tools/profiler/public/ProfileAdditionalInformation.h @@ -16,10 +16,7 @@ #include "SharedLibraries.h" #include "js/Value.h" -#include "js/Utility.h" -#include "js/ProfilingSources.h" #include "nsString.h" -#include "nsTArray.h" namespace IPC { class MessageReader; @@ -29,66 +26,25 @@ struct ParamTraits; } // namespace IPC namespace mozilla { - -// Entry pairing UUID strings with JS source data for WebChannel requests -struct JSSourceEntry { - nsCString uuid; - ProfilerJSSourceData sourceData; - - JSSourceEntry() = default; - JSSourceEntry(nsCString&& aUuid, ProfilerJSSourceData&& aSourceData) - : uuid(std::move(aUuid)), sourceData(std::move(aSourceData)) {} - - size_t SizeOf() const { return uuid.Length() + sourceData.SizeOf(); } -}; - // This structure contains additional information gathered while generating the // profile json and iterating the buffer. struct ProfileGenerationAdditionalInformation { ProfileGenerationAdditionalInformation() = default; explicit ProfileGenerationAdditionalInformation( - SharedLibraryInfo&& aSharedLibraries, - nsTArray<JSSourceEntry>&& aJSSourceEntries) - : mSharedLibraries(std::move(aSharedLibraries)), - mJSSourceEntries(std::move(aJSSourceEntries)) {} + SharedLibraryInfo&& aSharedLibraries) + : mSharedLibraries(std::move(aSharedLibraries)) {} - size_t SizeOf() const { - size_t size = mSharedLibraries.SizeOf(); - - for (const auto& entry : mJSSourceEntries) { - size += entry.SizeOf(); - } - - return size; - } - - ProfileGenerationAdditionalInformation( - const ProfileGenerationAdditionalInformation& other) = delete; - ProfileGenerationAdditionalInformation& operator=( - const ProfileGenerationAdditionalInformation&) = delete; - - ProfileGenerationAdditionalInformation( - ProfileGenerationAdditionalInformation&& other) = default; - ProfileGenerationAdditionalInformation& operator=( - ProfileGenerationAdditionalInformation&& other) = default; + size_t SizeOf() const { return mSharedLibraries.SizeOf(); } void Append(ProfileGenerationAdditionalInformation&& aOther) { mSharedLibraries.AddAllSharedLibraries(aOther.mSharedLibraries); - mJSSourceEntries.AppendElements(std::move(aOther.mJSSourceEntries)); } void FinishGathering() { mSharedLibraries.DeduplicateEntries(); } void ToJSValue(JSContext* aCx, JS::MutableHandle<JS::Value> aRetVal) const; - friend IPC::ParamTraits<mozilla::ProfileGenerationAdditionalInformation>; - - private: - JSString* CreateJSStringFromSourceData( - JSContext* aCx, const ProfilerJSSourceData& aSourceData) const; - SharedLibraryInfo mSharedLibraries; - nsTArray<JSSourceEntry> mJSSourceEntries; }; struct ProfileAndAdditionalInformation { @@ -102,15 +58,6 @@ struct ProfileAndAdditionalInformation { : mProfile(std::move(aProfile)), mAdditionalInformation(Some(std::move(aAdditionalInformation))) {} - ProfileAndAdditionalInformation(const ProfileAndAdditionalInformation&) = - delete; - ProfileAndAdditionalInformation& operator=( - const ProfileAndAdditionalInformation&) = delete; - - ProfileAndAdditionalInformation(ProfileAndAdditionalInformation&&) = default; - ProfileAndAdditionalInformation& operator=( - ProfileAndAdditionalInformation&&) = default; - size_t SizeOf() const { size_t size = mProfile.Length(); if (mAdditionalInformation.isSome()) { diff --git a/tools/profiler/public/ProfilerState.h b/tools/profiler/public/ProfilerState.h @@ -135,10 +135,7 @@ \ MACRO(27, "flows", Flows, \ "Include all flow-related markers. These markers show the program" \ - "better but can cause more overhead in some places than normal.") \ - \ - MACRO(28, "jssources", JSSources, \ - "Collect JavaScript source code information for profiled scripts.") + "better but can cause more overhead in some places than normal.") // *** Synchronize with lists in BaseProfilerState.h and geckoProfiler.json *** diff --git a/tools/profiler/public/ProfilerUtils.h b/tools/profiler/public/ProfilerUtils.h @@ -12,16 +12,6 @@ #include "mozilla/BaseProfilerUtils.h" -#include "mozilla/ResultVariant.h" - -enum class ProfilerError { - IsInactive, - JsonGenerationFailed, -}; - -template <typename T> -using ProfilerResult = mozilla::Result<T, ProfilerError>; - using ProfilerProcessId = mozilla::baseprofiler::BaseProfilerProcessId; using ProfilerThreadId = mozilla::baseprofiler::BaseProfilerThreadId; diff --git a/tools/profiler/tests/browser/browser.toml b/tools/profiler/tests/browser/browser.toml @@ -8,16 +8,6 @@ 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_js_sources_retrieval.js"] -support-files = [ - "simple.html", - "page_with_external_js.html", - "external_script.js" -] - ["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 @@ -1,210 +0,0 @@ -/* 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" - ); - }); -}); diff --git a/tools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js b/tools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js @@ -1,239 +0,0 @@ -/* 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 JS sources are collected and included in profile additional information. - */ -add_task(async function test_js_sources_in_profile_additional_info() { - 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"] }); - // Execute some JavaScript to create sources - await SpecialPowers.spawn(contentBrowser, [], () => { - content.window.eval(` - function testSourceFunction() { - console.log("This is a test function"); - return 42; - } - testSourceFunction(); - `); - }); - - // Get the profile with additional information - // Note: We don't have to await here for the pause because the await for - // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to - // be executed in the child process first. - Services.profiler.Pause(); - const profileData = - await Services.profiler.getProfileDataAsGzippedArrayBuffer(); - await Services.profiler.StopProfiler(); - - // Verify we got profile data - Assert.ok(!!profileData, "Should receive profile data"); - Assert.ok(!!profileData.profile, "Should have profile data"); - Assert.ok( - !!profileData.additionalInformation, - "Should have additional information" - ); - - // Check that the additional information has JS sources - const sources = profileData.additionalInformation.jsSources; - Assert.ok(!!sources, "Additional info should contain jsSources"); - Assert.equal(typeof sources, "object", "jsSources should be an object"); - - // Check that we have some JS sources - Assert.greater( - Object.keys(sources).length, - 0, - "Should have at least one JS source" - ); - - info(`Total JS sources collected: ${sources.length}`); - - // Check the sources to verify they contain actual source text - for (const sourceUuid in sources) { - const sourceText = sources[sourceUuid]; - Assert.ok( - typeof sourceUuid === "string" && !!sourceUuid.length, - "sourceUuid should be a non-empty string" - ); - Assert.ok( - typeof sourceText === "string" && !!sourceText.length, - `Source ${sourceUuid} should be a non-empty string` - ); - } - }); -}); - -/** - * Test that different types of JS sources are handled correctly. - */ -add_task(async function test_js_sources_different_types() { - 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"] }); - // Execute different types of JavaScript to test source collection - await SpecialPowers.spawn(contentBrowser, [], () => { - // Inline script - content.window.eval(` - function inlineFunction() { - return "inline"; - } - inlineFunction(); - `); - - // Anonymous function - content.window.eval(` - (function() { - console.log("anonymous function"); - })(); - `); - }); - - // Get profile data - // Note: We don't have to await here for the pause because the await for - // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to - // be executed in the child process first. - Services.profiler.Pause(); - const profileData = - await Services.profiler.getProfileDataAsGzippedArrayBuffer(); - await Services.profiler.StopProfiler(); - - const sources = profileData.additionalInformation.jsSources; - Assert.ok(!!sources, "Additional info should contain jsSources"); - Assert.equal(typeof sources, "object", "jsSources should be an object"); - - // Check that we captured sources for different types of JavaScript - let inlineSourceCount = 0; - let inlineSourceLength = 0; - - for (const sourceUuid in sources) { - const sourceText = sources[sourceUuid]; - if (typeof sourceText === "string" && sourceText.length) { - // Check if we found our test functions - if ( - sourceText.includes("inlineFunction") || - sourceText.includes("anonymous function") - ) { - inlineSourceCount += 1; - inlineSourceLength += sourceText.length; - } - } - } - - Assert.greater( - inlineSourceLength, - 0, - "Should have collected source text with content" - ); - - info(`Total source text length: ${inlineSourceLength} characters`); - - Assert.greaterOrEqual( - inlineSourceCount, - 2, - "Should have collected all the test functions" - ); - }); -}); - -/** - * Test that external JS files are properly collected in JS sources. - */ -add_task(async function test_js_sources_external_scripts() { - Assert.ok( - !Services.profiler.IsActive(), - "The profiler is not currently active" - ); - - const url = BASE_URL + "page_with_external_js.html"; - await BrowserTestUtils.withNewTab(url, async contentBrowser => { - await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] }); - // Wait for page to load and execute scripts - await SpecialPowers.spawn(contentBrowser, [], () => { - return new Promise(resolve => { - if (content.document.readyState === "complete") { - resolve(); - } else { - content.window.addEventListener("load", resolve); - } - }); - }); - - // Get profile data - // Note: We don't have to await here for the pause because the await for - // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to - // be executed in the child process first. - Services.profiler.Pause(); - const profileData = - await Services.profiler.getProfileDataAsGzippedArrayBuffer(); - await Services.profiler.StopProfiler(); - - const sources = profileData.additionalInformation.jsSources; - Assert.ok(!!sources, "Additional info should contain jsSources"); - Assert.equal(typeof sources, "object", "jsSources should be an object"); - - // Look for external script sources - let foundExternalScript = false; - let foundInlineScript = false; - let externalScriptSource = null; - - for (const sourceUuid in sources) { - const sourceText = sources[sourceUuid]; - if (typeof sourceText === "string" && sourceText.length) { - // Check for external script content - if ( - sourceText.includes("externalFunction") && - sourceText.includes("calculateSum") - ) { - foundExternalScript = true; - externalScriptSource = sourceText; - info(`Found external script source (${sourceText.length} chars)`); - } - - // Check for inline script content - if ( - sourceText.includes("inlineFunction") && - sourceText.includes("window.onload") - ) { - foundInlineScript = true; - info(`Found inline script source (${sourceText.length} chars)`); - } - } - } - - Assert.ok( - foundExternalScript, - "Should find external JavaScript file content in sources" - ); - - Assert.ok( - foundInlineScript, - "Should find inline JavaScript content in sources" - ); - - // Verify external script has expected functions - Assert.ok( - externalScriptSource.includes("This is an external function"), - "External script should contain expected comment" - ); - - Assert.ok( - externalScriptSource.includes("calculateSum"), - "External script should contain calculateSum function" - ); - - info("Successfully verified external JS file collection"); - }); -}); diff --git a/tools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.js b/tools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.js @@ -83,15 +83,11 @@ add_task(async function test_profile_feature_jstracing_objtestutils() { // Then lookup for the matching frame, based on the string index const { frameTable } = contentThread; const FRAME_LOCATION_SLOT = frameTable.schema.location; - const functionDummyFrameIndexes = []; - frameTable.data.forEach((frame, idx) => { - if (frame[FRAME_LOCATION_SLOT] === functionDummyStringIndex) { - functionDummyFrameIndexes.push(idx); - } - }); - + const functionDummyFrameIndex = frameTable.data.findIndex( + frame => frame[FRAME_LOCATION_SLOT] == functionDummyStringIndex + ); Assert.greater( - functionDummyFrameIndexes.length, + functionDummyFrameIndex, 0, "Found frame for 'dummy' function call" ); @@ -100,17 +96,14 @@ add_task(async function test_profile_feature_jstracing_objtestutils() { // Each symbol's frame is visible in a stack, and the stack tree is valid const { stackTable } = contentThread; const STACK_FRAME_SLOT = stackTable.schema.frame; - const functionDummyStacks = []; - stackTable.data.forEach((stack, idx) => { - if (functionDummyFrameIndexes.includes(stack[STACK_FRAME_SLOT])) { - functionDummyStacks.push(idx); - } - }); + const functionDummyStack = stackTable.data.findIndex( + stack => stack[STACK_FRAME_SLOT] == functionDummyFrameIndex + ); const { samples } = contentThread; const SAMPLE_STACK_SLOT = contentThread.samples.schema.stack; - const functionDummySamples = samples.data.filter(sample => - functionDummyStacks.includes(sample[SAMPLE_STACK_SLOT]) + const functionDummySamples = samples.data.filter( + sample => sample[SAMPLE_STACK_SLOT] == functionDummyStack ); const actualValues = []; diff --git a/tools/profiler/tests/browser/external_script.js b/tools/profiler/tests/browser/external_script.js @@ -1,16 +0,0 @@ -// This is an external function -function externalFunction() { - // Do some work for ~2ms to generate samples - const start = performance.now(); - let result = 0; - while (performance.now() - start < 10) { - for (let i = 0; i < 1000; i++) { - result += Math.random() * i; - } - } - return result; -} - -function calculateSum(a, b) { - return a + b; -} diff --git a/tools/profiler/tests/browser/page_with_external_js.html b/tools/profiler/tests/browser/page_with_external_js.html @@ -1,30 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>Test Page with External JS</title> - <script src="external_script.js"></script> -</head> -<body> - <h1>Test Page for External JS Sources</h1> - <p>This page loads an external JavaScript file to test JS source collection.</p> - - <script> - function inlineFunction() { - // Do some work for ~2ms to generate samples - const start = performance.now(); - let result = 0; - while (performance.now() - start < 10) { - for (let i = 0; i < 1000; i++) { - result += Math.sin(i) * Math.cos(i); - } - } - return result; - } - - window.onload = function() { - inlineFunction(); - externalFunction(); - }; - </script> -</body> -</html>