From 1b36a2e3f47b6b95fb3b65aedc3a82da5cd6f008 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 18 Jul 2018 08:47:58 -0700 Subject: [PATCH] add support for "wasm-function" symbol maps (#93) This is an improvement to #76, which added support for asm.js symbol maps. This PR expands this to also work for emscripten's WebAssembly symbol maps too. This currently only works in Firefox. Chrome would need to fix https://crbug.com/863205 for this to be useful in Chrome. --- application.tsx | 6 ++--- asm-js.ts | 26 ------------------- asm-js.test.ts => emscripten.test.ts | 33 ++++++++++++++++------- emscripten.ts | 39 ++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 39 deletions(-) delete mode 100644 asm-js.ts rename asm-js.test.ts => emscripten.test.ts (57%) create mode 100644 emscripten.ts diff --git a/application.tsx b/application.tsx index 7abdc4b..71b2378 100644 --- a/application.tsx +++ b/application.tsx @@ -16,7 +16,7 @@ import {SortMethod, SortField, SortDirection} from './profile-table-view' import {triangle} from './utils' import {Color} from './color' import {RowAtlas} from './row-atlas' -import {importAsmJsSymbolMap} from './asm-js' +import {importEmscriptenSymbolMap} from './emscripten' import {SandwichView} from './sandwich-view' import {saveToFile} from './file-format' @@ -384,9 +384,9 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { // a symbol map. If that's the case, we want to parse it, and apply the symbol // mapping to the already loaded profile. This can be use to take an opaque // profile and make it readable. - const map = importAsmJsSymbolMap(reader.result) + const map = importEmscriptenSymbolMap(reader.result) if (map) { - console.log('Importing as asm.js symbol map') + console.log('Importing as emscripten symbol map') let profile = this.state.profile profile.remapNames(name => map.get(name) || name) return profile diff --git a/asm-js.ts b/asm-js.ts deleted file mode 100644 index 33e517f..0000000 --- a/asm-js.ts +++ /dev/null @@ -1,26 +0,0 @@ -type AsmJsSymbolMap = Map - -// This imports symbol maps generated by emscripten using the "--emit-symbol-map" flag. -// It allows you to visualize a profile captured in a release build as long as you also -// have the associated symbol map. To do this, first drop the profile into speedscope -// and then drop the symbol map. After the second drop, the symbols will be remapped to -// their original names. -export function importAsmJsSymbolMap(contents: string): AsmJsSymbolMap | null { - const lines = contents.split('\n') - if (!lines.length) return null - - // Remove a trailing blank line if there is one - if (lines[lines.length - 1] === '') lines.pop() - if (!lines.length) return null - - const map: AsmJsSymbolMap = new Map() - const regex = /^([\$\w]+):([\$\w]+)$/ - - for (const line of lines) { - const match = regex.exec(line) - if (!match) return null - map.set(match[1], match[2]) - } - - return map -} diff --git a/asm-js.test.ts b/emscripten.test.ts similarity index 57% rename from asm-js.test.ts rename to emscripten.test.ts index c963788..f16d06b 100644 --- a/asm-js.test.ts +++ b/emscripten.test.ts @@ -1,9 +1,9 @@ -import {importAsmJsSymbolMap} from './asm-js' +import {importEmscriptenSymbolMap} from './emscripten' -test('importAsmJSSymbolMap', () => { +test('importEmscriptenSymbolMap', () => { // Valid symbol map expect( - importAsmJsSymbolMap( + importEmscriptenSymbolMap( [ /* prettier: ignore */ 'a:A', @@ -15,7 +15,7 @@ test('importAsmJSSymbolMap', () => { // Valid symbol map with trailing newline expect( - importAsmJsSymbolMap( + importEmscriptenSymbolMap( [ /* prettier: ignore */ 'a:A', @@ -27,14 +27,27 @@ test('importAsmJSSymbolMap', () => { ).toEqual(new Map([['a', 'A'], ['b', 'B'], ['c', 'C']])) // Valid symbol map with non-alpha characters - - expect(importAsmJsSymbolMap('u6:__ZN8tinyxml210XMLCommentD0Ev\n')).toEqual( + expect(importEmscriptenSymbolMap('u6:__ZN8tinyxml210XMLCommentD0Ev\n')).toEqual( new Map([['u6', '__ZN8tinyxml210XMLCommentD0Ev']]), ) + // WebAssembly symbol map + expect( + importEmscriptenSymbolMap( + [ + /* prettier: ignore */ + '0:A', + '1:B', + '2:C', + ].join('\n'), + ), + ).toEqual( + new Map([['wasm-function[0]', 'A'], ['wasm-function[1]', 'B'], ['wasm-function[2]', 'C']]), + ) + // Invalid symbol map expect( - importAsmJsSymbolMap( + importEmscriptenSymbolMap( [ /* prettier: ignore */ 'a:A', @@ -47,7 +60,7 @@ test('importAsmJSSymbolMap', () => { // Collapsed stack format should not be imported as an asm.js symbol map expect( - importAsmJsSymbolMap( + importEmscriptenSymbolMap( [ /* prettier: ignore */ 'a;b 1', @@ -58,6 +71,6 @@ test('importAsmJSSymbolMap', () => { ).toEqual(null) // Unrelated files - expect(importAsmJsSymbolMap('')).toEqual(null) - expect(importAsmJsSymbolMap('\n')).toEqual(null) + expect(importEmscriptenSymbolMap('')).toEqual(null) + expect(importEmscriptenSymbolMap('\n')).toEqual(null) }) diff --git a/emscripten.ts b/emscripten.ts new file mode 100644 index 0000000..d0ac9a2 --- /dev/null +++ b/emscripten.ts @@ -0,0 +1,39 @@ +type EmscriptenSymbolMap = Map + +// This imports symbol maps generated by emscripten using the "--emit-symbol-map" flag. +// It allows you to visualize a profile captured in a release build as long as you also +// have the associated symbol map. To do this, first drop the profile into speedscope +// and then drop the symbol map. After the second drop, the symbols will be remapped to +// their original names. +export function importEmscriptenSymbolMap(contents: string): EmscriptenSymbolMap | null { + const lines = contents.split('\n') + if (!lines.length) return null + + // Remove a trailing blank line if there is one + if (lines[lines.length - 1] === '') lines.pop() + if (!lines.length) return null + + const map: EmscriptenSymbolMap = new Map() + const intRegex = /^(\d+):([\$\w]+)$/ + const idRegex = /^([\$\w]+):([\$\w]+)$/ + + for (const line of lines) { + // Match lines like "103:__ZN8tinyxml210XMLCommentD0Ev" + const intMatch = intRegex.exec(line) + if (intMatch) { + map.set(`wasm-function[${intMatch[1]}]`, intMatch[2]) + continue + } + + // Match lines like "u6:__ZN8tinyxml210XMLCommentD0Ev" + const idMatch = idRegex.exec(line) + if (idMatch) { + map.set(idMatch[1], idMatch[2]) + continue + } + + return null + } + + return map +}