UNPKG

uae-dap

Version:

Debug Adapter Protocol for Amiga development with FS-UAE or WinUAE

825 lines 36.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GetJump = exports.JumpType = exports.profileCommon = exports.Profiler = exports.ProfileFile = exports.ProfileFrame = exports.SymbolScope = exports.SymbolType = exports.Register = exports.GfxResourceFlags = exports.GfxResourceType = exports.NR_DMA_REC_VPOS = exports.NR_DMA_REC_HPOS = void 0; const childProcess = __importStar(require("child_process")); const fs = __importStar(require("fs")); exports.NR_DMA_REC_HPOS = 227; exports.NR_DMA_REC_VPOS = 313; // needs to match gcc8_c_support.h var GfxResourceType; (function (GfxResourceType) { GfxResourceType[GfxResourceType["bitmap"] = 0] = "bitmap"; GfxResourceType[GfxResourceType["palette"] = 1] = "palette"; GfxResourceType[GfxResourceType["copperlist"] = 2] = "copperlist"; GfxResourceType[GfxResourceType["sprite"] = 3] = "sprite"; })(GfxResourceType = exports.GfxResourceType || (exports.GfxResourceType = {})); // needs to match gcc8_c_support.h var GfxResourceFlags; (function (GfxResourceFlags) { GfxResourceFlags[GfxResourceFlags["bitmap_interleaved"] = 1] = "bitmap_interleaved"; GfxResourceFlags[GfxResourceFlags["bitmap_masked"] = 2] = "bitmap_masked"; GfxResourceFlags[GfxResourceFlags["bitmap_ham"] = 4] = "bitmap_ham"; })(GfxResourceFlags = exports.GfxResourceFlags || (exports.GfxResourceFlags = {})); var Register; (function (Register) { Register[Register["D0"] = 0] = "D0"; Register[Register["D1"] = 1] = "D1"; Register[Register["D2"] = 2] = "D2"; Register[Register["D3"] = 3] = "D3"; Register[Register["D4"] = 4] = "D4"; Register[Register["D5"] = 5] = "D5"; Register[Register["D6"] = 6] = "D6"; Register[Register["D7"] = 7] = "D7"; Register[Register["A0"] = 8] = "A0"; Register[Register["A1"] = 9] = "A1"; Register[Register["A2"] = 10] = "A2"; Register[Register["A3"] = 11] = "A3"; Register[Register["A4"] = 12] = "A4"; Register[Register["A5"] = 13] = "A5"; Register[Register["A6"] = 14] = "A6"; Register[Register["A7"] = 15] = "A7"; Register[Register["SR"] = 16] = "SR"; Register[Register["_count"] = 17] = "_count"; })(Register = exports.Register || (exports.Register = {})); var SymbolType; (function (SymbolType) { SymbolType[SymbolType["Function"] = 0] = "Function"; SymbolType[SymbolType["File"] = 1] = "File"; SymbolType[SymbolType["Object"] = 2] = "Object"; SymbolType[SymbolType["Normal"] = 3] = "Normal"; })(SymbolType = exports.SymbolType || (exports.SymbolType = {})); var SymbolScope; (function (SymbolScope) { SymbolScope[SymbolScope["Local"] = 0] = "Local"; SymbolScope[SymbolScope["Global"] = 1] = "Global"; SymbolScope[SymbolScope["Neither"] = 2] = "Neither"; SymbolScope[SymbolScope["Both"] = 3] = "Both"; })(SymbolScope = exports.SymbolScope || (exports.SymbolScope = {})); // represents 1 frame worth of profiling data class ProfileFrame { constructor() { this.dmaRecords = []; this.gfxResources = []; // CPU cycles per frame: 142102 (according to winuae profiler) // we get 142094 (8 missing), good enough for now? // DMA cycles per frame: 227*313*2=142101 // http://eab.abime.net/showthread.php?t=51883 confirms 313 lines in PAL default } } exports.ProfileFrame = ProfileFrame; class ProfileFile { constructor(filename) { this.frames = []; const buffer = fs.readFileSync(filename); let bufferOffset = 0; const numFrames = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; const sectionCount = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.sectionBases = new Uint32Array(sectionCount); for (let i = 0; i < sectionCount; i++, bufferOffset += 4) this.sectionBases[i] = buffer.readUInt32LE(bufferOffset); this.systemStackLower = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.systemStackUpper = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.stackLower = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.stackUpper = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; // kickstart this.kickRomSize = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.kickRom = new Uint8Array(buffer.buffer, bufferOffset, this.kickRomSize); bufferOffset += this.kickRomSize; // memory this.chipMemSize = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.chipMem = new Uint8Array(buffer.buffer, bufferOffset, this.chipMemSize); bufferOffset += this.chipMemSize; this.bogoMemSize = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.bogoMem = new Uint8Array(buffer.buffer, bufferOffset, this.bogoMemSize); bufferOffset += this.bogoMemSize; // CPU info this.baseClock = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; this.cpuCycleUnit = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; console.log("baseclock", this.baseClock, "cpucycleunit", this.cpuCycleUnit); for (let i = 0; i < numFrames; i++) { const frame = new ProfileFrame(); // custom registers const customRegsLen = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; const customRegsOffset = bufferOffset; frame.chipsetFlags = buffer.readUInt32BE(bufferOffset); bufferOffset += 4; if (customRegsLen !== ProfileFile.customRegsLen) throw new Error(`customRegsLen mismatch (want ${ProfileFile.customRegsLen}, got ${customRegsLen})`); //frame.customRegs = new Uint16Array(buffer.buffer, bufferOffset, 256); bufferOffset += 256 * 2; // maybe unaligned, so read manually frame.customRegs = new Uint16Array(256); for (let i = 0; i < 256; i++) { frame.customRegs[i] = buffer.readUInt16BE(bufferOffset); bufferOffset += 2; } bufferOffset = customRegsOffset + customRegsLen; // DMA const dmaLen = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; const dmaCount = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; if (dmaLen !== ProfileFile.sizeofDmaRec) throw new Error("dmaLen mismatch"); if (dmaCount !== exports.NR_DMA_REC_HPOS * exports.NR_DMA_REC_VPOS) throw new Error(`dmaCount mismatch (${dmaCount} != ${exports.NR_DMA_REC_HPOS * exports.NR_DMA_REC_VPOS})`); const dmaBuffer = Buffer.from(buffer.buffer, bufferOffset, dmaLen * dmaCount); bufferOffset += dmaLen * dmaCount; for (let i = 0; i < dmaCount; i++) { const reg = dmaBuffer.readUInt16LE(i * dmaLen + 0); const dat = dmaBuffer.readUInt32LE(i * dmaLen + 2); const datHi = dmaBuffer.readUInt32LE(i * dmaLen + 2 + 4); const size = dmaBuffer.readUInt16LE(i * dmaLen + 10); const addr = dmaBuffer.readUInt32LE(i * dmaLen + 12); const evt = dmaBuffer.readUInt32LE(i * dmaLen + 16); const type = dmaBuffer.readInt16LE(i * dmaLen + 20); const extra = dmaBuffer.readUInt16LE(i * dmaLen + 22); const intlev = dmaBuffer.readInt8(i * dmaLen + 24); if (reg !== 0xffff) { frame.dmaRecords.push({ reg, dat, datHi, size, addr, evt, type, extra, intlev, }); } else if (evt) { frame.dmaRecords.push({ evt }); } else { frame.dmaRecords.push({}); } } // resources const resourceLen = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; const resourceCount = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; if (resourceLen !== ProfileFile.sizeofResource) throw new Error("resourceLen mismatch"); const resourceBuffer = Buffer.from(buffer.buffer, bufferOffset, resourceLen * resourceCount); bufferOffset += resourceLen * resourceCount; for (let i = 0; i < resourceCount; i++) { const address = resourceBuffer.readUInt32LE(i * resourceLen + 0); const size = resourceBuffer.readUInt32LE(i * resourceLen + 4); const name = resourceBuffer.toString("utf8", i * resourceLen + 8, resourceBuffer.indexOf(0, i * resourceLen + 8)); const type = resourceBuffer.readUInt16LE(i * resourceLen + 40); const flags = resourceBuffer.readUInt16LE(i * resourceLen + 42); const resource = { address, size, name, type, flags }; switch (type) { case GfxResourceType.bitmap: { const width = resourceBuffer.readUInt16LE(i * resourceLen + 44); const height = resourceBuffer.readUInt16LE(i * resourceLen + 46); const numPlanes = resourceBuffer.readUInt16LE(i * resourceLen + 48); resource.bitmap = { width, height, numPlanes }; break; } case GfxResourceType.palette: { const numEntries = resourceBuffer.readUInt16LE(i * resourceLen + 44); resource.palette = { numEntries }; break; } } frame.gfxResources.push(resource); } frame.profileCycles = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; frame.idleCycles = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; //console.log("profileCycles", frame.profileCycles, "idleCycles", frame.idleCycles); // profiles const profileCount = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; // profileArray may be unaligned, so manually read entries //frame.profileArray = new Uint32Array(buffer.buffer, bufferOffset, (buffer.length - bufferOffset) / Uint32Array.BYTES_PER_ELEMENT); frame.profileArray = new Uint32Array(profileCount); for (let i = 0; i < profileCount; i++) { frame.profileArray[i] = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; } const screenshotSize = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; const screenshotType = buffer.readUInt32LE(bufferOffset); bufferOffset += 4; if (screenshotType === 0) frame.screenshotType = "jpg"; else frame.screenshotType = "png"; frame.screenshot = new Uint8Array(buffer.buffer, bufferOffset, screenshotSize); bufferOffset += screenshotSize; this.frames.push(frame); } } } exports.ProfileFile = ProfileFile; ProfileFile.customRegsLen = 256 * 2 + 4 /*chipsetFlags*/ + 4 /*RefPtr*/; ProfileFile.sizeofDmaRec = 42; ProfileFile.sizeofResource = 52; class Profiler { constructor(sourceMap, symbolTable, kickstartSymbols) { this.sourceMap = sourceMap; this.symbolTable = symbolTable; this.kickstartSymbols = kickstartSymbols; } profileSavestate(profileFile) { const out = []; for (const frame of profileFile.frames) out.push(this.profileSavestateFrame(profileFile, frame)); const pcTrace = []; for (const frame of out) for (let i = 0; i < frame.$amiga.pcTrace.length; i += 2) pcTrace.push(frame.$amiga.pcTrace[i]); // store memory only for first frame, will later be reconstructed via dmaRecords for other frames out[0].$amiga.chipMem = Buffer.from(profileFile.chipMem).toString("base64"); out[0].$amiga.bogoMem = Buffer.from(profileFile.bogoMem).toString("base64"); out[0].$amiga.objdump = this.disassemblePcTrace(profileFile, pcTrace); return JSON.stringify(out); //, null, 2); } disassemblePcTrace(profileFile, pcTrace, functions) { pcTrace.sort((a, b) => a - b); let disasm = "\n"; if (!functions) { functions = new Map(); disasm = "00000000 <_start>:\n"; } const processBranch = (mem, addr, offset) => { const mem16 = new Uint16Array(4); for (let i = 0; i < 8 && offset + i < mem.length; i += 2) { mem16[i >>> 1] = (mem[offset + i] << 8) | mem[offset + i + 1]; } const jump = GetJump(addr, mem16); if (jump?.type === JumpType.Jsr) functions.set(jump.target, `_${jump.target.toString(16).padStart(8, "0")}`); }; const disasmInsn = (mem, addr, offset) => { const insn = print_insn_m68k(mem.slice(offset, Math.min(offset + 16, mem.length)), addr); if (insn.len > 0) { if (functions.has(addr)) disasm += `${addr.toString(16).padStart(8, "0")} <${functions.get(addr)}>:\n`; disasm += ` ${addr.toString(16)}:\t`; for (let i = 0; i < insn.len; i += 2) disasm += ((mem[offset + i] << 8) | mem[offset + i + 1]) .toString(16) .padStart(4, "0") + " "; disasm += insn.text + "\n"; if (insn.text === "rts" || insn.text === "rte") disasm += "\n"; } }; const process = (fn) => { let lastPC = 4294967295; const kickBase = profileFile.kickRomSize === 512 * 1024 ? 16252928 : 16515072; for (const pc of pcTrace) { if (pc !== lastPC) { if (pc > 0 && pc < profileFile.chipMemSize) fn(profileFile.chipMem, pc, pc - 0); else if (pc >= 12582912 && pc < 12582912 + profileFile.bogoMemSize) fn(profileFile.bogoMem, pc, pc - 12582912); else if (pc >= kickBase && pc < 16777216) fn(profileFile.kickRom, pc, pc - kickBase); lastPC = pc; } } }; process(processBranch); process(disasmInsn); return disasm; } profileSavestateFrame(profileFile, frame) { const pcTrace = []; const registerTrace = []; let lastPC; let totalCycles = 0; for (let i = 0; i < frame.profileArray.length; i++) { const p = frame.profileArray[i]; if (p < 4294901760) { if (lastPC === undefined) lastPC = p; } else { const cyc = (4294967295 - p) | 0; if (lastPC === undefined) lastPC = 4294967295; const registers = frame.profileArray.slice(i + 1, i + 1 + Register._count); i += Register._count; pcTrace.push(lastPC, cyc); registerTrace.push(...registers); lastPC = undefined; totalCycles += cyc; } } //console.log("totalCycles", totalCycles); const out = { nodes: [], startTime: 0, endTime: totalCycles, $amiga: { chipsetFlags: frame.chipsetFlags, baseClock: profileFile.baseClock, cpuCycleUnit: profileFile.cpuCycleUnit, customRegs: Array.from(frame.customRegs), dmaRecords: frame.dmaRecords, gfxResources: frame.gfxResources, idleCycles: frame.idleCycles, symbols: [], sections: [], systemStackLower: profileFile.systemStackLower, systemStackUpper: profileFile.systemStackUpper, stackLower: profileFile.stackLower, stackUpper: profileFile.stackUpper, uniqueCallFrames: [], callFrames: [], pcTrace, registerTrace, }, }; if (frame.screenshot.length) out.$amiga.screenshot = "data:image/" + frame.screenshotType + ";base64," + Buffer.from(frame.screenshot).toString("base64"); return out; } profileTime(profileFile, disassembly) { const out = []; for (const frame of profileFile.frames) out.push(this.profileTimeFrame(profileFile, frame)); // disassemble kickstart const kickTrace = []; for (const frame of out) for (let i = 0; i < frame.$amiga.pcTrace.length; i += 2) if (frame.$amiga.pcTrace[i] >= 16252928 && frame.$amiga.pcTrace[i] < 16777216) kickTrace.push(frame.$amiga.pcTrace[i]); const kickFunctions = new Map(); if (this.kickstartSymbols) { for (const f of this.kickstartSymbols.getFunctionSymbols()) kickFunctions.set(f.base + f.address, "[Kick]" + f.name); } // store memory only for first frame, will later be reconstructed via dmaRecords for other frames out[0].$amiga.chipMem = Buffer.from(profileFile.chipMem).toString("base64"); out[0].$amiga.bogoMem = Buffer.from(profileFile.bogoMem).toString("base64"); out[0].$amiga.objdump = disassembly + this.disassemblePcTrace(profileFile, kickTrace, kickFunctions); return JSON.stringify(out /*, null, 2*/); } profileTimeFrame(profileFile, frame) { const sameCallstack = (callstack1, callstack2) => { if (callstack1.frames.length !== callstack2.frames.length) return false; for (let i = 0; i < callstack1.frames.length; i++) { if (callstack1.frames[i] !== callstack2.frames[i]) return false; } return true; }; // same index const cycles = []; const locations = []; let lastLocation = -1; const callstack = { frames: [] }; const lastCallstack = { frames: [] }; const pcTrace = []; const registerTrace = []; let lastPC; let totalCycles = 0; for (let i = 0; i < frame.profileArray.length; i++) { const p = frame.profileArray[i]; if (p < 4294901760) { // PC if (lastPC === undefined) lastPC = p; if (p === 2147483647) { // IRQ processing callstack.frames.push({ func: "[IRQ]", file: "", line: 0 }); } else if (p >= 16252928 && p < 16777216) { // in Kickstart for (const f of lastCallstack.frames) if (f.file !== "") callstack.frames.push(f); let func = "[Kickstart]"; if (this.kickstartSymbols) { const sym = this.kickstartSymbols.getFunctionAtAddress(p, true); if (sym) func = "[Kick]" + sym.name; } callstack.frames.push({ func, file: "", line: 0 }); } else { let pc = p; if (callstack.frames.length) pc -= 2; // unwinding gets PC of next instruction, we want the previous! const l = this.sourceMap.uniqueLines[this.sourceMap.lines[pc >> 1]]; for (let i = l.frames.length - 1; i >= 0; i--) { callstack.frames.unshift({ ...l.frames[i] }); if (i !== 0) callstack.frames[0].func += " (inlined)"; } } } else { // #Cycles const cyc = (4294967295 - p) | 0; if (lastPC === undefined) lastPC = 4294967295; const registers = frame.profileArray.slice(i + 1, i + 1 + Register._count); i += Register._count; pcTrace.push(lastPC, cyc); registerTrace.push(...registers); lastPC = undefined; if (callstack.frames.length === 0) { // not in our code callstack.frames.push(...lastCallstack.frames); if (callstack.frames.length === 0 || callstack.frames[callstack.frames.length - 1].func !== "[External]") callstack.frames.push({ func: "[External]", file: "", line: 0 }); } if (sameCallstack(callstack, lastCallstack)) { cycles[lastLocation] += cyc; } else { const callstackCopy = { frames: [...callstack.frames] }; lastLocation = locations.push(callstackCopy) - 1; cycles.push(cyc); } totalCycles += cyc; lastCallstack.frames = [...callstack.frames]; callstack.frames.length = 0; } } //console.log("totalCycles", totalCycles); // filter symbols const sections = this.symbolTable.sections.filter((section) => section.flags.find((f) => f === "ALLOC")); const symbols = this.symbolTable.symbols.filter((symbol) => symbol.size > 0 && sections.find((section) => symbol.section === section.name)); const out = { ...profileCommon(cycles, locations), $amiga: { chipsetFlags: frame.chipsetFlags, baseClock: profileFile.baseClock, cpuCycleUnit: profileFile.cpuCycleUnit, customRegs: Array.from(frame.customRegs), dmaRecords: frame.dmaRecords, gfxResources: frame.gfxResources, idleCycles: frame.idleCycles, symbols, sections, systemStackLower: profileFile.systemStackLower, systemStackUpper: profileFile.systemStackUpper, stackLower: profileFile.stackLower, stackUpper: profileFile.stackUpper, uniqueCallFrames: this.sourceMap.uniqueLines, callFrames: this.sourceMap.lines, pcTrace, registerTrace, }, }; if (frame.screenshot) out.$amiga.screenshot = "data:image/jpg;base64," + Buffer.from(frame.screenshot).toString("base64"); return out; } profileSize(objdumpPath, elfPath) { const sectionMap = new Map(); const sizePerFunction = []; const locations = []; for (const section of this.symbolTable.sections) { if (!section.flags.includes("LOAD")) continue; if (section.name === ".text") { for (const line of this.sourceMap.lines) { const l = this.sourceMap.uniqueLines[line]; const callstack = { frames: [] }; for (let i = 0; i < l.frames.length; i++) { callstack.frames.push({ ...l.frames[i] }); if (i !== 0) callstack.frames[callstack.frames.length - 1].func += " (inlined)"; } // section node callstack.frames.unshift({ func: ".text", file: "", line: 0, }); locations.push(callstack); sizePerFunction.push(2); } } else { const symbols = []; for (const symbol of this.symbolTable.symbols .filter((sym) => sym.section === section.name && sym.size > 0) .sort((a, b) => a.address - b.address)) { const callstack = { frames: [ { func: section.name, file: "", line: 0, }, { func: symbol.name, file: section.name, line: symbol.address, }, ], }; symbols.push({ callstack, address: symbol.address, size: symbol.size, }); } sectionMap.set(section.name, symbols); } } // for unknown symbols, try to infer usage from relocations const objdump = childProcess.spawnSync(objdumpPath, ["--reloc", "--section=.text", elfPath], { maxBuffer: 10 * 1024 * 1024 }); if (objdump.status !== 0) throw objdump.error; const outputs = objdump.stdout.toString().replace(/\r/g, "").split("\n"); for (const line of outputs) { // 00000006 R_68K_32 __preinit_array_end // 0000022c R_68K_32 .rodata+0x00000112 const match = line.match(/^([0-9a-f]{8})\s\S+\s+(\..+)$/); if (match) { const addr = parseInt(match[1], 16); let section = match[2]; let offset = 0; const add = section.indexOf("+0x"); if (add !== -1) { offset = parseInt(section.substr(add + 3), 16); section = section.substr(0, add); } // ignore relocations to known symbols const sectionSymbols = sectionMap.get(section); if (sectionSymbols === undefined) continue; if (sectionSymbols.find((sym) => offset >= sym.address && offset < sym.address + sym.size)) continue; const sourceLine = this.sourceMap.uniqueLines[this.sourceMap.lines[addr >> 1]]; const sourceFrame = { ...sourceLine.frames[sourceLine.frames.length - 1], }; sourceFrame.func = `${section}+$${offset.toString(16)} (${sourceFrame.func})`; const callstack = { frames: [ { func: section, file: "", line: 0, }, sourceFrame, ], }; sectionSymbols.push({ callstack, address: offset, size: 0, }); } } for (const sectionName of sectionMap.keys()) { const sectionSymbols = sectionMap .get(sectionName) .sort((a, b) => a.address - b.address); const section = this.symbolTable.sections.find((sec) => sec.name === sectionName); let lastEmptySymbol = null; let lastSymbol = null; // guess size of reloc-referenced symbols for (const symbol of sectionSymbols) { if (lastSymbol && symbol.address === lastSymbol.address) { if (lastSymbol.callstack.frames[lastSymbol.callstack.frames.length - 1] .func !== symbol.callstack.frames[symbol.callstack.frames.length - 1].func) lastSymbol.callstack.frames[lastSymbol.callstack.frames.length - 1].func += ", " + symbol.callstack.frames[symbol.callstack.frames.length - 1].func; continue; } if (lastEmptySymbol) { lastEmptySymbol.size = symbol.address - lastEmptySymbol.address; lastEmptySymbol = null; } if (symbol.size === 0) lastEmptySymbol = symbol; lastSymbol = symbol; } if (lastEmptySymbol) lastEmptySymbol.size = section.size - lastEmptySymbol.address; // add symbols to profile let lastAddress = section.lma; lastSymbol = null; for (const symbol of sectionSymbols) { if (lastSymbol && lastSymbol.address === symbol.address) continue; if (symbol.size === 0) continue; if (symbol.address > lastAddress) { // gap (unknown symbol) locations.push({ frames: [{ func: section.name, file: "", line: lastAddress }], }); sizePerFunction.push(symbol.address - lastAddress); } locations.push(symbol.callstack); sizePerFunction.push(symbol.size); lastAddress = symbol.address + symbol.size; lastSymbol = symbol; } if (lastAddress < section.size) { locations.push({ frames: [{ func: section.name, file: "", line: lastAddress }], }); sizePerFunction.push(section.size - lastAddress); } } const out = [profileCommon(sizePerFunction, locations)]; return JSON.stringify(out, null, 2); } } exports.Profiler = Profiler; function profileCommon(weightPerLocation, sourceLocations, origWeightPerLocation) { // generate JSON .cpuprofile const nodes = []; const nodeMap = new Map(); const samples = []; const timeDeltas = []; const origTimeDeltas = []; // for shrinkler only const startTime = 0; let endTime = 0; let nextNodeId = 1; let nextLocationId = 0; const getNodeKey = (callFrame, depth) => { let key = ""; for (let i = 0; i < depth; i++) key += callFrame.frames[i].func + ":"; return key; }; const getCallFrame = (callFrame) => { return { scriptId: callFrame.file.toLowerCase().replace(/\\/g, "/"), functionName: callFrame.func, url: callFrame.file.toLowerCase().replace(/\\/g, "/"), lineNumber: callFrame.line, columnNumber: 0, }; }; const getNode = (callFrame, depth) => { const key = getNodeKey(callFrame, depth); let node = nodeMap.get(key); if (node === undefined) { const pp = getNode(callFrame, depth - 1); const fr = callFrame.frames[depth - 1]; node = { id: nextNodeId++, callFrame: getCallFrame(fr), children: [], locationId: nextLocationId++, positionTicks: [], }; pp.children.push(node.id); nodes.push(node); nodeMap.set(key, node); } return node; }; // add root node const rootNode = { id: nextNodeId++, callFrame: { functionName: "(root)", scriptId: "0", url: "", lineNumber: -1, columnNumber: -1, }, hitCount: 0, children: [], locationId: nextLocationId++, positionTicks: [], }; nodes.push(rootNode); samples.push(rootNode.id); nodeMap.set("", rootNode); for (let i = 0; i < weightPerLocation.length; i++) { if (weightPerLocation[i] === 0 && (origWeightPerLocation === undefined || origWeightPerLocation[i] === 0)) continue; const ticks = weightPerLocation[i]; const loc = sourceLocations[i]; const fr = sourceLocations[i].frames[sourceLocations[i].frames.length - 1]; /*const tick: typeof rootNode.positionTicks[0] = { line: fr.line, ticks, startLocationId: nextLocationId++, endLocationId: nextLocationId++ };*/ const node = getNode(loc, loc.frames.length); node.hitCount = ticks; //node.positionTicks.push(tick); samples.push(node.id); timeDeltas.push(ticks); if (origWeightPerLocation) origTimeDeltas.push(origWeightPerLocation[i]); endTime += ticks; } timeDeltas.push(0); if (origWeightPerLocation) origTimeDeltas.push(0); const out = { nodes, startTime, endTime, samples, timeDeltas, }; if (origWeightPerLocation) out.$shrinkler = { origTimeDeltas }; return out; } exports.profileCommon = profileCommon; var JumpType; (function (JumpType) { JumpType[JumpType["Branch"] = 0] = "Branch"; JumpType[JumpType["ConditionalBranch"] = 1] = "ConditionalBranch"; JumpType[JumpType["Jsr"] = 2] = "Jsr"; JumpType[JumpType["ConditionalJsr"] = 3] = "ConditionalJsr"; })(JumpType = exports.JumpType || (exports.JumpType = {})); function GetJump(pc, insn) { if ((insn[0] & 61440) === 24576) { // BRA,BSR,Bcc const cc = ((insn[0] >>> 8) & 0b1111); const type = cc === Conditional.T ? JumpType.Branch : cc === Conditional.F ? JumpType.Jsr : JumpType.ConditionalBranch; const displacement = insn[0] & 0xff ? uncomplement(insn[0] & 0xff, 8) : uncomplement(insn[1], 16); return { type, target: pc + 2 + displacement }; } if ((insn[0] & 61688) === 20680) // DBcc return { type: JumpType.ConditionalBranch, target: pc + 2 + uncomplement(insn[1], 16), }; if ((insn[0] & 65408) === 20096) { // JMP,JSR const effectiveAddress = GetAddressingMode(insn[0], 0, 3); const type = insn[0] & (1 << 6) ? JumpType.Branch : JumpType.Jsr; if (effectiveAddress === AddressingMode.AbsoluteShort) return { type, target: insn[1] }; else if (effectiveAddress === AddressingMode.AbsoluteLong) return { type, target: (insn[1] << 16) | insn[2] }; } } exports.GetJump = GetJump; //# sourceMappingURL=profiler.js.map