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.
This commit is contained in:
Evan Wallace
2018-07-18 08:47:58 -07:00
committed by Jamie Wong
parent 8fcc0f8108
commit 1b36a2e3f4
4 changed files with 65 additions and 39 deletions
+3 -3
View File
@@ -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
-26
View File
@@ -1,26 +0,0 @@
type AsmJsSymbolMap = Map<string, string>
// 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
}
+23 -10
View File
@@ -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)
})
+39
View File
@@ -0,0 +1,39 @@
type EmscriptenSymbolMap = Map<string, string>
// 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
}