mirror of
https://github.com/jlfwong/speedscope.git
synced 2024-11-23 06:22:41 +03:00
0e654801b5
![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.
263 lines
4.9 KiB
TypeScript
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)
|
|
})
|