forked from mirrors/gecko-dev
Bug 1721109 - Move ProfilerGetSymbols code out of toolkit/components/extensions and into devtools directories. r=canaltinova
Differential Revision: https://phabricator.services.mozilla.com/D120182
This commit is contained in:
parent
a058bd1bbf
commit
8b439f0bce
10 changed files with 194 additions and 193 deletions
|
|
@ -79,6 +79,9 @@ devtools/client/webconsole/test/browser/code_bundle_invalidmap.js
|
||||||
devtools/server/tests/xpcshell/setBreakpoint*
|
devtools/server/tests/xpcshell/setBreakpoint*
|
||||||
devtools/server/tests/xpcshell/sourcemapped.js
|
devtools/server/tests/xpcshell/sourcemapped.js
|
||||||
|
|
||||||
|
# Ignore generated code from wasm-bindgen
|
||||||
|
devtools/client/performance-new/profiler_get_symbols.js
|
||||||
|
|
||||||
# Testing syntax error
|
# Testing syntax error
|
||||||
devtools/client/webconsole/test/browser/test-syntaxerror-worklet.js
|
devtools/client/webconsole/test/browser/test-syntaxerror-worklet.js
|
||||||
|
|
||||||
|
|
@ -185,9 +188,6 @@ testing/web-platform/
|
||||||
|
|
||||||
# toolkit/ exclusions
|
# toolkit/ exclusions
|
||||||
|
|
||||||
# Ignore generated code from wasm-bindgen
|
|
||||||
toolkit/components/extensions/profiler_get_symbols.js
|
|
||||||
|
|
||||||
# Intentionally invalid JS
|
# Intentionally invalid JS
|
||||||
toolkit/components/workerloader/tests/moduleF-syntax-error.js
|
toolkit/components/workerloader/tests/moduleF-syntax-error.js
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ declare namespace MockedExports {
|
||||||
typeof import("resource://gre/modules/osfile.jsm");
|
typeof import("resource://gre/modules/osfile.jsm");
|
||||||
"resource://gre/modules/AppConstants.jsm":
|
"resource://gre/modules/AppConstants.jsm":
|
||||||
typeof import("resource://gre/modules/AppConstants.jsm");
|
typeof import("resource://gre/modules/AppConstants.jsm");
|
||||||
"resource://gre/modules/ProfilerGetSymbols.jsm":
|
|
||||||
typeof import("resource://gre/modules/ProfilerGetSymbols.jsm");
|
|
||||||
"resource:///modules/CustomizableUI.jsm":
|
"resource:///modules/CustomizableUI.jsm":
|
||||||
typeof import("resource:///modules/CustomizableUI.jsm")
|
typeof import("resource:///modules/CustomizableUI.jsm")
|
||||||
"resource:///modules/CustomizableWidgets.jsm":
|
"resource:///modules/CustomizableWidgets.jsm":
|
||||||
|
|
@ -189,16 +187,6 @@ declare namespace MockedExports {
|
||||||
decorate: (target: object) => void;
|
decorate: (target: object) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProfilerGetSymbolsJSM: {
|
|
||||||
ProfilerGetSymbols: {
|
|
||||||
getSymbolTable: (
|
|
||||||
path: string,
|
|
||||||
debugPath: string,
|
|
||||||
breakpadId: string
|
|
||||||
) => any;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppConstantsJSM: {
|
const AppConstantsJSM: {
|
||||||
AppConstants: {
|
AppConstants: {
|
||||||
platform: string;
|
platform: string;
|
||||||
|
|
@ -359,10 +347,6 @@ declare module "resource://gre/modules/AppConstants.jsm" {
|
||||||
export = MockedExports.AppConstantsJSM;
|
export = MockedExports.AppConstantsJSM;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "resource://gre/modules/ProfilerGetSymbols.jsm" {
|
|
||||||
export = MockedExports.ProfilerGetSymbolsJSM;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "resource://gre/modules/WebChannel.jsm" {
|
declare module "resource://gre/modules/WebChannel.jsm" {
|
||||||
export = MockedExports.WebChannelJSM;
|
export = MockedExports.WebChannelJSM;
|
||||||
}
|
}
|
||||||
|
|
@ -444,6 +428,8 @@ declare interface ChromeWindow extends Window {
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare class ChromeWorker extends Worker {}
|
||||||
|
|
||||||
declare interface MenuListElement extends XULElement {
|
declare interface MenuListElement extends XULElement {
|
||||||
value: string;
|
value: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
|
||||||
15
devtools/client/performance-new/@types/perf.d.ts
vendored
15
devtools/client/performance-new/@types/perf.d.ts
vendored
|
|
@ -499,3 +499,18 @@ export interface FeatureDescription {
|
||||||
// This will give a reason if the feature is disabled.
|
// This will give a reason if the feature is disabled.
|
||||||
disabledReason?: string;
|
disabledReason?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SymbolicationWorkerError = {
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
fileName?: string;
|
||||||
|
lineNumber?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SymbolicationWorkerReplyData =
|
||||||
|
| {
|
||||||
|
result: SymbolTableAsTuple;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
error: SymbolicationWorkerError;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,6 @@ const lazy = createLazyLoaders({
|
||||||
Chrome: () => require("chrome"),
|
Chrome: () => require("chrome"),
|
||||||
Services: () => require("Services"),
|
Services: () => require("Services"),
|
||||||
OS: () => ChromeUtils.import("resource://gre/modules/osfile.jsm"),
|
OS: () => ChromeUtils.import("resource://gre/modules/osfile.jsm"),
|
||||||
ProfilerGetSymbols: () =>
|
|
||||||
ChromeUtils.import("resource://gre/modules/ProfilerGetSymbols.jsm"),
|
|
||||||
PerfSymbolication: () =>
|
PerfSymbolication: () =>
|
||||||
ChromeUtils.import(
|
ChromeUtils.import(
|
||||||
"resource://devtools/client/performance-new/symbolication.jsm.js"
|
"resource://devtools/client/performance-new/symbolication.jsm.js"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ DevToolsModules(
|
||||||
"browser.js",
|
"browser.js",
|
||||||
"initializer.js",
|
"initializer.js",
|
||||||
"panel.js",
|
"panel.js",
|
||||||
|
"profiler_get_symbols.js",
|
||||||
|
"symbolication-worker.js",
|
||||||
"symbolication.jsm.js",
|
"symbolication.jsm.js",
|
||||||
"typescript-lazy-load.jsm.js",
|
"typescript-lazy-load.jsm.js",
|
||||||
"utils.js",
|
"utils.js",
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
importScripts(
|
importScripts(
|
||||||
"resource://gre/modules/osfile.jsm",
|
"resource://gre/modules/osfile.jsm",
|
||||||
"resource://gre/modules/profiler_get_symbols.js"
|
"resource://devtools/client/performance-new/profiler_get_symbols.js"
|
||||||
);
|
);
|
||||||
|
|
||||||
// This worker uses the wasm module that was generated from https://github.com/mstange/profiler-get-symbols.
|
// This worker uses the wasm module that was generated from https://github.com/mstange/profiler-get-symbols.
|
||||||
|
|
@ -20,6 +20,7 @@ importScripts(
|
||||||
// the wasm code, and returns the symbol table or an error. Then it shuts down
|
// the wasm code, and returns the symbol table or an error. Then it shuts down
|
||||||
// itself.
|
// itself.
|
||||||
|
|
||||||
|
/* eslint camelcase: 0*/
|
||||||
const { WasmMemBuffer, get_compact_symbol_table } = wasm_bindgen;
|
const { WasmMemBuffer, get_compact_symbol_table } = wasm_bindgen;
|
||||||
|
|
||||||
// Read an open OS.File instance into the Uint8Array dataBuf.
|
// Read an open OS.File instance into the Uint8Array dataBuf.
|
||||||
|
|
@ -103,7 +104,11 @@ onmessage = async e => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let output = get_compact_symbol_table(binaryData, debugData, breakpadId);
|
const output = get_compact_symbol_table(
|
||||||
|
binaryData,
|
||||||
|
debugData,
|
||||||
|
breakpadId
|
||||||
|
);
|
||||||
const result = [
|
const result = [
|
||||||
output.take_addr(),
|
output.take_addr(),
|
||||||
output.take_index(),
|
output.take_index(),
|
||||||
|
|
@ -13,14 +13,172 @@ const { createLazyLoaders } = ChromeUtils.import(
|
||||||
* @typedef {import("./@types/perf").PerfFront} PerfFront
|
* @typedef {import("./@types/perf").PerfFront} PerfFront
|
||||||
* @typedef {import("./@types/perf").SymbolTableAsTuple} SymbolTableAsTuple
|
* @typedef {import("./@types/perf").SymbolTableAsTuple} SymbolTableAsTuple
|
||||||
* @typedef {import("./@types/perf").SymbolicationService} SymbolicationService
|
* @typedef {import("./@types/perf").SymbolicationService} SymbolicationService
|
||||||
|
* @typedef {import("./@types/perf").SymbolicationWorkerReplyData} SymbolicationWorkerReplyData
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const lazy = createLazyLoaders({
|
const lazy = createLazyLoaders({
|
||||||
OS: () => ChromeUtils.import("resource://gre/modules/osfile.jsm"),
|
OS: () => ChromeUtils.import("resource://gre/modules/osfile.jsm"),
|
||||||
ProfilerGetSymbols: () =>
|
|
||||||
ChromeUtils.import("resource://gre/modules/ProfilerGetSymbols.jsm"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"setTimeout",
|
||||||
|
"resource://gre/modules/Timer.jsm"
|
||||||
|
);
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"clearTimeout",
|
||||||
|
"resource://gre/modules/Timer.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @type {any} */
|
||||||
|
const global = this;
|
||||||
|
|
||||||
|
// This module obtains symbol tables for binaries.
|
||||||
|
// It does so with the help of a WASM module which gets pulled in from the
|
||||||
|
// internet on demand. We're doing this purely for the purposes of saving on
|
||||||
|
// code size. The contents of the WASM module are expected to be static, they
|
||||||
|
// are checked against the hash specified below.
|
||||||
|
// The WASM code is run on a ChromeWorker thread. It takes the raw byte
|
||||||
|
// contents of the to-be-dumped binary (and of an additional optional pdb file
|
||||||
|
// on Windows) as its input, and returns a set of typed arrays which make up
|
||||||
|
// the symbol table.
|
||||||
|
|
||||||
|
// Don't let the strange looking URLs and strings below scare you.
|
||||||
|
// The hash check ensures that the contents of the wasm module are what we
|
||||||
|
// expect them to be.
|
||||||
|
// The source code is at https://github.com/mstange/profiler-get-symbols/ .
|
||||||
|
|
||||||
|
// Generated from https://github.com/mstange/profiler-get-symbols/commit/90ee39f1d18d2727f07dc57bd93cff6bc73ce8a0
|
||||||
|
const WASM_MODULE_URL =
|
||||||
|
"https://zealous-rosalind-a98ce8.netlify.com/wasm/8f7ca2f70e1cd21b5a2dbe96545672752887bfbd4e7b3b9437e9fc7c3da0a3bedae4584ff734f0c9f08c642e6b66ffab.wasm";
|
||||||
|
const WASM_MODULE_INTEGRITY =
|
||||||
|
"sha384-j3yi9w4c0htaLb6WVFZydSiHv71OezuUN+n8fD2go77a5FhP9zTwyfCMZC5rZv+r";
|
||||||
|
|
||||||
|
const EXPIRY_TIME_IN_MS = 5 * 60 * 1000; // 5 minutes
|
||||||
|
|
||||||
|
/** @type {Promise<WebAssembly.Module> | null} */
|
||||||
|
let gCachedWASMModulePromise = null;
|
||||||
|
let gCachedWASMModuleExpiryTimer = 0;
|
||||||
|
|
||||||
|
// Keep active workers alive (see bug 1592227).
|
||||||
|
const gActiveWorkers = new Set();
|
||||||
|
|
||||||
|
function clearCachedWASMModule() {
|
||||||
|
gCachedWASMModulePromise = null;
|
||||||
|
gCachedWASMModuleExpiryTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWASMProfilerGetSymbolsModule() {
|
||||||
|
if (!gCachedWASMModulePromise) {
|
||||||
|
gCachedWASMModulePromise = (async function() {
|
||||||
|
const request = new Request(WASM_MODULE_URL, {
|
||||||
|
integrity: WASM_MODULE_INTEGRITY,
|
||||||
|
credentials: "omit",
|
||||||
|
});
|
||||||
|
return WebAssembly.compileStreaming(fetch(request));
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset expiry timer.
|
||||||
|
clearTimeout(gCachedWASMModuleExpiryTimer);
|
||||||
|
gCachedWASMModuleExpiryTimer = setTimeout(
|
||||||
|
clearCachedWASMModule,
|
||||||
|
EXPIRY_TIME_IN_MS
|
||||||
|
);
|
||||||
|
|
||||||
|
return gCachedWASMModulePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain symbols for the binary at the specified location.
|
||||||
|
*
|
||||||
|
* @param {string} binaryPath The absolute path to the binary on the local
|
||||||
|
* file system.
|
||||||
|
* @param {string} debugPath The absolute path to the binary's pdb file on the
|
||||||
|
* local file system if on Windows, otherwise the same as binaryPath.
|
||||||
|
* @param {string} breakpadId The breakpadId for the binary whose symbols
|
||||||
|
* should be obtained. This is used for two purposes: 1) to locate the
|
||||||
|
* correct single-arch binary in "FatArch" files, and 2) to make sure the
|
||||||
|
* binary at the given path is actually the one that we want. If no ID match
|
||||||
|
* is found, this function throws (rejects the promise).
|
||||||
|
* @returns {Promise<SymbolTableAsTuple>} The symbol table in SymbolTableAsTuple format, see the
|
||||||
|
* documentation for nsIProfiler.getSymbolTable.
|
||||||
|
*/
|
||||||
|
async function getSymbolTableFromLocalBinary(
|
||||||
|
binaryPath,
|
||||||
|
debugPath,
|
||||||
|
breakpadId
|
||||||
|
) {
|
||||||
|
const module = await getWASMProfilerGetSymbolsModule();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const worker = new ChromeWorker(
|
||||||
|
"resource://devtools/client/performance-new/symbolication-worker.js"
|
||||||
|
);
|
||||||
|
gActiveWorkers.add(worker);
|
||||||
|
|
||||||
|
/** @param {MessageEvent<SymbolicationWorkerReplyData>} msg */
|
||||||
|
worker.onmessage = msg => {
|
||||||
|
gActiveWorkers.delete(worker);
|
||||||
|
if ("error" in msg.data) {
|
||||||
|
const error = msg.data.error;
|
||||||
|
if (error.name) {
|
||||||
|
// Turn the JSON error object into a real Error object.
|
||||||
|
const { name, message, fileName, lineNumber } = error;
|
||||||
|
const ErrorObjConstructor =
|
||||||
|
name in global && Error.isPrototypeOf(global[name])
|
||||||
|
? global[name]
|
||||||
|
: Error;
|
||||||
|
const e = new ErrorObjConstructor(message, fileName, lineNumber);
|
||||||
|
e.name = name;
|
||||||
|
reject(e);
|
||||||
|
} else {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(msg.data.result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle uncaught errors from the worker script. onerror is called if
|
||||||
|
// there's a syntax error in the worker script, for example, or when an
|
||||||
|
// unhandled exception is thrown, but not for unhandled promise
|
||||||
|
// rejections. Without this handler, mistakes during development such as
|
||||||
|
// syntax errors can be hard to track down.
|
||||||
|
worker.onerror = errorEvent => {
|
||||||
|
gActiveWorkers.delete(worker);
|
||||||
|
worker.terminate();
|
||||||
|
if (errorEvent instanceof ErrorEvent) {
|
||||||
|
const { message, filename, lineno } = errorEvent;
|
||||||
|
const error = new Error(`${message} at ${filename}:${lineno}`);
|
||||||
|
error.name = "WorkerError";
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
reject(new Error("Error in worker"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle errors from messages that cannot be deserialized. I'm not sure
|
||||||
|
// how to get into such a state, but having this handler seems like a good
|
||||||
|
// idea.
|
||||||
|
worker.onmessageerror = errorEvent => {
|
||||||
|
gActiveWorkers.delete(worker);
|
||||||
|
worker.terminate();
|
||||||
|
if (errorEvent instanceof ErrorEvent) {
|
||||||
|
const { message, filename, lineno } = errorEvent;
|
||||||
|
const error = new Error(`${message} at ${filename}:${lineno}`);
|
||||||
|
error.name = "WorkerMessageError";
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
reject(new Error("Error in worker"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.postMessage({ binaryPath, debugPath, breakpadId, module });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PerfFront} perfFront
|
* @param {PerfFront} perfFront
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
|
|
@ -140,20 +298,21 @@ class LocalSymbolicationService {
|
||||||
const candidatePaths = this._getCandidatePaths(debugName, breakpadId);
|
const candidatePaths = this._getCandidatePaths(debugName, breakpadId);
|
||||||
|
|
||||||
// Iterate over all the paths and try to get symbols from each entry.
|
// Iterate over all the paths and try to get symbols from each entry.
|
||||||
const { ProfilerGetSymbols } = lazy.ProfilerGetSymbols();
|
const errors = [];
|
||||||
for (const { path, debugPath } of candidatePaths) {
|
for (const { path, debugPath } of candidatePaths) {
|
||||||
if (await doesFileExistAtPath(path)) {
|
if (await doesFileExistAtPath(path)) {
|
||||||
try {
|
try {
|
||||||
return await ProfilerGetSymbols.getSymbolTable(
|
return await getSymbolTableFromLocalBinary(
|
||||||
path,
|
path,
|
||||||
debugPath,
|
debugPath,
|
||||||
breakpadId
|
breakpadId
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ProfilerGetSymbols.getSymbolTable was unsuccessful. So either the
|
// getSymbolTable was unsuccessful. So either the
|
||||||
// file wasn't parseable or its contents didn't match the specified
|
// file wasn't parseable or its contents didn't match the specified
|
||||||
// breakpadId, or some other error occurred.
|
// breakpadId, or some other error occurred.
|
||||||
// Advance to the next candidate path.
|
// Advance to the next candidate path.
|
||||||
|
errors.push(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +321,7 @@ class LocalSymbolicationService {
|
||||||
`Could not obtain symbols for the library ${debugName} ${breakpadId} ` +
|
`Could not obtain symbols for the library ${debugName} ${breakpadId} ` +
|
||||||
`because there was no matching file at any of the candidate paths: ${JSON.stringify(
|
`because there was no matching file at any of the candidate paths: ${JSON.stringify(
|
||||||
candidatePaths
|
candidatePaths
|
||||||
)}`
|
)}. Errors: ${errors.map(e => e.message).join(", ")}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
||||||
/* vim: set sts=2 sw=2 et 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const EXPORTED_SYMBOLS = ["ProfilerGetSymbols"];
|
|
||||||
|
|
||||||
ChromeUtils.defineModuleGetter(
|
|
||||||
this,
|
|
||||||
"setTimeout",
|
|
||||||
"resource://gre/modules/Timer.jsm"
|
|
||||||
);
|
|
||||||
ChromeUtils.defineModuleGetter(
|
|
||||||
this,
|
|
||||||
"clearTimeout",
|
|
||||||
"resource://gre/modules/Timer.jsm"
|
|
||||||
);
|
|
||||||
|
|
||||||
Cu.importGlobalProperties(["fetch"]);
|
|
||||||
|
|
||||||
const global = this;
|
|
||||||
|
|
||||||
// This module obtains symbol tables for binaries.
|
|
||||||
// It does so with the help of a WASM module which gets pulled in from the
|
|
||||||
// internet on demand. We're doing this purely for the purposes of saving on
|
|
||||||
// code size. The contents of the WASM module are expected to be static, they
|
|
||||||
// are checked against the hash specified below.
|
|
||||||
// The WASM code is run on a ChromeWorker thread. It takes the raw byte
|
|
||||||
// contents of the to-be-dumped binary (and of an additional optional pdb file
|
|
||||||
// on Windows) as its input, and returns a set of typed arrays which make up
|
|
||||||
// the symbol table.
|
|
||||||
|
|
||||||
// Don't let the strange looking URLs and strings below scare you.
|
|
||||||
// The hash check ensures that the contents of the wasm module are what we
|
|
||||||
// expect them to be.
|
|
||||||
// The source code is at https://github.com/mstange/profiler-get-symbols/ .
|
|
||||||
|
|
||||||
// Generated from https://github.com/mstange/profiler-get-symbols/commit/90ee39f1d18d2727f07dc57bd93cff6bc73ce8a0
|
|
||||||
const WASM_MODULE_URL =
|
|
||||||
"https://zealous-rosalind-a98ce8.netlify.com/wasm/8f7ca2f70e1cd21b5a2dbe96545672752887bfbd4e7b3b9437e9fc7c3da0a3bedae4584ff734f0c9f08c642e6b66ffab.wasm";
|
|
||||||
const WASM_MODULE_INTEGRITY =
|
|
||||||
"sha384-j3yi9w4c0htaLb6WVFZydSiHv71OezuUN+n8fD2go77a5FhP9zTwyfCMZC5rZv+r";
|
|
||||||
|
|
||||||
const EXPIRY_TIME_IN_MS = 5 * 60 * 1000; // 5 minutes
|
|
||||||
|
|
||||||
let gCachedWASMModulePromise = null;
|
|
||||||
let gCachedWASMModuleExpiryTimer = 0;
|
|
||||||
|
|
||||||
// Keep active workers alive (see bug 1592227).
|
|
||||||
let gActiveWorkers = new Set();
|
|
||||||
|
|
||||||
function clearCachedWASMModule() {
|
|
||||||
gCachedWASMModulePromise = null;
|
|
||||||
gCachedWASMModuleExpiryTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWASMProfilerGetSymbolsModule() {
|
|
||||||
if (!gCachedWASMModulePromise) {
|
|
||||||
gCachedWASMModulePromise = (async function() {
|
|
||||||
const request = new Request(WASM_MODULE_URL, {
|
|
||||||
integrity: WASM_MODULE_INTEGRITY,
|
|
||||||
credentials: "omit",
|
|
||||||
});
|
|
||||||
return WebAssembly.compileStreaming(fetch(request));
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset expiry timer.
|
|
||||||
clearTimeout(gCachedWASMModuleExpiryTimer);
|
|
||||||
gCachedWASMModuleExpiryTimer = setTimeout(
|
|
||||||
clearCachedWASMModule,
|
|
||||||
EXPIRY_TIME_IN_MS
|
|
||||||
);
|
|
||||||
|
|
||||||
return gCachedWASMModulePromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ProfilerGetSymbols = {
|
|
||||||
/**
|
|
||||||
* Obtain symbols for the binary at the specified location.
|
|
||||||
*
|
|
||||||
* @param {string} binaryPath The absolute path to the binary on the local
|
|
||||||
* file system.
|
|
||||||
* @param {string} debugPath The absolute path to the binary's pdb file on the
|
|
||||||
* local file system if on Windows, otherwise the same as binaryPath.
|
|
||||||
* @param {string} breakpadId The breakpadId for the binary whose symbols
|
|
||||||
* should be obtained. This is used for two purposes: 1) to locate the
|
|
||||||
* correct single-arch binary in "FatArch" files, and 2) to make sure the
|
|
||||||
* binary at the given path is actually the one that we want. If no ID match
|
|
||||||
* is found, this function throws (rejects the promise).
|
|
||||||
* @returns {Promise} The symbol table in SymbolTableAsTuple format, see the
|
|
||||||
* documentation for nsIProfiler.getSymbolTable.
|
|
||||||
*/
|
|
||||||
async getSymbolTable(binaryPath, debugPath, breakpadId) {
|
|
||||||
const module = await getWASMProfilerGetSymbolsModule();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const worker = new ChromeWorker(
|
|
||||||
"resource://gre/modules/ProfilerGetSymbols-worker.js"
|
|
||||||
);
|
|
||||||
gActiveWorkers.add(worker);
|
|
||||||
|
|
||||||
worker.onmessage = msg => {
|
|
||||||
gActiveWorkers.delete(worker);
|
|
||||||
if (msg.data.error) {
|
|
||||||
const error = msg.data.error;
|
|
||||||
if (error.name) {
|
|
||||||
// Turn the JSON error object into a real Error object.
|
|
||||||
const { name, message, fileName, lineNumber } = error;
|
|
||||||
const ErrorObjConstructor =
|
|
||||||
name in global && Error.isPrototypeOf(global[name])
|
|
||||||
? global[name]
|
|
||||||
: Error;
|
|
||||||
const e = new ErrorObjConstructor(message, fileName, lineNumber);
|
|
||||||
e.name = name;
|
|
||||||
reject(e);
|
|
||||||
} else {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve(msg.data.result);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle uncaught errors from the worker script. onerror is called if
|
|
||||||
// there's a syntax error in the worker script, for example, or when an
|
|
||||||
// unhandled exception is thrown, but not for unhandled promise
|
|
||||||
// rejections. Without this handler, mistakes during development such as
|
|
||||||
// syntax errors can be hard to track down.
|
|
||||||
worker.onerror = errorEvent => {
|
|
||||||
gActiveWorkers.delete(worker);
|
|
||||||
worker.terminate();
|
|
||||||
const { message, filename, lineno } = errorEvent;
|
|
||||||
const error = new Error(message, filename, lineno);
|
|
||||||
error.name = "WorkerError";
|
|
||||||
reject(error);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle errors from messages that cannot be deserialized. I'm not sure
|
|
||||||
// how to get into such a state, but having this handler seems like a good
|
|
||||||
// idea.
|
|
||||||
worker.onmessageerror = errorEvent => {
|
|
||||||
gActiveWorkers.delete(worker);
|
|
||||||
worker.terminate();
|
|
||||||
const { message, filename, lineno } = errorEvent;
|
|
||||||
const error = new Error(message, filename, lineno);
|
|
||||||
error.name = "WorkerMessageError";
|
|
||||||
reject(error);
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.postMessage({ binaryPath, debugPath, breakpadId, module });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -46,13 +46,6 @@ EXTRA_JS_MODULES += [
|
||||||
"WebNavigationFrames.jsm",
|
"WebNavigationFrames.jsm",
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android":
|
|
||||||
EXTRA_JS_MODULES += [
|
|
||||||
"profiler_get_symbols.js",
|
|
||||||
"ProfilerGetSymbols-worker.js",
|
|
||||||
"ProfilerGetSymbols.jsm",
|
|
||||||
]
|
|
||||||
|
|
||||||
EXTRA_COMPONENTS += [
|
EXTRA_COMPONENTS += [
|
||||||
"extensions-toolkit.manifest",
|
"extensions-toolkit.manifest",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue