uae-dap
Version:
Debug Adapter Protocol for Amiga development with FS-UAE or WinUAE
738 lines • 31 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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UAEDebugSession = void 0;
const debugadapter_1 = require("@vscode/debugadapter");
const logger_1 = require("@vscode/debugadapter/lib/logger");
const async_mutex_1 = require("async-mutex");
const path_1 = require("path");
const gdbClient_1 = require("./gdbClient");
const breakpointManager_1 = __importDefault(require("./breakpointManager"));
const strings_1 = require("./utils/strings");
const emulator_1 = require("./emulator");
const variableManager_1 = __importStar(require("./variableManager"));
const vasm_1 = require("./vasm");
const amigaHunkParser_1 = require("./amigaHunkParser");
const sourceMap_1 = __importDefault(require("./sourceMap"));
const disassembly_1 = require("./disassembly");
const hardware_1 = require("./hardware");
const stackManager_1 = __importDefault(require("./stackManager"));
const promise_retry_1 = __importDefault(require("promise-retry"));
const help_1 = require("./help");
/**
* Default values for custom launch/attach args
*/
const defaultArgs = {
program: undefined,
remoteProgram: undefined,
stopOnEntry: false,
trace: false,
exceptionMask: 0b111100,
serverName: "localhost",
serverPort: 2345,
emulatorBin: undefined,
emulatorType: process.platform === "win32" ? "winuae" : "fs-uae",
emulatorArgs: [],
memoryFormats: {
watch: {
length: 104,
wordLength: 2,
},
hover: {
length: 24,
wordLength: 2,
},
},
};
class UAEDebugSession extends debugadapter_1.LoggingDebugSession {
constructor() {
super();
this.trace = false;
this.stopOnEntry = false;
this.exceptionMask = defaultArgs.exceptionMask;
// This can be replaced with a persistent implementation in VS Code
this.dataBreakpointSizes = new Map();
// this debugger uses zero-based lines and columns
this.setDebuggerLinesStartAt1(false);
this.setDebuggerColumnsStartAt1(false);
this.gdb = new gdbClient_1.GdbClient();
process.on("unhandledRejection", (reason, p) => {
debugadapter_1.logger.error(reason + " Unhandled Rejection at Promise " + p);
});
process.on("uncaughtException", (err) => {
debugadapter_1.logger.error("Uncaught Exception thrown: " + this.errorString(err));
process.exit(1);
});
const mutex = new async_mutex_1.Mutex();
this.gdb.on("stop", (haltStatus) => {
mutex.runExclusive(async () => {
return this.handleStop(haltStatus).catch((err) => {
debugadapter_1.logger.error(this.errorString(err));
});
});
});
this.gdb.on("end", this.shutdown.bind(this));
this.gdb.on("output", (msg) => {
this.sendEvent(new debugadapter_1.OutputEvent(msg + "\n", "console"));
});
}
initializeRequest(response) {
// build and return the capabilities of this debug adapter:
response.body = {
...response.body,
supportsCompletionsRequest: true,
supportsConditionalBreakpoints: true,
supportsHitConditionalBreakpoints: true,
supportsLogPoints: true,
supportsConfigurationDoneRequest: true,
supportsDataBreakpoints: true,
supportsDisassembleRequest: true,
supportsEvaluateForHovers: true,
supportsExceptionInfoRequest: true,
supportsExceptionOptions: true,
supportsInstructionBreakpoints: true,
supportsReadMemoryRequest: true,
supportsRestartRequest: true,
supportsRestartFrame: false,
supportsSetVariable: true,
supportsSingleThreadExecutionRequests: false,
supportsStepBack: false,
supportsSteppingGranularity: false,
supportsValueFormattingOptions: true,
supportsWriteMemoryRequest: true,
exceptionBreakpointFilters: [
{
filter: "all",
label: "All Exceptions",
default: true,
},
],
};
this.sendResponse(response);
}
async launchRequest(response, customArgs) {
await this.launchOrAttach(response, customArgs, true, !customArgs.noDebug);
}
async attachRequest(response, customArgs) {
await this.launchOrAttach(response, customArgs, false);
}
async launchOrAttach(response, customArgs, startEmulator = true, debug = true) {
// Merge in default args
const args = {
...defaultArgs,
...customArgs,
memoryFormats: {
...defaultArgs.memoryFormats,
...customArgs.memoryFormats,
},
};
try {
this.trace = args.trace;
this.stopOnEntry = args.stopOnEntry;
this.exceptionMask = args.exceptionMask;
// Initialize logger:
debugadapter_1.logger.init((e) => this.sendEvent(e));
debugadapter_1.logger.setup(args.trace ? logger_1.LogLevel.Verbose : logger_1.LogLevel.Warn);
debugadapter_1.logger.log("[LAUNCH] " + JSON.stringify(args, null, 2));
// Start the emulator
if (startEmulator) {
this.emulator = emulator_1.Emulator.getInstance(args.emulatorType);
// Program is required when debugging
if (debug && !args.program) {
throw new Error("Missing program argument in launch request");
}
// Set default remoteProgram from program
if (args.program && !args.remoteProgram) {
args.remoteProgram = "SYS:" + (0, path_1.basename)(args.program);
}
// Determine HDD mount from program and remoteProgram
let mountDir = undefined;
if (args.program && args.remoteProgram) {
// By default mount the dir containing the remote program as hard drive 0 (SYS:)
mountDir = args.program
.replace(/\\/, "/")
.replace(args.remoteProgram.replace(/^.+:/, ""), "");
}
const runOpts = {
bin: args.emulatorBin,
args: args.emulatorArgs,
mountDir,
onExit: () => {
this.sendEvent(new debugadapter_1.TerminatedEvent());
},
};
if (debug) {
await this.emulator.debug({
...runOpts,
serverPort: args.serverPort,
remoteProgram: args.remoteProgram,
});
}
else {
await this.emulator.run(runOpts);
}
}
else {
debugadapter_1.logger.log(`[LAUNCH] Not starting emulator`);
}
if (!debug) {
debugadapter_1.logger.log(`[LAUNCH] Not debugging`);
return;
}
this.sendEvent(new debugadapter_1.OutputEvent(help_1.helpSummary, "console"));
// Connect to the remote debugger
await (0, promise_retry_1.default)((retry, attempt) => {
debugadapter_1.logger.log(`[LAUNCH] Connecting to remote debugger... [${attempt}]`);
return this.gdb
.connect(args.serverName, args.serverPort)
.catch(retry);
}, { retries: 20, factor: 1.1 });
this.gdb.setExceptionBreakpoint(args.exceptionMask);
for (const threadId of [hardware_1.Threads.CPU, hardware_1.Threads.COPPER]) {
this.sendEvent(new debugadapter_1.ThreadEvent("started", threadId));
}
// Get info to Initialize source map
const [hunks, offsets] = await Promise.all([
(0, amigaHunkParser_1.parseHunksFromFile)(args.program),
this.gdb.getOffsets(),
]);
const sourceMap = new sourceMap_1.default(hunks, offsets);
// Initialize managers:
this.variables = new variableManager_1.default(this.gdb, sourceMap, this.getSourceConstantResolver(customArgs), args.memoryFormats);
this.disassembly = new disassembly_1.DisassemblyManager(this.gdb, this.variables, sourceMap);
this.breakpoints = new breakpointManager_1.default(this.gdb, sourceMap, this.disassembly, this.dataBreakpointSizes);
this.stack = new stackManager_1.default(this.gdb, sourceMap, this.disassembly);
if (args.stopOnEntry) {
debugadapter_1.logger.log("[LAUNCH] Stopping on entry");
await this.gdb.stepIn(hardware_1.Threads.CPU);
this.sendStoppedEvent(hardware_1.Threads.CPU, "entry");
}
// Tell client that we can now handle breakpoints etc.
this.sendEvent(new debugadapter_1.InitializedEvent());
}
catch (err) {
this.sendEvent(new debugadapter_1.TerminatedEvent());
response.success = false;
if (err instanceof Error) {
response.message = err.message;
}
}
this.sendResponse(response);
}
configurationDoneRequest(response) {
this.handleAsyncRequest(response, async () => {
if (!this.stopOnEntry) {
debugadapter_1.logger.log("Continuing execution after config done");
await this.gdb.continueExecution(hardware_1.Threads.CPU);
}
});
}
restartRequest(response) {
this.handleAsyncRequest(response, async () => {
await this.gdb.monitor("reset");
});
}
shutdown() {
debugadapter_1.logger.log(`Shutting down`);
this.gdb.destroy();
this.emulator?.destroy();
}
// Breakpoints:
async setBreakPointsRequest(response, args) {
this.handleAsyncRequest(response, async () => {
const breakpoints = await this.breakpointManager().setSourceBreakpoints(args.source, args.breakpoints || []);
response.body = { breakpoints };
response.success = true;
});
}
async setInstructionBreakpointsRequest(response, args) {
this.handleAsyncRequest(response, async () => {
const breakpoints = await this.breakpointManager().setInstructionBreakpoints(args.breakpoints);
response.body = { breakpoints };
response.success = true;
});
}
async dataBreakpointInfoRequest(response, args) {
this.handleAsyncRequest(response, async () => {
if (!args.variablesReference || !args.name) {
return;
}
const variables = this.variableManager();
const { type } = variables.getScopeReference(args.variablesReference);
if (type === variableManager_1.ScopeType.Symbols || type === variableManager_1.ScopeType.Registers) {
const variableName = args.name;
const vars = await variables.getVariables();
const value = vars[variableName];
if (typeof value === "number") {
const displayValue = variables.formatVariable(variableName, value);
const isRegister = type === variableManager_1.ScopeType.Registers;
const dataId = `${variableName}(${displayValue})`;
response.body = {
dataId,
description: isRegister ? displayValue : dataId,
accessTypes: ["read", "write", "readWrite"],
canPersist: true,
};
}
}
});
}
async setDataBreakpointsRequest(response, args) {
this.handleAsyncRequest(response, async () => {
for (const i in args.breakpoints) {
const bp = args.breakpoints[i];
const { name, displayValue } = this.breakpointManager().parseDataId(bp.dataId);
const size = this.dataBreakpointSizes.get(bp.dataId);
if (!size) {
const sizeInput = await this.getDataBreakpointSize(displayValue, name);
this.dataBreakpointSizes.set(bp.dataId, sizeInput);
}
}
const breakpoints = await this.breakpointManager().setDataBreakpoints(args.breakpoints);
response.body = { breakpoints };
response.success = true;
});
}
async setExceptionBreakPointsRequest(response, args) {
this.handleAsyncRequest(response, async () => {
if (this.exceptionMask) {
// There is only one filter - "all exceptions" so just use it to toggle on/off
if (args.filters.length > 0) {
await this.gdb.setExceptionBreakpoint(this.exceptionMask);
}
else {
await this.gdb.setExceptionBreakpoint(0);
}
}
response.success = true;
});
}
// Running program info:
async threadsRequest(response) {
response.body = {
threads: [
{ id: hardware_1.Threads.CPU, name: "cpu" },
{ id: hardware_1.Threads.COPPER, name: "copper" },
],
};
this.sendResponse(response);
}
async stackTraceRequest(response, { threadId }) {
this.handleAsyncRequest(response, async () => {
const stack = this.stackManager();
const positions = await stack.getPositions(threadId);
const stackFrames = await stack.getStackTrace(threadId, positions);
stackFrames.map(({ source }) => this.processSource(source));
if (threadId === hardware_1.Threads.CPU && positions[0]) {
await this.onCpuFrame(positions[0].pc);
const breakpoints = this.breakpointManager();
if (breakpoints.temporaryBreakpointAtAddress(positions[0].pc)) {
await breakpoints.clearTemporaryBreakpoints();
}
}
response.body = { stackFrames, totalFrames: positions.length };
});
}
scopesRequest(response, { frameId }) {
this.handleAsyncRequest(response, async () => {
response.body = {
scopes: this.variableManager().getScopes(frameId),
};
});
}
async exceptionInfoRequest(response) {
this.handleAsyncRequest(response, async () => {
const haltStatus = await this.gdb.getHaltStatus();
if (haltStatus) {
response.body = {
exceptionId: haltStatus.signal.toString(),
description: haltStatus.label,
breakMode: "always",
};
}
});
}
// Execution flow:
async pauseRequest(response, { threadId }) {
this.sendResponse(response);
await this.gdb.pause(hardware_1.Threads.CPU);
this.sendStoppedEvent(threadId, "pause");
}
async continueRequest(response) {
response.body = { allThreadsContinued: true };
this.sendResponse(response);
await this.gdb.continueExecution(hardware_1.Threads.CPU);
}
async nextRequest(response, { threadId }) {
this.sendResponse(response);
const [frame] = await this.stackManager().getPositions(threadId);
await this.gdb.stepToRange(threadId, frame.pc, frame.pc);
this.sendStoppedEvent(threadId, "step");
}
async stepInRequest(response, { threadId }) {
this.sendResponse(response);
if (threadId === hardware_1.Threads.COPPER) {
const [frame] = await this.stackManager().getPositions(threadId);
await this.gdb.stepToRange(threadId, frame.pc, frame.pc);
}
else {
await this.gdb.stepIn(threadId);
}
this.sendStoppedEvent(threadId, "step");
}
async stepOutRequest(response, { threadId }) {
this.sendResponse(response);
const positions = await this.stackManager().getPositions(threadId);
if (positions[1]) {
// Set a temp breakpoint after PC of prev stack frame
const { pc } = positions[1];
await this.breakpointManager().addTemporaryBreakpoints(pc);
await this.gdb.continueExecution(threadId);
}
else {
// Step over instead
const { pc } = positions[0];
await this.gdb.stepToRange(threadId, pc, pc);
this.sendStoppedEvent(threadId, "step");
}
}
// Variables:
async variablesRequest(response, args) {
this.handleAsyncRequest(response, async () => {
// Try to look up stored reference
const variables = await this.variableManager().getVariablesByReference(args.variablesReference);
response.body = { variables };
});
}
async setVariableRequest(response, { variablesReference, name, value }) {
this.handleAsyncRequest(response, async () => {
const newValue = await this.variableManager().setVariable(variablesReference, name, value);
response.body = {
value: newValue,
};
});
}
async evaluateRequest(response, args) {
this.handleAsyncRequest(response, async () => {
args.expression = args.expression.trim();
// UAE debug console commands with '$' prefix
if (args.expression.startsWith("$")) {
const res = await this.gdb.monitor("console " + args.expression.substring(1).trim());
this.sendEvent(new debugadapter_1.OutputEvent(res, "console"));
response.body = { result: "", variablesReference: 0 };
return;
}
// Command help
if (args.expression.match(/^h\s/i)) {
const cmd = args.expression.replace(/^h\s+/, "");
const help = help_1.commandHelp[cmd] ||
`No help available for command '${cmd}'`;
this.sendEvent(new debugadapter_1.OutputEvent(help, "console"));
response.body = { result: "", variablesReference: 0 };
return;
}
// Expression
const body = await this.variableManager().evaluateExpression(args);
if (body) {
response.body = body;
return;
}
// Default help summary
response.body = { result: "", variablesReference: 0 };
this.sendEvent(new debugadapter_1.OutputEvent(help_1.helpSummary, "console"));
});
}
async completionsRequest(response, args) {
this.handleAsyncRequest(response, async () => {
const targets = await this.variableManager().getCompletions(args.text, args.frameId);
response.body = { targets };
});
}
// Memory:
async readMemoryRequest(response, args) {
this.handleAsyncRequest(response, async () => {
const address = parseInt(args.memoryReference);
let size = 0;
let memory = "";
const maxChunkSize = 1000;
let remaining = args.count;
while (remaining > 0) {
let chunkSize = maxChunkSize;
if (remaining < chunkSize) {
chunkSize = remaining;
}
memory += await this.gdb.readMemory(address + size, chunkSize);
remaining -= chunkSize;
size += chunkSize;
}
let unreadable = args.count - size;
if (unreadable < 0) {
unreadable = 0;
}
response.body = {
address: address.toString(16),
data: (0, strings_1.hexToBase64)(memory),
};
});
}
async writeMemoryRequest(response, args) {
this.handleAsyncRequest(response, async () => {
let address = parseInt(args.memoryReference);
if (args.offset) {
address += args.offset;
}
const hexString = (0, strings_1.base64ToHex)(args.data);
const count = hexString.length;
const maxChunkSize = 1000;
let remaining = count;
let size = 0;
while (remaining > 0) {
let chunkSize = maxChunkSize;
if (remaining < chunkSize) {
chunkSize = remaining;
}
await this.gdb.writeMemory(address, hexString.substring(size, chunkSize));
remaining -= chunkSize;
size += chunkSize;
}
response.body = {
bytesWritten: size,
};
});
}
// Disassembly:
async disassembleRequest(response, args) {
this.handleAsyncRequest(response, async () => {
const instructions = await this.disassemblyManager().disassemble(args);
instructions.map(({ location }) => this.processSource(location));
response.body = { instructions };
});
}
sourceRequest(response, args) {
this.handleAsyncRequest(response, async () => {
const content = await this.disassembly?.getDisassembledFileContentsByRef(args.sourceReference);
if (!content) {
throw new Error("Source not found");
}
response.body = { content };
});
}
async customRequest(command, response,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args) {
if (command === "disassembledFileContents") {
const fileReq = args;
const content = await this.disassemblyManager().getDisassembledFileContentsByPath(fileReq.path);
response.body = { content };
return this.sendResponse(response);
}
if (command === "modifyVariableFormat") {
const variableReq = args;
this.variableManager().setVariableFormat(variableReq.variableInfo.variable.name, variableReq.variableDisplayFormat);
this.sendEvent(new debugadapter_1.InvalidatedEvent(["variables"]));
return this.sendResponse(response);
}
super.customRequest(command, response, args);
}
// Internals:
async handleAsyncRequest(response, cb) {
try {
await cb();
}
catch (err) {
if (err instanceof Error) {
// Display stack trace in trace mode
response.message = this.trace ? err.stack ?? err.message : err.message;
}
response.success = false;
}
this.sendResponse(response);
}
sendStoppedEvent(threadId, reason, preserveFocusHint) {
this.sendEvent({
event: "stopped",
body: {
reason,
threadId,
allThreadsStopped: true,
preserveFocusHint,
},
});
}
errorString(err) {
if (err instanceof Error)
return this.trace ? err.stack || err.message : err.message;
return String(err);
}
/**
* Process a Source object before returning in to the client
*/
processSource(source) {
// Ensure path is in correct format for client
if (source?.path) {
source.path = this.convertDebuggerPathToClient(source.path);
}
}
/**
* Event handler for stop/halt event
*/
async handleStop(e) {
debugadapter_1.logger.log(`[STOP] ${e.label} thread: ${e.threadId}`);
// Any other halt code other than TRAP must be an exception:
if (e.signal !== gdbClient_1.HaltSignal.TRAP) {
debugadapter_1.logger.log(`[STOP] Exception`);
this.sendStoppedEvent(hardware_1.Threads.CPU, "exception");
return;
}
// Get stack position to find current PC address for thread
const threadId = e.threadId ?? hardware_1.Threads.CPU;
const { pc, stackFrameIndex } = await this.stackManager().getStackPosition(threadId, gdbClient_1.DEFAULT_FRAME_INDEX);
const manager = this.breakpointManager();
// Check temporary breakpoints:
// Are we waiting for a temporary breakpoint?
if (manager.hasTemporaryBreakpoints()) {
// Did we hit it or something else?
if (manager.temporaryBreakpointAtAddress(pc)) {
debugadapter_1.logger.log(`[STOP] Matched temporary breakpoint at address ${(0, strings_1.formatAddress)(pc)}`);
await manager.clearTemporaryBreakpoints();
this.sendStoppedEvent(hardware_1.Threads.CPU, "step");
}
else {
debugadapter_1.logger.log(`[STOP] ignoring while waiting for temporary breakpoint`);
await this.gdb.continueExecution(hardware_1.Threads.CPU);
}
return;
}
// Check instruction breakpoints:
if (manager.instructionBreakpointAtAddress(pc)) {
debugadapter_1.logger.log(`[STOP] Matched instruction breakpoint at address ${(0, strings_1.formatAddress)(pc)}`);
this.sendStoppedEvent(threadId, "instruction breakpoint");
return;
}
// Check source / data breakpoints:
let ref = manager.sourceBreakpointAtAddress(pc);
let type = "breakpoint";
if (!ref) {
ref = manager.dataBreakpointAtAddress(pc);
type = "data breakpoint";
}
if (!ref) {
debugadapter_1.logger.log(`[STOP] No breakpoint found at address ${(0, strings_1.formatAddress)(pc)}`);
this.sendStoppedEvent(threadId, "breakpoint");
return;
}
debugadapter_1.logger.log(`[STOP] Matched ${type} at address ${(0, strings_1.formatAddress)(pc)}`);
// Decide whether to stop at this breakpoint or continue
// Needs to check for conditional / log breakpoints etc.
let shouldStop = true;
const bp = ref.breakpoint;
// Log point:
const logMessage = bp.logMessage;
if (logMessage) {
// Interpolate variables
const message = await (0, strings_1.replaceAsync)(logMessage, /\{((#\{((#\{[^}]*\})|[^}])*\})|[^}])*\}/g, // Up to two levels of nesting
async (match) => {
try {
const v = await this.variableManager().evaluate(match.substring(1, match.length - 1), stackFrameIndex);
return (0, strings_1.formatHexadecimal)(v, 0);
}
catch {
return "#error";
}
});
this.sendEvent(new debugadapter_1.OutputEvent(message + "\n", "console"));
shouldStop = false;
}
// Conditional breakpoint:
if (bp.condition) {
const result = await this.variableManager().evaluate(bp.condition, stackFrameIndex);
const stop = !!result;
debugadapter_1.logger.log(`[STOP] Evaluated conditional breakpoint ${bp.condition} = ${stop}`);
}
// Hit count:
if (bp.hitCondition) {
const evaluatedCondition = await this.variableManager().evaluate(bp.hitCondition, stackFrameIndex);
debugadapter_1.logger.log(`[STOP] Hit count: ${ref.hitCount}/${evaluatedCondition}`);
if (++ref.hitCount === evaluatedCondition) {
this.gdb.removeBreakpoint(pc);
}
else {
shouldStop = false;
}
}
if (shouldStop) {
debugadapter_1.logger.log(`[STOP] stopping at ${type}`);
this.sendStoppedEvent(threadId, type);
}
else {
debugadapter_1.logger.log(`[STOP] continuing execution`);
await this.gdb.continueExecution(threadId);
}
}
// Implementation specific overrides:
// VS Code extension will override these methods to add native behaviours.
getSourceConstantResolver(args) {
return new vasm_1.VasmSourceConstantResolver(args.vasm);
}
async onCpuFrame(_address) {
// This will trigger an update to the disassembly view
}
async getDataBreakpointSize(_displayValue, _name) {
// This will prompt for user input in VS Code
// TODO: could have a better guess at size by looking at disassembly e.g. `dc.l` or address of next label
return 2;
}
// Manager getters:
// Ensures these are defined i.e. the program is started
breakpointManager() {
if (!this.breakpoints) {
throw new Error("BreakpointManager not initialized");
}
return this.breakpoints;
}
variableManager() {
if (!this.variables) {
throw new Error("VariableManager not initialized");
}
return this.variables;
}
disassemblyManager() {
if (!this.disassembly) {
throw new Error("DisassemblyManager not initialized");
}
return this.disassembly;
}
stackManager() {
if (!this.stack) {
throw new Error("StackManager not initialized");
}
return this.stack;
}
}
exports.UAEDebugSession = UAEDebugSession;
//# sourceMappingURL=debugSession.js.map