From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../performance-new/shared/symbolication.worker.js | 277 +++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 devtools/client/performance-new/shared/symbolication.worker.js (limited to 'devtools/client/performance-new/shared/symbolication.worker.js') diff --git a/devtools/client/performance-new/shared/symbolication.worker.js b/devtools/client/performance-new/shared/symbolication.worker.js new file mode 100644 index 0000000000..199514ac77 --- /dev/null +++ b/devtools/client/performance-new/shared/symbolication.worker.js @@ -0,0 +1,277 @@ +/* -*- 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/. */ + +// FIXME: This file is currently not covered by TypeScript, there is no "@ts-check" comment. +// We should fix this once we know how to deal with the module imports below. +// (Maybe once Firefox supports worker module? Bug 1247687) + +"use strict"; + +/* import-globals-from profiler_get_symbols.js */ +importScripts( + "resource://devtools/client/performance-new/shared/profiler_get_symbols.js" +); + +/** + * @typedef {import("../@types/perf").SymbolicationWorkerInitialMessage} SymbolicationWorkerInitialMessage + * @typedef {import("../@types/perf").FileHandle} FileHandle + */ + +// This worker uses the wasm module that was generated from https://github.com/mstange/profiler-get-symbols. +// See ProfilerGetSymbols.jsm for more information. +// +// The worker instantiates the module, reads the binary into wasm memory, runs +// the wasm code, and returns the symbol table or an error. Then it shuts down +// itself. + +/* eslint camelcase: 0*/ +const { getCompactSymbolTable, queryAPI } = wasm_bindgen; + +// Returns a plain object that is Structured Cloneable and has name and +// message properties. +function createPlainErrorObject(e) { + // Regular errors: just rewrap the object. + const { name, message, fileName, lineNumber } = e; + return { name, message, fileName, lineNumber }; +} + +/** + * A FileAndPathHelper object is passed to getCompactSymbolTable, which calls + * the methods `getCandidatePathsForBinaryOrPdb` and `readFile` on it. + */ +class FileAndPathHelper { + constructor(libInfoMap, objdirs) { + this._libInfoMap = libInfoMap; + this._objdirs = objdirs; + } + + /** + * Enumerate all paths at which we could find files with symbol information. + * This method is called by wasm code (via the bindings). + * + * @param {LibraryInfo} libraryInfo + * @returns {Array} + */ + getCandidatePathsForDebugFile(libraryInfo) { + const { debugName, breakpadId } = libraryInfo; + const key = `${debugName}:${breakpadId}`; + const lib = this._libInfoMap.get(key); + if (!lib) { + throw new Error( + `Could not find the library for "${debugName}", "${breakpadId}".` + ); + } + + const { name, path, debugPath, arch } = lib; + const candidatePaths = []; + + // First, try to find a binary with a matching file name and breakpadId + // in one of the manually specified objdirs. + // This is needed if the debuggee is a build running on a remote machine that + // was compiled by the developer on *this* machine (the "host machine"). In + // that case, the objdir will contain the compiled binary with full symbol and + // debug information, whereas the binary on the device may not exist in + // uncompressed form or may have been stripped of debug information and some + // symbol information. + // An objdir, or "object directory", is a directory on the host machine that's + // used to store build artifacts ("object files") from the compilation process. + // This only works if the binary is one of the Gecko binaries and not + // a system library. + for (const objdirPath of this._objdirs) { + try { + // Binaries are usually expected to exist at objdir/dist/bin/filename. + candidatePaths.push(PathUtils.join(objdirPath, "dist", "bin", name)); + + // Also search in the "objdir" directory itself (not just in dist/bin). + // If, for some unforeseen reason, the relevant binary is not inside the + // objdirs dist/bin/ directory, this provides a way out because it lets the + // user specify the actual location. + candidatePaths.push(PathUtils.join(objdirPath, name)); + } catch (e) { + // PathUtils.join throws if objdirPath is not an absolute path. + // Ignore those invalid objdir paths. + } + } + + // Check the absolute paths of the library last. + // We do this after the objdir search because the library's path may point + // to a stripped binary, which will have fewer symbols than the original + // binaries in the objdir. + if (debugPath !== path) { + // We're on Windows, and debugPath points to a PDB file. + // On non-Windows, path and debugPath are always the same. + + // Check the PDB file before the binary because the PDB has the symbol + // information. The binary is only used as a last-ditch fallback + // for things like Windows system libraries (e.g. graphics drivers). + candidatePaths.push(debugPath); + } + + // The location of the binary. If the profile was obtained on this machine + // (and not, for example, on an Android device), this file should always + // exist. + candidatePaths.push(path); + + // On macOS, for system libraries, add a final fallback for the dyld shared + // cache. Starting with macOS 11, most system libraries are located in this + // system-wide cache file and not present as individual files. + if (arch && (path.startsWith("/usr/") || path.startsWith("/System/"))) { + // Use the special syntax `dyldcache::`. + + // Dyld cache location used on macOS 13+: + candidatePaths.push( + `dyldcache:/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_${arch}:${path}` + ); + // Dyld cache location used on macOS 11 and 12: + candidatePaths.push( + `dyldcache:/System/Library/dyld/dyld_shared_cache_${arch}:${path}` + ); + } + + return candidatePaths; + } + + /** + * Enumerate all paths at which we could find the binary which matches the + * given libraryInfo, in order to disassemble machine code. + * This method is called by wasm code (via the bindings). + * + * @param {LibraryInfo} libraryInfo + * @returns {Array} + */ + getCandidatePathsForBinary(libraryInfo) { + const { debugName, breakpadId } = libraryInfo; + const key = `${debugName}:${breakpadId}`; + const lib = this._libInfoMap.get(key); + if (!lib) { + throw new Error( + `Could not find the library for "${debugName}", "${breakpadId}".` + ); + } + + const { name, path, arch } = lib; + const candidatePaths = []; + + // The location of the binary. If the profile was obtained on this machine + // (and not, for example, on an Android device), this file should always + // exist. + candidatePaths.push(path); + + // Fall back to searching in the manually specified objdirs. + // This is needed if the debuggee is a build running on a remote machine that + // was compiled by the developer on *this* machine (the "host machine"). In + // that case, the objdir will contain the compiled binary. + for (const objdirPath of this._objdirs) { + try { + // Binaries are usually expected to exist at objdir/dist/bin/filename. + candidatePaths.push(PathUtils.join(objdirPath, "dist", "bin", name)); + + // Also search in the "objdir" directory itself (not just in dist/bin). + // If, for some unforeseen reason, the relevant binary is not inside the + // objdirs dist/bin/ directory, this provides a way out because it lets the + // user specify the actual location. + candidatePaths.push(PathUtils.join(objdirPath, name)); + } catch (e) { + // PathUtils.join throws if objdirPath is not an absolute path. + // Ignore those invalid objdir paths. + } + } + + // On macOS, for system libraries, add a final fallback for the dyld shared + // cache. Starting with macOS 11, most system libraries are located in this + // system-wide cache file and not present as individual files. + if (arch && (path.startsWith("/usr/") || path.startsWith("/System/"))) { + // Use the special syntax `dyldcache::`. + + // Dyld cache location used on macOS 13+: + candidatePaths.push( + `dyldcache:/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_${arch}:${path}` + ); + // Dyld cache location used on macOS 11 and 12: + candidatePaths.push( + `dyldcache:/System/Library/dyld/dyld_shared_cache_${arch}:${path}` + ); + } + + return candidatePaths; + } + + /** + * Asynchronously prepare the file at `path` for synchronous reading. + * This method is called by wasm code (via the bindings). + * + * @param {string} path + * @returns {FileHandle} + */ + async readFile(path) { + const info = await IOUtils.stat(path); + if (info.type === "directory") { + throw new Error(`Path "${path}" is a directory.`); + } + + return IOUtils.openFileForSyncReading(path); + } +} + +/** @param {MessageEvent} e */ +onmessage = async e => { + try { + const { request, libInfoMap, objdirs, module } = e.data; + + if (!(module instanceof WebAssembly.Module)) { + throw new Error("invalid WebAssembly module"); + } + + // Instantiate the WASM module. + await wasm_bindgen(module); + + const helper = new FileAndPathHelper(libInfoMap, objdirs); + + switch (request.type) { + case "GET_SYMBOL_TABLE": { + const { debugName, breakpadId } = request; + const result = await getCompactSymbolTable( + debugName, + breakpadId, + helper + ); + postMessage( + { result }, + result.map(r => r.buffer) + ); + break; + } + case "QUERY_SYMBOLICATION_API": { + const { path, requestJson } = request; + const result = await queryAPI(path, requestJson, helper); + postMessage({ result }); + break; + } + default: + throw new Error(`Unexpected request type ${request.type}`); + } + } catch (error) { + postMessage({ error: createPlainErrorObject(error) }); + } + close(); +}; + +onunhandledrejection = e => { + // Unhandled rejections can happen if the WASM code throws a + // "RuntimeError: unreachable executed" exception, which can happen + // if the Rust code panics or runs out of memory. + // These panics currently are not propagated to the promise reject + // callback, see https://github.com/rustwasm/wasm-bindgen/issues/2724 . + // Ideally, the Rust code should never panic and handle all error + // cases gracefully. + e.preventDefault(); + postMessage({ error: createPlainErrorObject(e.reason) }); +}; + +// Catch any other unhandled errors, just to be sure. +onerror = e => { + postMessage({ error: createPlainErrorObject(e) }); +}; -- cgit v1.2.3