From 124dd3228fed6a92bfad5356861186fc66c5cab1 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 30 Jul 2021 20:39:54 +0000 Subject: [PATCH] Bug 1615066 - Add support for new profiler-get-symbols version which supports partial file reading. r=canaltinova Differential Revision: https://phabricator.services.mozilla.com/D117938 --- .../client/performance-new/@types/perf.d.ts | 13 + .../performance-new/profiler_get_symbols.js | 736 +++++++++--------- .../performance-new/symbolication-worker.js | 198 ++--- .../performance-new/symbolication.jsm.js | 9 +- 4 files changed, 485 insertions(+), 471 deletions(-) diff --git a/devtools/client/performance-new/@types/perf.d.ts b/devtools/client/performance-new/@types/perf.d.ts index 8da10303ad71..a2ad285fb169 100644 --- a/devtools/client/performance-new/@types/perf.d.ts +++ b/devtools/client/performance-new/@types/perf.d.ts @@ -544,3 +544,16 @@ export type SymbolicationWorkerReplyData = | { error: SymbolicationWorkerError; }; + +// This type is used in the symbolication worker for the return type of the +// FileAndPathHelper's readFile method. +// FIXME: Or rather, this type *would* be used if the worker code was checked +// by TypeScript. +export interface FileHandle { + // Return the length of the file in bytes. + getLength: () => number; + // Synchronously read the bytes at offset `offset` into the array `dest`. + readBytesInto: (dest: Uint8Array, offset: number) => void; + // Called when the file is no longer needed, to allow closing the file. + drop: () => void; +} diff --git a/devtools/client/performance-new/profiler_get_symbols.js b/devtools/client/performance-new/profiler_get_symbols.js index 5f498a5a73d6..849cb46c48fc 100644 --- a/devtools/client/performance-new/profiler_get_symbols.js +++ b/devtools/client/performance-new/profiler_get_symbols.js @@ -6,213 +6,32 @@ // THIS FILE IS AUTOGENERATED by wasm-bindgen. // // Generated from: -// https://github.com/mstange/profiler-get-symbols/commit/90ee39f1d18d2727f07dc57bd93cff6bc73ce8a0 +// https://github.com/mstange/profiler-get-symbols/commit/c1dca28a2a506df40f0a6f32c12ba51ec54b02be // by following the instructions in that repository's Readme.md // +let wasm_bindgen; (function() { const __exports = {}; let wasm; - let cachegetInt32Memory = null; - function getInt32Memory() { - if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) { - cachegetInt32Memory = new Int32Array(wasm.memory.buffer); - } - return cachegetInt32Memory; - } - - let cachegetUint32Memory = null; - function getUint32Memory() { - if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { - cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); - } - return cachegetUint32Memory; - } - - function getArrayU32FromWasm(ptr, len) { - return getUint32Memory().subarray(ptr / 4, ptr / 4 + len); - } - - let cachegetUint8Memory = null; - function getUint8Memory() { - if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { - cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); - } - return cachegetUint8Memory; - } - - function getArrayU8FromWasm(ptr, len) { - return getUint8Memory().subarray(ptr / 1, ptr / 1 + len); - } - - const heap = new Array(32); - - heap.fill(undefined); + const heap = new Array(32).fill(undefined); heap.push(undefined, null, true, false); - let stack_pointer = 32; - - function addBorrowedObject(obj) { - if (stack_pointer == 1) throw new Error('out of js stack'); - heap[--stack_pointer] = obj; - return stack_pointer; - } - - function _assertClass(instance, klass) { - if (!(instance instanceof klass)) { - throw new Error(`expected instance of ${klass.name}`); - } - return instance.ptr; - } - - let WASM_VECTOR_LEN = 0; - - let cachedTextEncoder = new TextEncoder('utf-8'); - - let passStringToWasm; - if (typeof cachedTextEncoder.encodeInto === 'function') { - passStringToWasm = function(arg) { - - - let size = arg.length; - let ptr = wasm.__wbindgen_malloc(size); - let offset = 0; - { - const mem = getUint8Memory(); - for (; offset < arg.length; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - } - - if (offset !== arg.length) { - arg = arg.slice(offset); - ptr = wasm.__wbindgen_realloc(ptr, size, size = offset + arg.length * 3); - const view = getUint8Memory().subarray(ptr + offset, ptr + size); - const ret = cachedTextEncoder.encodeInto(arg, view); - - offset += ret.written; - } - WASM_VECTOR_LEN = offset; - return ptr; - }; - } else { - passStringToWasm = function(arg) { - - - let size = arg.length; - let ptr = wasm.__wbindgen_malloc(size); - let offset = 0; - { - const mem = getUint8Memory(); - for (; offset < arg.length; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - } - - if (offset !== arg.length) { - const buf = cachedTextEncoder.encode(arg.slice(offset)); - ptr = wasm.__wbindgen_realloc(ptr, size, size = offset + buf.length); - getUint8Memory().set(buf, ptr + offset); - offset += buf.length; - } - WASM_VECTOR_LEN = offset; - return ptr; - }; - } - /** - * @param {WasmMemBuffer} binary_data - * @param {WasmMemBuffer} debug_data - * @param {string} breakpad_id - * @returns {CompactSymbolTable} - */ - __exports.get_compact_symbol_table = function(binary_data, debug_data, breakpad_id) { - _assertClass(binary_data, WasmMemBuffer); - _assertClass(debug_data, WasmMemBuffer); - const ret = wasm.get_compact_symbol_table(binary_data.ptr, debug_data.ptr, passStringToWasm(breakpad_id), WASM_VECTOR_LEN); - return CompactSymbolTable.__wrap(ret); - }; - function getObject(idx) { return heap[idx]; } -function debugString(val) { - // primitive types - const type = typeof val; - if (type == 'number' || type == 'boolean' || val == null) { - return `${val}`; - } - if (type == 'string') { - return `"${val}"`; - } - if (type == 'symbol') { - const description = val.description; - if (description == null) { - return 'Symbol'; - } else { - return `Symbol(${description})`; - } - } - if (type == 'function') { - const name = val.name; - if (typeof name == 'string' && name.length > 0) { - return `Function(${name})`; - } else { - return 'Function'; - } - } - // objects - if (Array.isArray(val)) { - const length = val.length; - let debug = '['; - if (length > 0) { - debug += debugString(val[0]); - } - for(let i = 1; i < length; i++) { - debug += ', ' + debugString(val[i]); - } - debug += ']'; - return debug; - } - // Test for built-in - const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); - let className; - if (builtInMatches.length > 1) { - className = builtInMatches[1]; - } else { - // Failed to match the standard '[object ClassName]' - return toString.call(val); - } - if (className == 'Object') { - // we're a user defined class or Object - // JSON.stringify avoids problems with cycles, and is generally much - // easier than looping through ownProperties of `val`. - try { - return 'Object(' + JSON.stringify(val) + ')'; - } catch (_) { - return 'Object'; - } - } - // errors - if (val instanceof Error) { - return `${val.name}: ${val.message}\n${val.stack}`; - } - // TODO we could test for more things here, like `Set`s and `Map`s. - return className; -} - -let cachedTextDecoder = new TextDecoder('utf-8'); - -function getStringFromWasm(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); -} - let heap_next = heap.length; +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + function dropObject(idx) { if (idx < 36) return; heap[idx] = heap_next; @@ -225,202 +44,413 @@ function takeObject(idx) { return ret; } -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - heap[idx] = obj; - return idx; +cachedTextDecoder.decode(); + +let cachegetUint8Memory0 = null; +function getUint8Memory0() { + if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { + cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachegetUint8Memory0; } -function handleError(e) { - wasm.__wbindgen_exn_store(addHeapObject(e)); +function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } -/** -*/ -class CompactSymbolTable { - static __wrap(ptr) { - const obj = Object.create(CompactSymbolTable.prototype); - obj.ptr = ptr; +let WASM_VECTOR_LEN = 0; - return obj; - } +let cachedTextEncoder = new TextEncoder('utf-8'); - free() { - const ptr = this.ptr; - this.ptr = 0; - - wasm.__wbg_compactsymboltable_free(ptr); - } - /** - * @returns {CompactSymbolTable} - */ - constructor() { - const ret = wasm.compactsymboltable_new(); - return CompactSymbolTable.__wrap(ret); - } - /** - * @returns {Uint32Array} - */ - take_addr() { - const retptr = 8; - const ret = wasm.compactsymboltable_take_addr(retptr, this.ptr); - const memi32 = getInt32Memory(); - const v0 = getArrayU32FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice(); - wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 4); - return v0; - } - /** - * @returns {Uint32Array} - */ - take_index() { - const retptr = 8; - const ret = wasm.compactsymboltable_take_index(retptr, this.ptr); - const memi32 = getInt32Memory(); - const v0 = getArrayU32FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice(); - wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 4); - return v0; - } - /** - * @returns {Uint8Array} - */ - take_buffer() { - const retptr = 8; - const ret = wasm.compactsymboltable_take_buffer(retptr, this.ptr); - const memi32 = getInt32Memory(); - const v0 = getArrayU8FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice(); - wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 1); - return v0; - } +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); } -__exports.CompactSymbolTable = CompactSymbolTable; -/** -* WasmMemBuffer lets you allocate a chunk of memory on the wasm heap and -* directly initialize it from JS without a copy. The constructor takes the -* allocation size and a callback function which does the initialization. -* This is useful if you need to get very large amounts of data from JS into -* wasm (for example, the contents of a 1.7GB libxul.so). -*/ -class WasmMemBuffer { + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); - static __wrap(ptr) { - const obj = Object.create(WasmMemBuffer.prototype); - obj.ptr = ptr; +function passStringToWasm0(arg, malloc, realloc) { - return obj; + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length); + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; } - free() { - const ptr = this.ptr; - this.ptr = 0; + let len = arg.length; + let ptr = malloc(len); - wasm.__wbg_wasmmembuffer_free(ptr); + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; } - /** - * Create the buffer and initialize it synchronously in the callback function. - * f is called with one argument: the Uint8Array that wraps our buffer. - * f should not return anything; its return value is ignored. - * f must not call any exported wasm functions! Anything that causes the - * wasm heap to resize will invalidate the typed array\'s internal buffer! - * Do not hold on to the array that is passed to f after f completes. - * @param {number} byte_length - * @param {any} f - * @returns {WasmMemBuffer} - */ - constructor(byte_length, f) { + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3); + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachegetInt32Memory0 = null; +function getInt32Memory0() { + if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { + cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachegetInt32Memory0; +} + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; try { - const ret = wasm.wasmmembuffer_new(byte_length, addBorrowedObject(f)); - return WasmMemBuffer.__wrap(ret); + return f(a, state.b, ...args); } finally { - heap[stack_pointer++] = undefined; + if (--state.cnt === 0) { + wasm.__wbindgen_export_2.get(state.dtor)(a, state.b); + + } else { + state.a = a; + } + } + }; + real.original = state; + + return real; +} +function __wbg_adapter_18(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0f6e06591fd1eecd(arg0, arg1, addHeapObject(arg2)); +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} +function __wbg_adapter_33(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen__convert__closures__invoke2_mut__hb11591fa7d9b8977(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +} + +/** +* Usage: +* +* ```js +* async function getSymbolTable(debugName, breakpadId, libKeyToPathMap) { +* const helper = { +* getCandidatePathsForBinaryOrPdb: (debugName, breakpadId) => { +* const path = libKeyToPathMap.get(`${debugName}/${breakpadId}`); +* if (path !== undefined) { +* return [path]; +* } +* return []; +* }, +* readFile: async (filename) => { +* const byteLength = await getFileSizeInBytes(filename); +* const fileHandle = getFileHandle(filename); +* return { +* getLength: () => byteLength, +* readBytesInto: (array, offset) => { +* syncReadFilePartIntoBuffer(fileHandle, array, offset); +* }, +* drop: () => {}, +* }; +* }, +* }; +* +* const [addr, index, buffer] = await getCompactSymbolTable(debugName, breakpadId, helper); +* return [addr, index, buffer]; +* } +* ``` +* @param {string} debug_name +* @param {string} breakpad_id +* @param {any} helper +* @returns {Promise} +*/ +__exports.getCompactSymbolTable = function(debug_name, breakpad_id, helper) { + var ptr0 = passStringToWasm0(debug_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + var ptr1 = passStringToWasm0(breakpad_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + var ret = wasm.getCompactSymbolTable(ptr0, len0, ptr1, len1, addHeapObject(helper)); + return takeObject(ret); +}; + +/** +* Usage: +* +* ```js +* async function getSymbolTable(url, requestJSONString, libKeyToPathMap) { +* const helper = { +* getCandidatePathsForBinaryOrPdb: (debugName, breakpadId) => { +* const path = libKeyToPathMap.get(`${debugName}/${breakpadId}`); +* if (path !== undefined) { +* return [path]; +* } +* return []; +* }, +* readFile: async (filename) => { +* const byteLength = await getFileSizeInBytes(filename); +* const fileHandle = getFileHandle(filename); +* return { +* getLength: () => byteLength, +* readBytesInto: (array, offset) => { +* syncReadFilePartIntoBuffer(fileHandle, array, offset); +* }, +* drop: () => {}, +* }; +* }, +* }; +* +* const responseJSONString = await queryAPI(deburlugName, requestJSONString, helper); +* return responseJSONString; +* } +* ``` +* @param {string} url +* @param {string} request_json +* @param {any} helper +* @returns {Promise} +*/ +__exports.queryAPI = function(url, request_json, helper) { + var ptr0 = passStringToWasm0(url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + var ptr1 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + var ret = wasm.queryAPI(ptr0, len0, ptr1, len1, addHeapObject(helper)); + return takeObject(ret); +}; + +async function load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; } } } -__exports.WasmMemBuffer = WasmMemBuffer; -function init(module) { - - let result; +async function init(input) { + if (typeof input === 'undefined') { + let src; + if (typeof document === 'undefined') { + src = location.href; + } else { + src = document.currentScript.src; + } + input = src.replace(/\.js$/, '_bg.wasm'); + } const imports = {}; imports.wbg = {}; - imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { - const ret = debugString(getObject(arg1)); - const ret0 = passStringToWasm(ret); - const ret1 = WASM_VECTOR_LEN; - getInt32Memory()[arg0 / 4 + 0] = ret0; - getInt32Memory()[arg0 / 4 + 1] = ret1; - }; - imports.wbg.__wbindgen_throw = function(arg0, arg1) { - throw new Error(getStringFromWasm(arg0, arg1)); - }; - imports.wbg.__wbindgen_rethrow = function(arg0) { - throw takeObject(arg0); - }; - imports.wbg.__wbindgen_memory = function() { - const ret = wasm.memory; - return addHeapObject(ret); - }; - imports.wbg.__wbg_buffer_aa8ebea80955a01a = function(arg0) { - const ret = getObject(arg0).buffer; - return addHeapObject(ret); - }; - imports.wbg.__wbg_newwithbyteoffsetandlength_3e607c21646a8aef = function(arg0, arg1, arg2) { - const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + imports.wbg.__wbindgen_object_clone_ref = function(arg0) { + var ret = getObject(arg0); return addHeapObject(ret); }; + imports.wbg.__wbg_drop_6095f5f9a28379e4 = function() { return handleError(function (arg0) { + getObject(arg0).drop(); + }, arguments) }; imports.wbg.__wbindgen_object_drop_ref = function(arg0) { takeObject(arg0); }; - imports.wbg.__wbg_call_9c879b23724d007e = function(arg0, arg1, arg2) { - try { - const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - } catch (e) { - handleError(e) + imports.wbg.__wbg_call_3fc07b7d5fc9022d = function() { return handleError(function (arg0, arg1, arg2) { + var ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + var ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); + }; + imports.wbg.__wbg_readFile_1005cc171b90cf75 = function(arg0, arg1, arg2) { + var ret = getObject(arg0).readFile(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_then_6c9a4bf55755f9b8 = function(arg0, arg1, arg2) { + var ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_cb_drop = function(arg0) { + const obj = takeObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; } + var ret = false; + return ret; + }; + imports.wbg.__wbg_getLength_83c461bbda972f3b = function() { return handleError(function (arg0) { + var ret = getObject(arg0).getLength(); + return ret; + }, arguments) }; + imports.wbg.__wbg_name_966f168ad0e59c92 = function(arg0) { + var ret = getObject(arg0).name; + return addHeapObject(ret); + }; + imports.wbg.__wbg_message_e440fbd911a845a2 = function(arg0) { + var ret = getObject(arg0).message; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_memory = function() { + var ret = wasm.memory; + return addHeapObject(ret); + }; + imports.wbg.__wbg_buffer_9e184d6f785de5ed = function(arg0) { + var ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_d92dfdd78c30c55a = function(arg0, arg1, arg2) { + var ret = new Uint32Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_71de2365d17503e7 = function(arg0) { + var ret = new Uint32Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_e57ad1f2ce812c03 = function(arg0, arg1, arg2) { + var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_e8101319e4cf95fc = function(arg0) { + var ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_of_7c797480f9ceac60 = function(arg0, arg1, arg2) { + var ret = Array.of(getObject(arg0), getObject(arg1), getObject(arg2)); + return addHeapObject(ret); }; imports.wbg.__wbindgen_json_parse = function(arg0, arg1) { - const ret = JSON.parse(getStringFromWasm(arg0, arg1)); + var ret = JSON.parse(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_readBytesInto_02ee0cacc563822d = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).readBytesInto(takeObject(arg1), arg2); + }, arguments) }; + imports.wbg.__wbg_getCandidatePathsForBinaryOrPdb_6ff1ea4b13b7cbee = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + var ret = getObject(arg0).getCandidatePathsForBinaryOrPdb(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_from_f4f6c9225e58242f = function(arg0) { + var ret = Array.from(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_length_555f836564bf148d = function(arg0) { + var ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_get_b7bbf50adcc94294 = function(arg0, arg1) { + var ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_c143a4f563f78c4e = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return __wbg_adapter_33(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + var ret = new Promise(cb0); + return addHeapObject(ret); + } finally { + state0.a = state0.b = 0; + } + }; + imports.wbg.__wbindgen_string_get = function(arg0, arg1) { + const obj = getObject(arg1); + var ret = typeof(obj) === 'string' ? obj : undefined; + var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbg_then_c2361a9d5c9a4fcb = function(arg0, arg1) { + var ret = getObject(arg0).then(getObject(arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_resolve_cae3d8f752f5db88 = function(arg0) { + var ret = Promise.resolve(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper230 = function(arg0, arg1, arg2) { + var ret = makeMutClosure(arg0, arg1, 70, __wbg_adapter_18); return addHeapObject(ret); }; - if (module instanceof URL || typeof module === 'string' || module instanceof Request) { - - const response = fetch(module); - if (typeof WebAssembly.instantiateStreaming === 'function') { - result = WebAssembly.instantiateStreaming(response, imports) - .catch(e => { - console.warn("`WebAssembly.instantiateStreaming` failed. Assuming this is because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - return response - .then(r => r.arrayBuffer()) - .then(bytes => WebAssembly.instantiate(bytes, imports)); - }); - } else { - result = response - .then(r => r.arrayBuffer()) - .then(bytes => WebAssembly.instantiate(bytes, imports)); - } - } else { - - result = WebAssembly.instantiate(module, imports) - .then(result => { - if (result instanceof WebAssembly.Instance) { - return { instance: result, module }; - } else { - return result; - } - }); + if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { + input = fetch(input); } - return result.then(({instance, module}) => { - wasm = instance.exports; - init.__wbindgen_wasm_module = module; - return wasm; - }); + + + const { instance, module } = await load(await input, imports); + + wasm = instance.exports; + init.__wbindgen_wasm_module = module; + + return wasm; } -self.wasm_bindgen = Object.assign(init, __exports); +wasm_bindgen = Object.assign(init, __exports); })(); diff --git a/devtools/client/performance-new/symbolication-worker.js b/devtools/client/performance-new/symbolication-worker.js index f7f941283e1a..dca86bfbcaf6 100644 --- a/devtools/client/performance-new/symbolication-worker.js +++ b/devtools/client/performance-new/symbolication-worker.js @@ -19,6 +19,7 @@ importScripts( /** * @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. @@ -29,60 +30,55 @@ importScripts( // itself. /* eslint camelcase: 0*/ -const { WasmMemBuffer, get_compact_symbol_table } = wasm_bindgen; +const { getCompactSymbolTable } = wasm_bindgen; -// Read an open OS.File instance into the Uint8Array dataBuf. -function readFileInto(file, dataBuf) { - // Ideally we'd be able to call file.readTo(dataBuf) here, but readTo no - // longer exists. - // So instead, we copy the file over into wasm memory in 4MB chunks. This - // will take 425 invocations for a a 1.7GB file (such as libxul.so for a - // Firefox for Android build) and not take up too much memory per call. - const dataBufLen = dataBuf.byteLength; +// Read parts of an open OS.File instance into the Uint8Array dataBuf. +// This reads destBuf.byteLength bytes at offset offset. +function readFileBytesInto(file, offset, destBuf) { + file.setPosition(offset); + // We read the file in 4MB chunks. This limits the amount of temporary + // memory we use while we copy the file contents into wasm memory. const chunkSize = 4 * 1024 * 1024; - let pos = 0; - while (pos < dataBufLen) { - const chunkData = file.read({ bytes: chunkSize }); + let posInDestBuf = 0; + let remainingBytes = destBuf.byteLength; + while (remainingBytes > 0) { + const bytes = remainingBytes > chunkSize ? chunkSize : remainingBytes; + const chunkData = file.read({ bytes }); const chunkBytes = chunkData.byteLength; if (chunkBytes === 0) { break; } - dataBuf.set(chunkData, pos); - pos += chunkBytes; + destBuf.set(chunkData, posInDestBuf); + posInDestBuf += chunkBytes; + remainingBytes -= chunkBytes; } } // Returns a plain object that is Structured Cloneable and has name and -// description properties. +// message properties. function createPlainErrorObject(e) { - // OS.File.Error has an empty message property; it constructs the error - // message on-demand in its toString() method. So we handle those errors - // specially. - if (!(e instanceof OS.File.Error)) { - // Regular errors: just rewrap the object. - if (e instanceof Error) { - const { name, message, fileName, lineNumber } = e; - return { name, message, fileName, lineNumber }; - } - // The WebAssembly code throws errors with fields error_type and error_msg. - if (e.error_type) { - return { - name: e.error_type, - message: e.error_msg, - }; - } + if (e instanceof OS.File.Error) { + // OS.File.Error has an empty message property; it constructs the error + // message on-demand in its toString() method. So we handle those errors + // specially. + return { + name: "OSFileError", + message: e.toString(), + fileName: e.fileName, + lineNumber: e.lineNumber, + }; } - - return { - name: e instanceof OS.File.Error ? "OSFileError" : "Error", - message: e.toString(), - fileName: e.fileName, - lineNumber: e.lineNumber, - }; + // Regular errors: just rewrap the object. + const { name, message, fileName, lineNumber } = e; + return { name, message, fileName, lineNumber }; } -class PathHelper { +/** + * 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; @@ -90,12 +86,13 @@ class PathHelper { /** * Enumerate all paths at which we could find files with symbol information. + * This method is called by wasm code (via the bindings). * * @param {string} debugName * @param {string} breakpadId - * @returns {Array<{ path: string, debugPath: string }>} + * @returns {Array} */ - getCandidatePaths(debugName, breakpadId) { + getCandidatePathsForBinaryOrPdb(debugName, breakpadId) { const key = `${debugName}:${breakpadId}`; const lib = this._libInfoMap.get(key); if (!lib) { @@ -121,91 +118,66 @@ class PathHelper { // a system library. for (const objdirPath of this._objdirs) { // Binaries are usually expected to exist at objdir/dist/bin/filename. - candidatePaths.push({ - path: OS.Path.join(objdirPath, "dist", "bin", name), - debugPath: OS.Path.join(objdirPath, "dist", "bin", name), - }); + candidatePaths.push(OS.Path.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({ - path: OS.Path.join(objdirPath, name), - debugPath: OS.Path.join(objdirPath, name), - }); + candidatePaths.push(OS.Path.join(objdirPath, name)); } - // Check the absolute paths of the library file(s) last. + // 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. - candidatePaths.push({ path, debugPath }); + 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); return candidatePaths; } -} -function getCompactSymbolTableFromPath(binaryPath, debugPath, breakpadId) { - // Read the binary file into WASM memory. - const binaryFile = OS.File.open(binaryPath, { read: true }); - const binaryData = new WasmMemBuffer(binaryFile.stat().size, array => { - readFileInto(binaryFile, array); - }); - binaryFile.close(); - - // Do the same for the debug file, if it is supplied and different from the - // binary file. This is only the case on Windows. - let debugData = binaryData; - if (debugPath && debugPath !== binaryPath) { - const debugFile = OS.File.open(debugPath, { read: true }); - debugData = new WasmMemBuffer(debugFile.stat().size, array => { - readFileInto(debugFile, array); - }); - debugFile.close(); - } - - try { - const output = get_compact_symbol_table(binaryData, debugData, breakpadId); - const result = [ - output.take_addr(), - output.take_index(), - output.take_buffer(), - ]; - output.free(); - return result; - } finally { - binaryData.free(); - if (debugData != binaryData) { - debugData.free(); + /** + * 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 file = OS.File.open(path, { read: true }); + const info = file.stat(); + if (info.isDir) { + throw new Error(`Path "${path}" is a directory.`); } + + const fileSize = info.size; + + // Create and return a FileHandle object. The methods of this object are + // called by wasm code (via the bindings). + return { + getLength: () => fileSize, + readBytesInto: (dest, offset) => { + readFileBytesInto(file, offset, dest); + }, + drop: () => { + file.close(); + }, + }; } } -function getSymbolTableInWorker(debugName, breakpadId, libInfoMap, objdirs) { - const helper = new PathHelper(libInfoMap, objdirs); - const candidatePaths = helper.getCandidatePaths(debugName, breakpadId); - - const errors = []; - for (const { path, debugPath } of candidatePaths) { - try { - return getCompactSymbolTableFromPath(path, debugPath, breakpadId); - } catch (e) { - // getCompactSymbolTableFromPath was unsuccessful. So either the - // file wasn't parseable or its contents didn't match the specified - // breakpadId, or some other error occurred. - // Advance to the next candidate path. - errors.push(e); - } - } - - throw new Error( - `Could not obtain symbols for the library ${debugName} ${breakpadId} ` + - `because there was no matching file at any of the candidate paths: ${JSON.stringify( - candidatePaths - )}. Errors: ${errors.map(e => e.message).join(", ")}` - ); -} - /** @param {MessageEvent} e */ onmessage = async e => { try { @@ -218,12 +190,8 @@ onmessage = async e => { // Instantiate the WASM module. await wasm_bindgen(module); - const result = getSymbolTableInWorker( - debugName, - breakpadId, - libInfoMap, - objdirs - ); + const helper = new FileAndPathHelper(libInfoMap, objdirs); + const result = await getCompactSymbolTable(debugName, breakpadId, helper); postMessage( { result }, result.map(r => r.buffer) diff --git a/devtools/client/performance-new/symbolication.jsm.js b/devtools/client/performance-new/symbolication.jsm.js index afee4d95b4a3..9f91ce98adb7 100644 --- a/devtools/client/performance-new/symbolication.jsm.js +++ b/devtools/client/performance-new/symbolication.jsm.js @@ -45,12 +45,15 @@ const global = this; // 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/ . +// Documentation is at https://docs.rs/profiler-get-symbols/ . +// The sha384 sum can be computed with the following command (tested on macOS): +// shasum -b -a 384 profiler_get_symbols_wasm_bg.wasm | awk '{ print $1 }' | xxd -r -p | base64 -// Generated from https://github.com/mstange/profiler-get-symbols/commit/90ee39f1d18d2727f07dc57bd93cff6bc73ce8a0 +// Generated from https://github.com/mstange/profiler-get-symbols/commit/c1dca28a2a506df40f0a6f32c12ba51ec54b02be const WASM_MODULE_URL = - "https://zealous-rosalind-a98ce8.netlify.com/wasm/8f7ca2f70e1cd21b5a2dbe96545672752887bfbd4e7b3b9437e9fc7c3da0a3bedae4584ff734f0c9f08c642e6b66ffab.wasm"; + "https://storage.googleapis.com/firefox-profiler-get-symbols/c1dca28a2a506df40f0a6f32c12ba51ec54b02be.wasm"; const WASM_MODULE_INTEGRITY = - "sha384-j3yi9w4c0htaLb6WVFZydSiHv71OezuUN+n8fD2go77a5FhP9zTwyfCMZC5rZv+r"; + "sha384-ZWi2jwcKJr20rE2gmHjFQGHgsCF9CagkyPLsrIZfmf5QKD2oXgkLa8tMKHK6zPA1"; const EXPIRY_TIME_IN_MS = 5 * 60 * 1000; // 5 minutes