2018-01-07 03:37:36 +03:00
|
|
|
import {Profile, TimeFormatter, FrameInfo} from '../profile'
|
2017-12-31 06:48:30 +03:00
|
|
|
|
|
|
|
interface TimelineEvent {
|
|
|
|
pid: number,
|
|
|
|
tid: number,
|
|
|
|
ts: number,
|
|
|
|
ph: string,
|
|
|
|
cat: string,
|
|
|
|
name: string,
|
|
|
|
dur: number,
|
|
|
|
tdur: number,
|
|
|
|
tts: number,
|
|
|
|
args: { [key: string]: any }
|
|
|
|
}
|
|
|
|
|
|
|
|
interface PositionTickInfo {
|
|
|
|
line: number,
|
|
|
|
ticks: number
|
|
|
|
}
|
|
|
|
|
|
|
|
interface CPUProfileNode {
|
|
|
|
callFrame: {
|
|
|
|
columnNumber: number,
|
|
|
|
functionName: string,
|
|
|
|
lineNumber: number,
|
|
|
|
scriptId: string,
|
|
|
|
url: string
|
|
|
|
},
|
|
|
|
hitCount: number
|
|
|
|
id: number
|
|
|
|
children?: number[]
|
|
|
|
positionTicks?: PositionTickInfo[]
|
|
|
|
parent?: CPUProfileNode
|
|
|
|
}
|
|
|
|
|
|
|
|
interface CPUProfile {
|
|
|
|
startTime: number,
|
|
|
|
endTime: number,
|
|
|
|
nodes: CPUProfileNode[],
|
|
|
|
samples: number[],
|
|
|
|
timeDeltas: number[]
|
|
|
|
}
|
|
|
|
|
|
|
|
export function importFromChrome(events: TimelineEvent[]) {
|
|
|
|
const profileEvent = events[events.length - 1]
|
|
|
|
const chromeProfile = profileEvent.args.data.cpuProfile as CPUProfile
|
|
|
|
const profile = new Profile(chromeProfile.endTime - chromeProfile.startTime)
|
|
|
|
|
|
|
|
const nodeById = new Map<number, CPUProfileNode>()
|
|
|
|
for (let node of chromeProfile.nodes) {
|
|
|
|
nodeById.set(node.id, node)
|
|
|
|
}
|
|
|
|
for (let node of chromeProfile.nodes) {
|
|
|
|
if (!node.children) continue
|
|
|
|
for (let childId of node.children) {
|
|
|
|
const child = nodeById.get(childId)
|
|
|
|
if (!child) continue
|
|
|
|
child.parent = node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const samples: number[] = []
|
|
|
|
const timeDeltas: number[] = []
|
|
|
|
|
|
|
|
let elapsed = 0
|
|
|
|
let lastNodeId = NaN
|
|
|
|
|
|
|
|
// The chrome CPU profile format doesn't collapse identical samples. We'll do that
|
|
|
|
// here to save a ton of work later doing mergers.
|
|
|
|
for (let i = 0; i < chromeProfile.samples.length; i++) {
|
|
|
|
const nodeId = chromeProfile.samples[i]
|
|
|
|
if (nodeId != lastNodeId) {
|
|
|
|
samples.push(nodeId)
|
|
|
|
timeDeltas.push(elapsed)
|
|
|
|
elapsed = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
elapsed += chromeProfile.timeDeltas[i]
|
|
|
|
lastNodeId = nodeId
|
|
|
|
}
|
|
|
|
if (!isNaN(lastNodeId)) {
|
|
|
|
samples.push(lastNodeId)
|
|
|
|
timeDeltas.push(elapsed)
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < samples.length; i++) {
|
2017-12-31 10:13:57 +03:00
|
|
|
const timeDelta = timeDeltas[i+1] || 0
|
2017-12-31 06:48:30 +03:00
|
|
|
const nodeId = samples[i]
|
|
|
|
let node = nodeById.get(nodeId)
|
|
|
|
if (!node) continue
|
|
|
|
|
|
|
|
// TODO(jlfwong): This is silly and slow, but good enough for now
|
|
|
|
const stack: FrameInfo[] = []
|
|
|
|
for (let node = nodeById.get(nodeId); node; node = node.parent) {
|
2018-01-01 03:51:26 +03:00
|
|
|
if (node.callFrame.functionName === '(root)') continue
|
|
|
|
if (node.callFrame.functionName === '(idle)') continue
|
2017-12-31 06:48:30 +03:00
|
|
|
stack.push({
|
|
|
|
key: node.id,
|
2017-12-31 09:27:56 +03:00
|
|
|
name: node.callFrame.functionName || "(anonymous)",
|
2017-12-31 06:48:30 +03:00
|
|
|
file: node.callFrame.url,
|
|
|
|
line: node.callFrame.lineNumber,
|
|
|
|
col: node.callFrame.columnNumber
|
|
|
|
})
|
|
|
|
}
|
|
|
|
stack.reverse()
|
|
|
|
|
|
|
|
profile.appendSample(stack, timeDelta)
|
|
|
|
}
|
2018-01-07 03:37:36 +03:00
|
|
|
|
|
|
|
profile.setValueFormatter(new TimeFormatter('us'))
|
2017-12-31 06:48:30 +03:00
|
|
|
return profile
|
|
|
|
}
|