uae-dap
Version:
Debug Adapter Protocol for Amiga development with FS-UAE or WinUAE
825 lines • 36.4 kB
JavaScript
"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