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