speedscope/profile.test.ts
Jamie Wong 0e654801b5
Upgrade the "Table view" to a "Sandwich" view (#73)
![image](https://user-images.githubusercontent.com/150329/41837387-25417bae-7812-11e8-83cb-d3e6782b734e.png)

This provides information about the caller & callees of individual functions selected in the table view.
2018-06-29 12:06:19 -07:00

263 lines
4.9 KiB
TypeScript

import {
FrameInfo,
StackListProfileBuilder,
CallTreeNode,
CallTreeProfileBuilder,
Profile,
} from './profile'
function getFrameInfo(key: string): FrameInfo {
return {
key: key,
name: key,
file: `${key}.ts`,
line: key.length,
}
}
const fa = getFrameInfo('a')
const fb = getFrameInfo('b')
const fc = getFrameInfo('c')
const fd = getFrameInfo('d')
const fe = getFrameInfo('e')
function toStackList(profile: Profile, grouped: boolean): string[] {
let stackList: string[] = []
const curStack: (number | string)[] = []
let lastValue = 0
function openFrame(node: CallTreeNode, value: number) {
if (lastValue != value) {
stackList.push(curStack.map(k => `${k}`).join(';'))
lastValue = value
}
curStack.push(node.frame.key)
}
function closeFrame(node: CallTreeNode, value: number) {
if (lastValue != value) {
stackList.push(curStack.map(k => `${k}`).join(';'))
lastValue = value
}
curStack.pop()
}
if (grouped) {
profile.forEachCallGrouped(openFrame, closeFrame)
} else {
profile.forEachCall(openFrame, closeFrame)
}
return stackList
}
function verifyProfile(profile: Profile) {
const allFrameKeys = new Set([fa, fb, fc, fd, fe].map(f => f.key))
const framesInProfile = new Set<string | number>()
profile.forEachFrame(f => framesInProfile.add(f.key))
expect(allFrameKeys).toEqual(framesInProfile)
expect(toStackList(profile, false)).toEqual([
// prettier-ignore
'a',
'a;b',
'a;b;d',
'a;b;c',
'',
'a',
'a;b',
'a;b;b',
'a;b;e',
'a',
])
expect(toStackList(profile, true)).toEqual([
// prettier-ignore
'a;b;e',
'a;b;b',
'a;b;c',
'a;b;d',
'a;b',
'a',
])
const flattened = profile.getProfileWithRecursionFlattened()
expect(toStackList(flattened, false)).toEqual([
// prettier-ignore
'a',
'a;b',
'a;b;d',
'a;b;c',
'',
'a',
'a;b',
'a;b;e',
'a',
])
expect(toStackList(flattened, true)).toEqual([
// prettier-ignore
'a;b;e',
'a;b;c',
'a;b;d',
'a;b',
'a',
])
}
test('StackListProfileBuilder', () => {
const b = new StackListProfileBuilder()
const samples = [
// prettier-ignore
[fa],
[fa, fb],
[fa, fb],
[fa, fb, fd],
[fa, fb, fc],
[],
[fa],
[fa, fb],
[fa, fb, fb],
[fa, fb, fe],
[fa],
]
samples.forEach(stack => {
b.appendSample(stack, 1)
})
b.appendSample([], 4)
const profile = b.build()
expect(profile.getTotalWeight()).toBe(samples.length + 4)
expect(profile.getTotalNonIdleWeight()).toBe(samples.length - 1)
verifyProfile(profile)
})
test('CallTreeProfileBuilder', () => {
const b = new CallTreeProfileBuilder()
b.enterFrame(fa, 0)
b.enterFrame(fb, 1)
b.enterFrame(fd, 3)
b.leaveFrame(fd, 4)
b.enterFrame(fc, 4)
b.leaveFrame(fc, 5)
b.leaveFrame(fb, 5)
b.leaveFrame(fa, 5)
b.enterFrame(fa, 6)
b.enterFrame(fb, 7)
b.enterFrame(fb, 8)
b.leaveFrame(fb, 9)
b.enterFrame(fe, 9)
b.leaveFrame(fe, 10)
b.leaveFrame(fb, 10)
b.leaveFrame(fa, 11)
const profile = b.build()
verifyProfile(profile)
})
test('getInvertedProfileForCallersOf', () => {
const b = new StackListProfileBuilder()
const samples = [
// prettier-ignore
[fb],
[fa, fb],
[fa, fb, fc],
[fa],
[fa, fb, fd],
[fa],
[fd, fb],
]
samples.forEach(stack => {
b.appendSample(stack, 1)
})
const profile = b.build()
const inverted = profile.getInvertedProfileForCallersOf(fb)
expect(toStackList(inverted, false)).toEqual([
// prettier-ignore
'b',
'b;a',
'b;d',
])
})
test('getProfileForCalleesOf', () => {
const b = new StackListProfileBuilder()
const samples = [
// prettier-ignore
[fb],
[fa, fb],
[fa, fb, fc],
[fa],
[fa, fb, fd],
[fa],
[fd, fb],
]
samples.forEach(stack => {
b.appendSample(stack, 1)
})
const profile = b.build()
const inverted = profile.getProfileForCalleesOf(fb)
expect(toStackList(inverted, false)).toEqual([
// prettier-ignore
'b',
'b;c',
'b;d',
'b',
])
})
test('getProfileWithRecursionFlattened', () => {
const b = new StackListProfileBuilder()
const samples = [
// prettier-ignore
[fa],
[fa, fb, fa],
[fa, fb, fa, fb, fa],
[fa, fb, fa],
]
samples.forEach(stack => {
b.appendSample(stack, 1)
})
const profile = b.build()
const inverted = profile.getProfileWithRecursionFlattened()
expect(toStackList(inverted, false)).toEqual([
// prettier-ignore
'a',
'a;b',
])
const framesInProfile = new Set<string | number>()
inverted.forEachFrame(f => {
if (f.key === fa.key) {
expect(f.getSelfWeight()).toEqual(4)
}
if (f.key === fb.key) {
expect(f.getSelfWeight()).toEqual(0)
}
framesInProfile.add(f.key)
})
const allFrameKeys = new Set([fa, fb].map(f => f.key))
expect(allFrameKeys).toEqual(framesInProfile)
})