Path: blob/main/tools/profiler/convert-to-perfetto.ts
6435 views
const filename = Deno.args[0];1const frameSkip = Deno.args[1] === undefined ? 3 : Number(Deno.args[1]);2const file = Deno.readTextFileSync(filename);34let longestCommonPrefix: string | undefined = undefined;5// find the longest common prefix for source to trim6for (const fileLine of file.split("\n")) {7if (fileLine === "")8continue;9const entries = fileLine.split(" ");10if (entries.length === 4) {11continue;12}13const [_name, source] = fileLine.split(" ");14if (source === "=[C]") continue;1516if (longestCommonPrefix === undefined) {17longestCommonPrefix = source;18} else {19// find the longest common prefix20let i = 0;21while (i < longestCommonPrefix.length && longestCommonPrefix[i] === source[i]) {22i++;23}24longestCommonPrefix = longestCommonPrefix.slice(0, i);25}26}2728if (longestCommonPrefix === undefined) {29throw new Error("Internal error: no common prefix found");30}3132type LuaStack = {33frames: LuaStackFrame[];34time: number;35line: number;36category: string;37}3839type LuaStackFrame = {40location: string;41// name: string;42// source: string;43// line: number;44}4546const stacks: LuaStack[] = [];47let stackNum = "";48let time = 0;49let thisStack: LuaStackFrame[] = [];50let category = "";5152for (const fileLine of file.split("\n")) {53if (fileLine === "") continue;54const entries = fileLine.split(" ");55if (entries.length === 4) {56category = entries[2];57if (stackNum !== entries[0]) {58if (thisStack.length) {59thisStack[0].location = `${thisStack[0].location}:${entries[3]}`;60}61stacks.push({62frames: thisStack,63time: Number(entries[1]),64line: Number(entries[3]),65category66});67thisStack = [];68stackNum = entries[0];69}70continue;71}72const [name, source, line] = fileLine.split(" ");73try {74const frame: LuaStackFrame = {75location: `${source.slice(longestCommonPrefix.length)}:${line}:${name}`,76};77thisStack.push(frame);78} catch (_e) {79throw new Error(`Error parsing line: ${fileLine}`);80}81}8283// add a sentinel stack to make sure the last stack is also output84stacks.push({85frames: [],86time,87line: 0,88category: ""89});9091// convert these to chrome://tracing format92type Frame = {93name: string;94parent?: string;95}9697type TraceEvent = {98name: string;99sf: string;100ph: string;101ts: number;102cat?: string;103}104105const stackFrames: Record<number, Frame> = {};106let stackNo = 0;107const traceEvents: TraceEvent[] = [];108let prevStack: LuaStackFrame[] = [];109let prevCat: string = "";110const perfettoStack: number[] = [];111112let now = 0;113for (const stack of stacks) {114debugger;115const thisStackFrames = stack.frames.toReversed().slice(frameSkip);116117let overlappingI = 0;118while (overlappingI < thisStackFrames.length && overlappingI < prevStack.length) {119if (thisStackFrames[overlappingI].location !== prevStack[overlappingI].location ||120stack.category !== prevCat) {121break;122}123overlappingI++;124}125// pop off the stack126for (let i = prevStack.length - 1; i >= overlappingI; --i) {127const prevFrame = prevStack[i];128let newNow = stack.time * 1000000;129if (newNow <= now) {130newNow = now + 1;131}132now = newNow;133134traceEvents.push({135ph: "E",136name: prevFrame.location,137sf: String(perfettoStack.pop()),138ts: now,139cat: prevCat !== "" ? prevCat : undefined140});141}142143// push on the stack144for (let i = overlappingI; i < thisStackFrames.length; ++i) {145const nextFrame = thisStackFrames[i];146let newNow = stack.time * 1000000;147if (newNow <= now) {148newNow = now + 1;149}150now = newNow;151stackFrames[stackNo] = {152name: nextFrame.location,153parent: perfettoStack.length ? String(perfettoStack[perfettoStack.length - 1]) : undefined154}155traceEvents.push({156ph: "B",157name: nextFrame.location,158sf: String(stackNo),159ts: newNow,160cat: stack.category !== "" ? stack.category : undefined161});162perfettoStack.push(stackNo++);163}164prevStack = thisStackFrames;165prevCat = stack.category;166}167168console.log(JSON.stringify({ traceEvents, stackFrames }, null, 2));169170