This is being done in preparation for writing a format from rbspy to import into speedscope, whose internal file format is a list of stacks (111689fe13/src/storage/v1.rs (L13))
For now, speedscope will always export the evented format rather than the sampled format, but will accept either as input. I also added tests for existing versions of the file format to ensure I don't accidentally drop support for a past version of the file format.
68 lines
1.7 KiB
TypeScript
68 lines
1.7 KiB
TypeScript
import * as fs from 'fs'
|
|
import * as path from 'path'
|
|
import {Profile, CallTreeNode, Frame} from './profile'
|
|
import {importProfile} from './import'
|
|
import {exportProfile, importSpeedscopeProfiles} from './file-format'
|
|
|
|
interface DumpedProfile {
|
|
stacks: string[]
|
|
frames: Frame[]
|
|
}
|
|
|
|
export function dumpProfile(profile: Profile): any {
|
|
const dump: DumpedProfile = {
|
|
stacks: [],
|
|
frames: [],
|
|
}
|
|
|
|
profile.forEachFrame(f => dump.frames.push(f))
|
|
|
|
let lastValue = 0
|
|
const curStack: (number | string)[] = []
|
|
|
|
function maybeEmit(value: number) {
|
|
if (lastValue != value) {
|
|
dump.stacks.push(
|
|
curStack.map(k => `${k}`).join(';') + ` ${profile.formatValue(value - lastValue)}`,
|
|
)
|
|
lastValue = value
|
|
}
|
|
}
|
|
|
|
function openFrame(node: CallTreeNode, value: number) {
|
|
maybeEmit(value)
|
|
curStack.push(node.frame.name)
|
|
}
|
|
|
|
function closeFrame(node: CallTreeNode, value: number) {
|
|
maybeEmit(value)
|
|
curStack.pop()
|
|
}
|
|
|
|
profile.forEachCall(openFrame, closeFrame)
|
|
|
|
return dump
|
|
}
|
|
|
|
export async function checkProfileSnapshot(filepath: string) {
|
|
const input = fs.readFileSync(filepath, 'utf8')
|
|
|
|
const profile = await importProfile(path.basename(filepath), input)
|
|
if (profile) {
|
|
expect(dumpProfile(profile)).toMatchSnapshot()
|
|
} else {
|
|
fail('Failed to extract profile')
|
|
return
|
|
}
|
|
|
|
const exported = exportProfile(profile)
|
|
const reimported = importSpeedscopeProfiles(exported)[0]
|
|
|
|
expect(reimported.getName()).toEqual(profile.getName())
|
|
expect(reimported.getTotalWeight()).toEqual(profile.getTotalWeight())
|
|
expect(dumpProfile(reimported).stacks.join('\n')).toEqual(dumpProfile(profile).stacks.join('\n'))
|
|
|
|
const reexported = exportProfile(reimported)
|
|
expect(exported).toEqual(reexported)
|
|
}
|