UNPKG

uae-dap

Version:

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

221 lines 8.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const gdbClient_1 = require("./gdbClient"); const debugadapter_1 = require("@vscode/debugadapter"); const strings_1 = require("./utils/strings"); const disassembly_1 = require("./disassembly"); /** * Breakpoint manager * * Handles adding and removing breakpoints to program */ class BreakpointManager { constructor(gdb, sourceMap, disassembly, sizes) { this.gdb = gdb; this.sourceMap = sourceMap; this.disassembly = disassembly; this.sizes = sizes; /** Source breakpoints mapped by source and line number */ this.sourceBreakpoints = new Map(); /** Source breakpoints mapped by address */ this.sourceBreakpointsByAddress = new Map(); /** Data breakpoints mapped by address */ this.dataBreakpoints = new Map(); /** Instruction breakoint addresses */ this.instructionBreakpoints = new Set(); /** Temporary breakoint address groups */ this.temporaryBreakpoints = new Set(); this.nextId = 0; } async setSourceBreakpoints(source, breakpoints) { const sourceKey = source.path || source.sourceReference?.toString(); if (!sourceKey) { throw new Error("Invalid source"); } // Remove existing breakpoints for source const existing = this.sourceBreakpoints.get(sourceKey); if (existing) { debugadapter_1.logger.log("[BP] Removing existing breakpoints for source " + sourceKey); for (const { address } of existing.values()) { this.sourceBreakpointsByAddress.delete(address); await this.gdb.removeBreakpoint(address).catch(() => { debugadapter_1.logger.log("[BP] Error removing breakpoint at " + address); }); } } // Add new breakpoints const outBreakpoints = []; const newRefs = new Map(); for (const bp of breakpoints) { const outBp = { ...bp, verified: false, }; try { if (!this.sourceMap) { throw new Error("Program not loaded"); } if (!source.path) { throw new Error("Source has no path"); } let address; if ((0, disassembly_1.isDisassembledFile)(source.path)) { address = await this.disassembly.getAddressForFileEditorLine(source.name ?? "", bp.line); } else { const loc = this.sourceMap.lookupSourceLine(source.path, bp.line); debugadapter_1.logger.log(`Source breakoint at ${loc.path}:${loc.line} ${(0, strings_1.formatAddress)(loc.address)}`); address = loc.address; } await this.gdb.setBreakpoint(address); const ref = { address, breakpoint: bp, hitCount: 0, }; newRefs.set(bp.line, ref); this.sourceBreakpointsByAddress.set(address, ref); outBp.verified = true; } catch (err) { if (err instanceof Error) outBp.message = err.message; } outBreakpoints.push(outBp); } this.sourceBreakpoints.set(sourceKey, newRefs); return outBreakpoints; } sourceBreakpointAtAddress(address) { return this.sourceBreakpointsByAddress.get(address); } async setDataBreakpoints(breakpoints) { const outBreakpoints = []; const types = { read: gdbClient_1.BreakpointCode.READ, write: gdbClient_1.BreakpointCode.WRITE, readWrite: gdbClient_1.BreakpointCode.ACCESS, }; const newIds = breakpoints.map((bp) => bp.dataId); // Clear existing data points: for (const [address, ref] of this.dataBreakpoints.entries()) { try { // If a breakpoint has actually been removed, delete the size for the map if (!newIds.includes(ref.breakpoint.dataId)) { debugadapter_1.logger.log("[BP] removing size for breakpoint " + ref.breakpoint.dataId); this.sizes.delete(ref.breakpoint.dataId); } const { accessType } = ref.breakpoint; const type = accessType ? types[accessType] : gdbClient_1.BreakpointCode.ACCESS; await this.gdb.removeBreakpoint(address, type, ref.size); } catch (err) { if (err instanceof Error) { debugadapter_1.logger.error(err.message); } } } this.dataBreakpoints.clear(); // Process new data points: for (const i in breakpoints) { const bp = breakpoints[i]; const outBp = { id: this.nextId++, ...bp, verified: false, }; outBreakpoints.push(outBp); try { const { address } = this.parseDataId(bp.dataId); const size = this.sizes.get(bp.dataId) || 2; outBp.message = `${size} bytes watched starting at ${(0, strings_1.formatAddress)(address)}`; debugadapter_1.logger.log(`[BP] Data breakoint: ${outBp.message}`); this.dataBreakpoints.set(address, { breakpoint: bp, address, hitCount: 0, size, }); const type = bp.accessType ? types[bp.accessType] : gdbClient_1.BreakpointCode.ACCESS; await this.gdb.setBreakpoint(address, type, size); outBp.verified = true; } catch (err) { if (err instanceof Error) outBp.message = err.message; } } return outBreakpoints; } parseDataId(dataId) { const match = dataId.match(/(?<name>.+)\((?<displayValue>.+)\)/); if (!match?.groups) { throw new Error("DataId format invalid"); } const { name, displayValue } = match.groups; const address = parseInt(displayValue); return { name, displayValue, address }; } dataBreakpointAtAddress(address) { return this.dataBreakpoints.get(address); } async setInstructionBreakpoints(breakpoints) { const outBreakpoints = []; // Clear existing breakpoints: for (const address of this.instructionBreakpoints.values()) { await this.gdb.removeBreakpoint(address); } this.instructionBreakpoints.clear(); // Process new breakpoints: for (const bp of breakpoints) { const outBp = { ...bp, verified: false, }; outBreakpoints.push(outBp); try { const address = parseInt(bp.instructionReference); debugadapter_1.logger.log(`[BP] Instruction Breakpoint at ${(0, strings_1.formatAddress)(address)}`); // Set in GDB: await this.gdb.setBreakpoint(address); this.instructionBreakpoints.add(address); outBp.verified = true; } catch (err) { if (err instanceof Error) outBp.message = err.message; } } return outBreakpoints; } instructionBreakpointAtAddress(address) { return this.instructionBreakpoints.has(address); } // Temporary breakpoints: async addTemporaryBreakpoints(pc) { await this.clearTemporaryBreakpoints(); // Set breakpoints at three possible offsets from PC const tmpBreakpoints = [pc + 1, pc + 2, pc + 4]; for (const offset of tmpBreakpoints) { debugadapter_1.logger.log(`[BP] Temporary Breakpoint at ${(0, strings_1.formatAddress)(offset)}`); this.temporaryBreakpoints.add(offset); await this.gdb.setBreakpoint(offset); } } hasTemporaryBreakpoints() { return this.temporaryBreakpoints.size > 0; } temporaryBreakpointAtAddress(pc) { return this.temporaryBreakpoints.has(pc); } async clearTemporaryBreakpoints() { for (const address of this.temporaryBreakpoints.values()) { await this.gdb.removeBreakpoint(address); } this.temporaryBreakpoints.clear(); } } exports.default = BreakpointManager; //# sourceMappingURL=breakpointManager.js.map