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:
+3
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user