Skip to content

Commit

Permalink
Add support for dropping asm.js symbol maps (#76)
Browse files Browse the repository at this point in the history
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.

This is a fixed up version of #75
  • Loading branch information
jlfwong committed Jun 25, 2018
1 parent 27bc4c7 commit 7f7f5ee
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 1 deletion.
21 changes: 20 additions & 1 deletion application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {ProfileTableView, SortMethod, SortField, SortDirection} from './profile-
import {triangle} from './utils'
import {Color} from './color'
import {RowAtlas} from './row-atlas'
import {importAsmJsSymbolMap} from './asm-js'

declare function require(x: string): any
const exampleProfileURL = require('./sample/profiles/stackcollapse/perf-vertx-stacks-01-collapsed-all.txt')
Expand Down Expand Up @@ -413,7 +414,25 @@ export class Application extends ReloadableComponent<{}, ApplicationState> {
profile.setName(file.name)
}
resolve(profile)
} else reject()
return
}

if (this.state.profile) {
// If a profile is already loaded, it's possible the file being imported is
// 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)
if (map) {
console.log('Importing as asm.js symbol map')
let profile = this.state.profile
profile.remapNames(name => map.get(name) || name)
resolve(profile)
return
}
}

reject()
})
reader.readAsText(file)
}),
Expand Down
63 changes: 63 additions & 0 deletions asm-js.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {importAsmJsSymbolMap} from './asm-js'

test('importAsmJSSymbolMap', () => {
// Valid symbol map
expect(
importAsmJsSymbolMap(
[
/* prettier: ignore */
'a:A',
'b:B',
'c:C',
].join('\n'),
),
).toEqual(new Map([['a', 'A'], ['b', 'B'], ['c', 'C']]))

// Valid symbol map with trailing newline
expect(
importAsmJsSymbolMap(
[
/* prettier: ignore */
'a:A',
'b:B',
'c:C',
'',
].join('\n'),
),
).toEqual(new Map([['a', 'A'], ['b', 'B'], ['c', 'C']]))

// Valid symbol map with non-alpha characters

expect(importAsmJsSymbolMap('u6:__ZN8tinyxml210XMLCommentD0Ev\n')).toEqual(
new Map([['u6', '__ZN8tinyxml210XMLCommentD0Ev']]),
)

// Invalid symbol map
expect(
importAsmJsSymbolMap(
[
/* prettier: ignore */
'a:A',
'b:B',
'c',
'',
].join('\n'),
),
).toEqual(null)

// Collapsed stack format should not be imported as an asm.js symbol map
expect(
importAsmJsSymbolMap(
[
/* prettier: ignore */
'a;b 1',
'a;c 3',
'',
].join('\n'),
),
).toEqual(null)

// Unrelated files
expect(importAsmJsSymbolMap('')).toEqual(null)
expect(importAsmJsSymbolMap('\n')).toEqual(null)
})
26 changes: 26 additions & 0 deletions asm-js.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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
}
6 changes: 6 additions & 0 deletions profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ export class Profile {
}
}
}

remapNames(callback: (name: string) => string) {
for (let frame of this.frames) {
frame.name = callback(frame.name)
}
}
}

export class StackListProfileBuilder extends Profile {
Expand Down

0 comments on commit 7f7f5ee

Please sign in to comment.