From 3ce7ccff4116da34370cecfb329420f266499ec4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 14 Feb 2024 21:36:53 -0800 Subject: [PATCH] debug: initial support of breakpoint modes (#205251) This supports breakpoint modes on exception, instruction, and source breakpoints. It doesn't yet do data breakpoints since I was having trouble figuring out a good user flow for that. You can test this on the `connor4312/breakpoint-modes` branch of mock-debug, which I'll merge in after the next DAP release. ![](https://memes.peet.io/img/24-02-68cd0222-8ef5-4d39-aa51-81ba5b8c2405.png) --- .../api/browser/mainThreadDebugService.ts | 7 +- .../workbench/api/common/extHost.protocol.ts | 4 + .../api/common/extHostDebugService.ts | 12 +- src/vs/workbench/api/common/extHostTypes.ts | 18 +- .../contrib/debug/browser/breakpointWidget.ts | 53 ++- .../contrib/debug/browser/breakpointsView.ts | 114 ++++- .../debug/browser/debugEditorActions.ts | 2 +- .../contrib/debug/browser/debugService.ts | 18 +- .../contrib/debug/browser/debugSession.ts | 9 +- .../contrib/debug/browser/disassemblyView.ts | 2 +- .../debug/browser/media/breakpointWidget.css | 93 ++-- .../contrib/debug/browser/variablesView.ts | 6 +- .../workbench/contrib/debug/common/debug.ts | 32 +- .../contrib/debug/common/debugModel.ts | 411 ++++++++++++------ .../contrib/debug/common/debugProtocol.d.ts | 194 ++++++--- .../contrib/debug/common/debugStorage.ts | 17 +- .../debug/test/browser/breakpoints.test.ts | 10 +- .../debug/test/common/debugModel.test.ts | 13 +- .../contrib/debug/test/common/mockDebug.ts | 3 +- 19 files changed, 734 insertions(+), 284 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 1b383b49d75..3178df6d093 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -217,14 +217,15 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb column: l.character > 0 ? l.character + 1 : undefined, // a column value of 0 results in an omitted column attribute; see #46784 condition: l.condition, hitCondition: l.hitCondition, - logMessage: l.logMessage + logMessage: l.logMessage, + mode: l.mode, } ); this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps); } else if (dto.type === 'function') { - this.debugService.addFunctionBreakpoint(dto.functionName, dto.id); + this.debugService.addFunctionBreakpoint(dto.functionName, dto.id, dto.mode); } else if (dto.type === 'data') { - this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType); + this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType, dto.mode); } } return Promise.resolve(); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b8b4266b5e1..34e3f6664b3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2277,11 +2277,13 @@ export interface IBreakpointDto { condition?: string; hitCondition?: string; logMessage?: string; + mode?: string; } export interface IFunctionBreakpointDto extends IBreakpointDto { type: 'function'; functionName: string; + mode?: string; } export interface IDataBreakpointDto extends IBreakpointDto { @@ -2291,6 +2293,7 @@ export interface IDataBreakpointDto extends IBreakpointDto { label: string; accessTypes?: DebugProtocol.DataBreakpointAccessType[]; accessType: DebugProtocol.DataBreakpointAccessType; + mode?: string; } export interface ISourceBreakpointDto extends IBreakpointDto { @@ -2317,6 +2320,7 @@ export interface ISourceMultiBreakpointDto { logMessage?: string; line: number; character: number; + mode?: string; }[]; } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 133ff216767..38d5f2a3205 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -427,7 +427,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E hitCondition: bp.hitCondition, logMessage: bp.logMessage, line: bp.location.range.start.line, - character: bp.location.range.start.character + character: bp.location.range.start.character, + mode: bp.mode, }); } else if (bp instanceof FunctionBreakpoint) { dtos.push({ @@ -437,7 +438,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E hitCondition: bp.hitCondition, logMessage: bp.logMessage, condition: bp.condition, - functionName: bp.functionName + functionName: bp.functionName, + mode: bp.mode, }); } } @@ -713,12 +715,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E if (id && !this._breakpoints.has(id)) { let bp: Breakpoint; if (bpd.type === 'function') { - bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); + bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage, bpd.mode); } else if (bpd.type === 'data') { - bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage); + bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage, bpd.mode); } else { const uri = URI.revive(bpd.uri); - bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); + bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage, bpd.mode); } setBreakpointId(bp, id); this._breakpoints.set(id, bp); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6d563a0e6a9..d6c3996105e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2918,8 +2918,9 @@ export class Breakpoint { readonly condition?: string; readonly hitCondition?: string; readonly logMessage?: string; + readonly mode?: string; - protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) { + protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) { this.enabled = typeof enabled === 'boolean' ? enabled : true; if (typeof condition === 'string') { this.condition = condition; @@ -2930,6 +2931,9 @@ export class Breakpoint { if (typeof logMessage === 'string') { this.logMessage = logMessage; } + if (typeof mode === 'string') { + this.mode = mode; + } } get id(): string { @@ -2944,8 +2948,8 @@ export class Breakpoint { export class SourceBreakpoint extends Breakpoint { readonly location: Location; - constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) { - super(enabled, condition, hitCondition, logMessage); + constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) { + super(enabled, condition, hitCondition, logMessage, mode); if (location === null) { throw illegalArgument('location'); } @@ -2957,8 +2961,8 @@ export class SourceBreakpoint extends Breakpoint { export class FunctionBreakpoint extends Breakpoint { readonly functionName: string; - constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) { - super(enabled, condition, hitCondition, logMessage); + constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) { + super(enabled, condition, hitCondition, logMessage, mode); this.functionName = functionName; } } @@ -2969,8 +2973,8 @@ export class DataBreakpoint extends Breakpoint { readonly dataId: string; readonly canPersist: boolean; - constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) { - super(enabled, condition, hitCondition, logMessage); + constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) { + super(enabled, condition, hitCondition, logMessage, mode); if (!dataId) { throw illegalArgument('dataId'); } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 877c0921a4c..26ae95ccf1d 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -85,10 +85,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private selectBreakpointContainer!: HTMLElement; private input!: IActiveCodeEditor; private selectBreakpointBox!: SelectBox; + private selectModeBox?: SelectBox; private toDispose: lifecycle.IDisposable[]; private conditionInput = ''; private hitCountInput = ''; private logMessageInput = ''; + private modeInput?: DebugProtocol.BreakpointMode; private breakpoint: IBreakpoint | undefined; private context: Context; private heightInPx: number | undefined; @@ -216,6 +218,8 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.updateContextInput(); }); + this.createModesInput(container); + this.inputContainer = $('.inputContainer'); this.createBreakpointInput(dom.append(container, this.inputContainer)); @@ -232,6 +236,33 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi setTimeout(() => this.focusInput(), 150); } + private createModesInput(container: HTMLElement) { + const modes = this.debugService.getModel().getBreakpointModes('source'); + if (modes.length <= 1) { + return; + } + + const sb = this.selectModeBox = new SelectBox( + [ + { text: nls.localize('bpMode', 'Mode'), isDisabled: true }, + ...modes.map(mode => ({ text: mode.label, description: mode.description })), + ], + modes.findIndex(m => m.mode === this.breakpoint?.mode) + 1, + this.contextViewService, + defaultSelectBoxStyles, + ); + this.toDispose.push(sb); + this.toDispose.push(sb.onDidSelect(e => { + this.modeInput = modes[e.index - 1]; + })); + + const modeWrapper = $('.select-mode-container'); + const selectionWrapper = $('.select-box-container'); + dom.append(modeWrapper, selectionWrapper); + sb.render(selectionWrapper); + dom.append(container, modeWrapper); + } + private createTriggerBreakpointInput(container: HTMLElement) { const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp !== this.breakpoint); @@ -404,10 +435,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi if (success) { // if there is already a breakpoint on this location - remove it. - let condition = this.breakpoint && this.breakpoint.condition; - let hitCondition = this.breakpoint && this.breakpoint.hitCondition; - let logMessage = this.breakpoint && this.breakpoint.logMessage; - let triggeredBy = this.breakpoint && this.breakpoint.triggeredBy; + let condition = this.breakpoint?.condition; + let hitCondition = this.breakpoint?.hitCondition; + let logMessage = this.breakpoint?.logMessage; + let triggeredBy = this.breakpoint?.triggeredBy; + let mode = this.breakpoint?.mode; + let modeLabel = this.breakpoint?.modeLabel; this.rememberInput(); @@ -420,6 +453,10 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi if (this.logMessageInput || this.context === Context.LOG_MESSAGE) { logMessage = this.logMessageInput; } + if (this.selectModeBox) { + mode = this.modeInput?.mode; + modeLabel = this.modeInput?.label; + } if (this.context === Context.TRIGGER_POINT) { // currently, trigger points don't support additional conditions: condition = undefined; @@ -434,7 +471,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi condition, hitCondition, logMessage, - triggeredBy + triggeredBy, + mode, + modeLabel, }); this.debugService.updateBreakpoints(this.breakpoint.originalUri, data, false).then(undefined, onUnexpectedError); } else { @@ -447,7 +486,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi condition, hitCondition, logMessage, - triggeredBy + triggeredBy, + mode, + modeLabel, }]); } } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 5222b57d2e7..e60c8079a14 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -39,6 +39,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -48,7 +49,7 @@ import { IEditorPane } from 'vs/workbench/common/editor'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; import { DisassemblyView } from 'vs/workbench/contrib/debug/browser/disassemblyView'; -import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, DEBUG_SCHEME, DebuggerString, IBaseBreakpoint, IBreakpoint, IBreakpointEditorContribution, IDataBreakpoint, IDebugModel, IDebugService, IEnablement, IExceptionBreakpoint, IFunctionBreakpoint, IInstructionBreakpoint, State } from 'vs/workbench/contrib/debug/common/debug'; +import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_HAS_MODES, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, DEBUG_SCHEME, DebuggerString, IBaseBreakpoint, IBreakpoint, IBreakpointEditorContribution, IBreakpointUpdateData, IDataBreakpoint, IDebugModel, IDebugService, IEnablement, IExceptionBreakpoint, IFunctionBreakpoint, IInstructionBreakpoint, State } from 'vs/workbench/contrib/debug/common/debug'; import { Breakpoint, DataBreakpoint, ExceptionBreakpoint, FunctionBreakpoint, InstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -84,6 +85,7 @@ export class BreakpointsView extends ViewPane { private ignoreLayout = false; private menu: IMenu; private breakpointItemType: IContextKey; + private breakpointHasMultipleModes: IContextKey; private breakpointSupportsCondition: IContextKey; private _inputBoxData: InputBoxData | undefined; breakpointInputFocused: IContextKey; @@ -116,6 +118,7 @@ export class BreakpointsView extends ViewPane { this.menu = menuService.createMenu(MenuId.DebugBreakpointsContext, contextKeyService); this._register(this.menu); this.breakpointItemType = CONTEXT_BREAKPOINT_ITEM_TYPE.bindTo(contextKeyService); + this.breakpointHasMultipleModes = CONTEXT_BREAKPOINT_HAS_MODES.bindTo(contextKeyService); this.breakpointSupportsCondition = CONTEXT_BREAKPOINT_SUPPORTS_CONDITION.bindTo(contextKeyService); this.breakpointInputFocused = CONTEXT_BREAKPOINT_INPUT_FOCUSED.bindTo(contextKeyService); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); @@ -132,12 +135,12 @@ export class BreakpointsView extends ViewPane { const delegate = new BreakpointsDelegate(this); this.list = this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [ - this.instantiationService.createInstance(BreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType), - new ExceptionBreakpointsRenderer(this.menu, this.breakpointSupportsCondition, this.breakpointItemType, this.debugService), + this.instantiationService.createInstance(BreakpointsRenderer, this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType), + new ExceptionBreakpointsRenderer(this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType, this.debugService), new ExceptionBreakpointInputRenderer(this, this.debugService, this.contextViewService), this.instantiationService.createInstance(FunctionBreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType), new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.labelService), - this.instantiationService.createInstance(DataBreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType), + this.instantiationService.createInstance(DataBreakpointsRenderer, this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType), new DataBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.labelService), this.instantiationService.createInstance(InstructionBreakpointsRenderer), ], { @@ -426,6 +429,7 @@ interface IBaseBreakpointTemplateData { context: BreakpointItem; actionBar: ActionBar; toDispose: IDisposable[]; + badge: HTMLElement; } interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData { @@ -433,7 +437,6 @@ interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateDat } interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData { - lineNumber: HTMLElement; filePath: HTMLElement; } @@ -486,6 +489,7 @@ class BreakpointsRenderer implements IListRenderer, private breakpointSupportsCondition: IContextKey, private breakpointItemType: IContextKey, @IDebugService private readonly debugService: IDebugService, @@ -521,8 +525,8 @@ class BreakpointsRenderer implements IListRenderer 1); createAndFillInActionBarActions(this.menu, { arg: breakpoint, shouldForwardArgs: true }, { primary, secondary: [] }, 'inline'); data.actionBar.clear(); data.actionBar.push(primary, { icon: true, label: false }); @@ -567,6 +576,7 @@ class ExceptionBreakpointsRenderer implements IListRenderer, private breakpointSupportsCondition: IContextKey, private breakpointItemType: IContextKey, private debugService: IDebugService @@ -598,6 +608,9 @@ class ExceptionBreakpointsRenderer implements IListRenderer 1); createAndFillInActionBarActions(this.menu, { arg: exceptionBreakpoint, shouldForwardArgs: true }, { primary, secondary: [] }, 'inline'); data.actionBar.clear(); data.actionBar.push(primary, { icon: true, label: false }); @@ -661,6 +682,8 @@ class FunctionBreakpointsRenderer implements IListRenderer, private breakpointSupportsCondition: IContextKey, private breakpointItemType: IContextKey, @IDebugService private readonly debugService: IDebugService, @@ -738,6 +769,8 @@ class DataBreakpointsRenderer implements IListRenderer 1); this.breakpointItemType.set('dataBreakpoint'); createAndFillInActionBarActions(this.menu, { arg: dataBreakpoint, shouldForwardArgs: true }, { primary, secondary: [] }, 'inline'); data.actionBar.clear(); @@ -817,6 +858,8 @@ class InstructionBreakpointsRenderer implements IListRenderer { view.renderInputBox({ breakpoint, type: 'hitCount' }); } }); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: 'debug.editBreakpointMode', + viewId: BREAKPOINTS_VIEW_ID, + title: localize('editMode', "Edit Mode..."), + menu: [{ + id: MenuId.DebugBreakpointsContext, + group: 'navigation', + order: 20, + when: ContextKeyExpr.and( + CONTEXT_BREAKPOINT_HAS_MODES, + ContextKeyExpr.or(CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('breakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('exceptionBreakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('instructionBreakpoint')) + ) + }] + }); + } + + async runInView(accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IBreakpoint) { + const kind = breakpoint instanceof Breakpoint ? 'source' : breakpoint instanceof InstructionBreakpoint ? 'instruction' : 'exception'; + const debugService = accessor.get(IDebugService); + const modes = debugService.getModel().getBreakpointModes(kind); + const picked = await accessor.get(IQuickInputService).pick( + modes.map(mode => ({ label: mode.label, description: mode.description, mode: mode.mode })), + { placeHolder: localize('selectBreakpointMode', "Select Breakpoint Mode") } + ); + + if (!picked) { + return; + } + + if (kind === 'source') { + const data = new Map(); + data.set(breakpoint.getId(), { mode: picked.mode, modeLabel: picked.label }); + debugService.updateBreakpoints(breakpoint.originalUri, data, false); + } else if (breakpoint instanceof InstructionBreakpoint) { + debugService.removeInstructionBreakpoints(breakpoint.instructionReference, breakpoint.offset); + debugService.addInstructionBreakpoint({ ...breakpoint.toJSON(), mode: picked.mode, modeLabel: picked.label }); + } else if (breakpoint instanceof ExceptionBreakpoint) { + breakpoint.mode = picked.mode; + breakpoint.modeLabel = picked.label; + debugService.setExceptionBreakpointCondition(breakpoint, breakpoint.condition); // no-op to trigger a re-send + } + } +}); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 50a0b86f6b0..2dd7760e492 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -67,7 +67,7 @@ class ToggleBreakpointAction extends Action2 { if (toRemove) { debugService.removeInstructionBreakpoints(toRemove.instructionReference, toRemove.offset); } else { - debugService.addInstructionBreakpoint(location.reference, location.offset, location.address); + debugService.addInstructionBreakpoint({ instructionReference: location.reference, offset: location.offset, address: location.address, canPersist: false }); } } return; diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 73750df8fb6..5478398dfb6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -44,7 +44,7 @@ import { DebugTaskRunner, TaskRunResult } from 'vs/workbench/contrib/debug/brows import { CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_HAS_DEBUGGED, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_UX, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_IN_DEBUG_MODE, debuggerDisabledMessage, DEBUG_MEMORY_SCHEME, getStateLabel, IAdapterManager, IBreakpoint, IBreakpointData, ICompound, IConfig, IConfigurationManager, IDebugConfiguration, IDebugModel, IDebugService, IDebugSession, IDebugSessionOptions, IEnablement, IExceptionBreakpoint, IGlobalConfig, ILaunch, IStackFrame, IThread, IViewModel, REPL_VIEW_ID, State, VIEWLET_ID, DEBUG_SCHEME, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; -import { Breakpoint, DataBreakpoint, DebugModel, FunctionBreakpoint, InstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Breakpoint, DataBreakpoint, DebugModel, FunctionBreakpoint, IInstructionBreakpointOptions, InstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; @@ -1065,8 +1065,8 @@ export class DebugService implements IDebugService { return this.sendAllBreakpoints(); } - addFunctionBreakpoint(name?: string, id?: string): void { - this.model.addFunctionBreakpoint(name || '', id); + addFunctionBreakpoint(name?: string, id?: string, mode?: string): void { + this.model.addFunctionBreakpoint(name || '', id, mode); } async updateFunctionBreakpoint(id: string, update: { name?: string; hitCondition?: string; condition?: string }): Promise { @@ -1081,8 +1081,8 @@ export class DebugService implements IDebugService { await this.sendFunctionBreakpoints(); } - async addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType): Promise { - this.model.addDataBreakpoint(label, dataId, canPersist, accessTypes, accessType); + async addDataBreakpoint(description: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType, mode: string | undefined): Promise { + this.model.addDataBreakpoint({ description, dataId, canPersist, accessTypes, accessType, mode }); this.debugStorage.storeBreakpoints(this.model); await this.sendDataBreakpoints(); this.debugStorage.storeBreakpoints(this.model); @@ -1100,8 +1100,8 @@ export class DebugService implements IDebugService { await this.sendDataBreakpoints(); } - async addInstructionBreakpoint(instructionReference: string, offset: number, address: bigint, condition?: string, hitCondition?: string): Promise { - this.model.addInstructionBreakpoint(instructionReference, offset, address, condition, hitCondition); + async addInstructionBreakpoint(opts: IInstructionBreakpointOptions): Promise { + this.model.addInstructionBreakpoint(opts); this.debugStorage.storeBreakpoints(this.model); await this.sendInstructionBreakpoints(); this.debugStorage.storeBreakpoints(this.model); @@ -1118,8 +1118,8 @@ export class DebugService implements IDebugService { this.debugStorage.storeBreakpoints(this.model); } - setExceptionBreakpointsForSession(session: IDebugSession, data: DebugProtocol.ExceptionBreakpointsFilter[]): void { - this.model.setExceptionBreakpointsForSession(session.getId(), data); + setExceptionBreakpointsForSession(session: IDebugSession, filters: DebugProtocol.ExceptionBreakpointsFilter[]): void { + this.model.setExceptionBreakpointsForSession(session.getId(), filters); this.debugStorage.storeBreakpoints(this.model); } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index b7502e41c74..861135c6f6a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -344,6 +344,7 @@ export class DebugSession implements IDebugSession, IDisposable { this.initialized = true; this._onDidChangeState.fire(); this.debugService.setExceptionBreakpointsForSession(this, (this.raw && this.raw.capabilities.exceptionBreakpointFilters) || []); + this.debugService.getModel().registerBreakpointModes(this.configuration.type, this.raw.capabilities.breakpointModes || []); } catch (err) { this.initialized = true; this._onDidChangeState.fire(); @@ -457,7 +458,7 @@ export class DebugSession implements IDebugSession, IDisposable { const response = await this.raw.setBreakpoints({ source: rawSource, lines: breakpointsToSend.map(bp => bp.sessionAgnosticData.lineNumber), - breakpoints: breakpointsToSend.map(bp => ({ line: bp.sessionAgnosticData.lineNumber, column: bp.sessionAgnosticData.column, condition: bp.condition, hitCondition: bp.hitCondition, logMessage: bp.logMessage })), + breakpoints: breakpointsToSend.map(bp => bp.toDAP()), sourceModified }); if (response && response.body) { @@ -476,7 +477,7 @@ export class DebugSession implements IDebugSession, IDisposable { } if (this.raw.readyForBreakpoints) { - const response = await this.raw.setFunctionBreakpoints({ breakpoints: fbpts }); + const response = await this.raw.setFunctionBreakpoints({ breakpoints: fbpts.map(bp => bp.toDAP()) }); if (response && response.body) { const data = new Map(); for (let i = 0; i < fbpts.length; i++) { @@ -534,7 +535,7 @@ export class DebugSession implements IDebugSession, IDisposable { } if (this.raw.readyForBreakpoints) { - const response = await this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints }); + const response = await this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints.map(bp => bp.toDAP()) }); if (response && response.body) { const data = new Map(); for (let i = 0; i < dataBreakpoints.length; i++) { @@ -551,7 +552,7 @@ export class DebugSession implements IDebugSession, IDisposable { } if (this.raw.readyForBreakpoints) { - const response = await this.raw.setInstructionBreakpoints({ breakpoints: instructionBreakpoints.map(ib => ib.toJSON()) }); + const response = await this.raw.setInstructionBreakpoints({ breakpoints: instructionBreakpoints.map(ib => ib.toDAP()) }); if (response && response.body) { const data = new Map(); for (let i = 0; i < instructionBreakpoints.length; i++) { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index f196bb14aa4..e092df64537 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -690,7 +690,7 @@ class BreakpointRenderer implements ITableRenderer { const debugService = accessor.get(IDebugService); if (dataBreakpointInfoResponse) { - await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'write'); + await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'write', undefined); } } }); @@ -813,7 +813,7 @@ CommandsRegistry.registerCommand({ handler: async (accessor: ServicesAccessor) => { const debugService = accessor.get(IDebugService); if (dataBreakpointInfoResponse) { - await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'readWrite'); + await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'readWrite', undefined); } } }); @@ -824,7 +824,7 @@ CommandsRegistry.registerCommand({ handler: async (accessor: ServicesAccessor) => { const debugService = accessor.get(IDebugService); if (dataBreakpointInfoResponse) { - await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'read'); + await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'read', undefined); } } }); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 039bcc1fb10..86d0e94b826 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -24,6 +24,7 @@ import { ITelemetryEndpoint } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IEditorPane } from 'vs/workbench/common/editor'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; +import { IInstructionBreakpointOptions } from 'vs/workbench/contrib/debug/common/debugModel'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { ITaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -61,6 +62,7 @@ export const CONTEXT_CALLSTACK_SESSION_HAS_ONE_THREAD = new RawContextKey('watchItemType', undefined, { type: 'string', description: nls.localize('watchItemType', "Represents the item type of the focused element in the WATCH view. For example: 'expression', 'variable'") }); export const CONTEXT_CAN_VIEW_MEMORY = new RawContextKey('canViewMemory', undefined, { type: 'boolean', description: nls.localize('canViewMemory', "Indicates whether the item in the view has an associated memory refrence.") }); export const CONTEXT_BREAKPOINT_ITEM_TYPE = new RawContextKey('breakpointItemType', undefined, { type: 'string', description: nls.localize('breakpointItemType', "Represents the item type of the focused element in the BREAKPOINTS view. For example: 'breakpoint', 'exceptionBreakppint', 'functionBreakpoint', 'dataBreakpoint'") }); +export const CONTEXT_BREAKPOINT_HAS_MODES = new RawContextKey('breakpointHasModes', false, { type: 'boolean', description: nls.localize('breakpointHasModes', "Whether the breakpoint has multiple modes it can switch to.") }); export const CONTEXT_BREAKPOINT_SUPPORTS_CONDITION = new RawContextKey('breakpointSupportsCondition', false, { type: 'boolean', description: nls.localize('breakpointSupportsCondition', "True when the focused breakpoint supports conditions.") }); export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey('loadedScriptsSupported', false, { type: 'boolean', description: nls.localize('loadedScriptsSupported', "True when the focused sessions supports the LOADED SCRIPTS view") }); export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey('loadedScriptsItemType', undefined, { type: 'string', description: nls.localize('loadedScriptsItemType', "Represents the item type of the focused element in the LOADED SCRIPTS view.") }); @@ -540,6 +542,8 @@ export interface IBreakpointData { readonly logMessage?: string; readonly hitCondition?: string; readonly triggeredBy?: string; + readonly mode?: string; + readonly modeLabel?: string; } export interface IBreakpointUpdateData { @@ -549,6 +553,8 @@ export interface IBreakpointUpdateData { readonly lineNumber?: number; readonly column?: number; readonly triggeredBy?: string; + readonly mode?: string; + readonly modeLabel?: string; } export interface IBaseBreakpoint extends IEnablement { @@ -558,6 +564,10 @@ export interface IBaseBreakpoint extends IEnablement { readonly verified: boolean; readonly supported: boolean; readonly message?: string; + /** The preferred mode of the breakpoint from {@link DebugProtocol.BreakpointMode} */ + readonly mode?: string; + /** The preferred mode label of the breakpoint from {@link DebugProtocol.BreakpointMode} */ + readonly modeLabel?: string; readonly sessionsThatVerified: string[]; getIdFromAdapter(sessionId: string): number | undefined; } @@ -582,10 +592,13 @@ export interface IBreakpoint extends IBaseBreakpoint { setSessionDidTrigger(sessionId: string): void; /** Gets whether the `triggeredBy` condition has been met in the given sesison ID. */ getSessionDidTrigger(sessionId: string): boolean; + + toDAP(): DebugProtocol.SourceBreakpoint; } export interface IFunctionBreakpoint extends IBaseBreakpoint { readonly name: string; + toDAP(): DebugProtocol.FunctionBreakpoint; } export interface IExceptionBreakpoint extends IBaseBreakpoint { @@ -599,6 +612,7 @@ export interface IDataBreakpoint extends IBaseBreakpoint { readonly dataId: string; readonly canPersist: boolean; readonly accessType: DebugProtocol.DataBreakpointAccessType; + toDAP(): DebugProtocol.DataBreakpoint; } export interface IInstructionBreakpoint extends IBaseBreakpoint { @@ -606,7 +620,7 @@ export interface IInstructionBreakpoint extends IBaseBreakpoint { readonly offset?: number; /** Original instruction memory address; display purposes only */ readonly address: bigint; - toJSON(): DebugProtocol.InstructionBreakpoint; + toDAP(): DebugProtocol.InstructionBreakpoint; } export interface IExceptionInfo { @@ -683,7 +697,8 @@ export interface IDebugModel extends ITreeElement { getInstructionBreakpoints(): ReadonlyArray; getWatchExpressions(): ReadonlyArray; - + registerBreakpointModes(debugType: string, modes: DebugProtocol.BreakpointMode[]): void; + getBreakpointModes(forBreakpointType: 'source' | 'exception' | 'data' | 'instruction'): DebugProtocol.BreakpointMode[]; onDidChangeBreakpoints: Event; onDidChangeCallStack: Event; onDidChangeWatchExpressions: Event; @@ -1112,7 +1127,7 @@ export interface IDebugService { /** * Adds a new function breakpoint for the given name. */ - addFunctionBreakpoint(name?: string, id?: string): void; + addFunctionBreakpoint(name?: string, id?: string, mode?: string): void; /** * Updates an already existing function breakpoint. @@ -1129,7 +1144,7 @@ export interface IDebugService { /** * Adds a new data breakpoint. */ - addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType): Promise; + addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType, mode: string | undefined): Promise; /** * Updates an already existing data breakpoint. @@ -1146,7 +1161,7 @@ export interface IDebugService { /** * Adds a new instruction breakpoint. */ - addInstructionBreakpoint(instructionReference: string, offset: number, address: bigint, condition?: string, hitCondition?: string): Promise; + addInstructionBreakpoint(opts: IInstructionBreakpointOptions): Promise; /** * Removes all instruction breakpoints. If address is passed only removes the instruction breakpoint with the passed address. @@ -1157,7 +1172,12 @@ export interface IDebugService { setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise; - setExceptionBreakpointsForSession(session: IDebugSession, data: DebugProtocol.ExceptionBreakpointsFilter[]): void; + /** + * Creates breakpoints based on the sesison filter options. This will create + * disabled breakpoints (or enabled, if the filter indicates it's a default) + * for each filter provided in the session. + */ + setExceptionBreakpointsForSession(session: IDebugSession, filters: DebugProtocol.ExceptionBreakpointsFilter[]): void; /** * Sends all breakpoints to the passed session. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 98618a40cf4..8098d1ce57b 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -817,22 +817,35 @@ function toBreakpointSessionData(data: DebugProtocol.Breakpoint, capabilities: D }, data); } +export interface IBaseBreakpointOptions { + enabled?: boolean; + hitCondition?: string; + condition?: string; + logMessage?: string; + mode?: string; + modeLabel?: string; +} + export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoint { private sessionData = new Map(); protected data: IBreakpointSessionData | undefined; + public hitCondition: string | undefined; + public condition: string | undefined; + public logMessage: string | undefined; + public mode: string | undefined; + public modeLabel: string | undefined; constructor( - enabled: boolean, - public hitCondition: string | undefined, - public condition: string | undefined, - public logMessage: string | undefined, - id: string + id: string, + opts: IBaseBreakpointOptions ) { - super(enabled, id); - if (enabled === undefined) { - this.enabled = true; - } + super(opts.enabled ?? true, id); + this.condition = opts.condition; + this.hitCondition = opts.hitCondition; + this.logMessage = opts.logMessage; + this.mode = opts.mode; + this.modeLabel = opts.modeLabel; } setSessionData(sessionId: string, data: IBreakpointSessionData | undefined): void { @@ -904,37 +917,59 @@ export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoi return undefined; } - toJSON(): any { - const result = Object.create(null); - result.id = this.getId(); - result.enabled = this.enabled; - result.condition = this.condition; - result.hitCondition = this.hitCondition; - result.logMessage = this.logMessage; - - return result; + toJSON(): IBaseBreakpointOptions & { id: string } { + return { + id: this.getId(), + enabled: this.enabled, + condition: this.condition, + hitCondition: this.hitCondition, + logMessage: this.logMessage, + mode: this.mode, + modeLabel: this.modeLabel, + }; } } +export interface IBreakpointOptions extends IBaseBreakpointOptions { + uri: uri; + lineNumber: number; + column: number | undefined; + adapterData: any; + triggeredBy: string | undefined; +} + export class Breakpoint extends BaseBreakpoint implements IBreakpoint { private sessionsDidTrigger?: Set; + private readonly _uri: uri; + private _adapterData: any; + private _lineNumber: number; + private _column: number | undefined; + public triggeredBy: string | undefined; constructor( - private readonly _uri: uri, - private _lineNumber: number, - private _column: number | undefined, - enabled: boolean, - condition: string | undefined, - hitCondition: string | undefined, - logMessage: string | undefined, - private _adapterData: any, + opts: IBreakpointOptions, private readonly textFileService: ITextFileService, private readonly uriIdentityService: IUriIdentityService, private readonly logService: ILogService, id = generateUuid(), - public triggeredBy: string | undefined = undefined ) { - super(enabled, hitCondition, condition, logMessage, id); + super(id, opts); + this._uri = opts.uri; + this._lineNumber = opts.lineNumber; + this._column = opts.column; + this._adapterData = opts.adapterData; + this.triggeredBy = opts.triggeredBy; + } + + toDAP(): DebugProtocol.SourceBreakpoint { + return { + line: this.sessionAgnosticData.lineNumber, + column: this.sessionAgnosticData.column, + condition: this.condition, + hitCondition: this.hitCondition, + logMessage: this.logMessage, + mode: this.mode + }; } get originalUri() { @@ -1019,14 +1054,15 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { } } - override toJSON(): any { - const result = super.toJSON(); - result.uri = this._uri; - result.lineNumber = this._lineNumber; - result.column = this._column; - result.adapterData = this.adapterData; - result.triggeredBy = this.triggeredBy; - return result; + override toJSON(): IBreakpointOptions & { id: string } { + return { + ...super.toJSON(), + uri: this._uri, + lineNumber: this._lineNumber, + column: this._column, + adapterData: this.adapterData, + triggeredBy: this.triggeredBy, + }; } override toString(): string { @@ -1058,6 +1094,10 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { if (data.hasOwnProperty('logMessage')) { this.logMessage = data.logMessage; } + if (data.hasOwnProperty('mode')) { + this.mode = data.mode; + this.modeLabel = data.modeLabel; + } if (data.hasOwnProperty('triggeredBy')) { this.triggeredBy = data.triggeredBy; this.sessionsDidTrigger = undefined; @@ -1065,24 +1105,34 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { } } +export interface IFunctionBreakpointOptions extends IBaseBreakpointOptions { + name: string; +} + export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreakpoint { + public name: string; constructor( - public name: string, - enabled: boolean, - hitCondition: string | undefined, - condition: string | undefined, - logMessage: string | undefined, + opts: IFunctionBreakpointOptions, id = generateUuid() ) { - super(enabled, hitCondition, condition, logMessage, id); + super(id, opts); + this.name = opts.name; } - override toJSON(): any { - const result = super.toJSON(); - result.name = this.name; + toDAP(): DebugProtocol.FunctionBreakpoint { + return { + name: this.name, + condition: this.condition, + hitCondition: this.hitCondition, + }; + } - return result; + override toJSON(): IFunctionBreakpointOptions & { id: string } { + return { + ...super.toJSON(), + name: this.name, + }; } get supported(): boolean { @@ -1098,30 +1148,51 @@ export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreak } } +export interface IDataBreakpointOptions extends IBaseBreakpointOptions { + description: string; + dataId: string; + canPersist: boolean; + accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined; + accessType: DebugProtocol.DataBreakpointAccessType; +} + export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { + public readonly description: string; + public readonly dataId: string; + public readonly canPersist: boolean; + public readonly accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined; + public readonly accessType: DebugProtocol.DataBreakpointAccessType; constructor( - public readonly description: string, - public readonly dataId: string, - public readonly canPersist: boolean, - enabled: boolean, - hitCondition: string | undefined, - condition: string | undefined, - logMessage: string | undefined, - public readonly accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, - public readonly accessType: DebugProtocol.DataBreakpointAccessType, + opts: IDataBreakpointOptions, id = generateUuid() ) { - super(enabled, hitCondition, condition, logMessage, id); + super(id, opts); + this.description = opts.description; + this.dataId = opts.dataId; + this.canPersist = opts.canPersist; + this.accessTypes = opts.accessTypes; + this.accessType = opts.accessType; } - override toJSON(): any { - const result = super.toJSON(); - result.description = this.description; - result.dataId = this.dataId; - result.accessTypes = this.accessTypes; - result.accessType = this.accessType; - return result; + toDAP(): DebugProtocol.DataBreakpoint { + return { + dataId: this.dataId, + accessType: this.accessType, + condition: this.condition, + hitCondition: this.hitCondition, + }; + } + + override toJSON(): IDataBreakpointOptions & { id: string } { + return { + ...super.toJSON(), + description: this.description, + dataId: this.dataId, + accessTypes: this.accessTypes, + accessType: this.accessType, + canPersist: this.canPersist, + }; } get supported(): boolean { @@ -1137,35 +1208,51 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { } } +export interface IExceptionBreakpointOptions extends IBaseBreakpointOptions { + filter: string; + label: string; + supportsCondition: boolean; + description: string | undefined; + conditionDescription: string | undefined; + fallback?: boolean; +} + export class ExceptionBreakpoint extends BaseBreakpoint implements IExceptionBreakpoint { private supportedSessions: Set = new Set(); + public readonly filter: string; + public readonly label: string; + public readonly supportsCondition: boolean; + public readonly description: string | undefined; + public readonly conditionDescription: string | undefined; + private fallback: boolean = false; + constructor( - public readonly filter: string, - public readonly label: string, - enabled: boolean, - public readonly supportsCondition: boolean, - condition: string | undefined, - public readonly description: string | undefined, - public readonly conditionDescription: string | undefined, - private fallback: boolean = false + opts: IExceptionBreakpointOptions, + id = generateUuid(), ) { - super(enabled, undefined, condition, undefined, generateUuid()); + super(id, opts); + this.filter = opts.filter; + this.label = opts.label; + this.supportsCondition = opts.supportsCondition; + this.description = opts.description; + this.conditionDescription = opts.conditionDescription; + this.fallback = opts.fallback || false; } - override toJSON(): any { - const result = Object.create(null); - result.filter = this.filter; - result.label = this.label; - result.enabled = this.enabled; - result.supportsCondition = this.supportsCondition; - result.conditionDescription = this.conditionDescription; - result.condition = this.condition; - result.fallback = this.fallback; - result.description = this.description; - - return result; + override toJSON(): IExceptionBreakpointOptions & { id: string } { + return { + ...super.toJSON(), + filter: this.filter, + label: this.label, + enabled: this.enabled, + supportsCondition: this.supportsCondition, + conditionDescription: this.conditionDescription, + condition: this.condition, + fallback: this.fallback, + description: this.description, + }; } setSupportedSession(sessionId: string, supported: boolean): void { @@ -1198,7 +1285,11 @@ export class ExceptionBreakpoint extends BaseBreakpoint implements IExceptionBre } matches(filter: DebugProtocol.ExceptionBreakpointsFilter) { - return this.filter === filter.filter && this.label === filter.label && this.supportsCondition === !!filter.supportsCondition && this.conditionDescription === filter.conditionDescription && this.description === filter.description; + return this.filter === filter.filter + && this.label === filter.label + && this.supportsCondition === !!filter.supportsCondition + && this.conditionDescription === filter.conditionDescription + && this.description === filter.description; } override toString(): string { @@ -1206,27 +1297,48 @@ export class ExceptionBreakpoint extends BaseBreakpoint implements IExceptionBre } } +export interface IInstructionBreakpointOptions extends IBaseBreakpointOptions { + instructionReference: string; + offset: number; + canPersist: boolean; + address: bigint; +} + export class InstructionBreakpoint extends BaseBreakpoint implements IInstructionBreakpoint { + public readonly instructionReference: string; + public readonly offset: number; + public readonly canPersist: boolean; + public readonly address: bigint; constructor( - public readonly instructionReference: string, - public readonly offset: number, - public readonly canPersist: boolean, - enabled: boolean, - hitCondition: string | undefined, - condition: string | undefined, - logMessage: string | undefined, - public readonly address: bigint, + opts: IInstructionBreakpointOptions, id = generateUuid() ) { - super(enabled, hitCondition, condition, logMessage, id); + super(id, opts); + this.instructionReference = opts.instructionReference; + this.offset = opts.offset; + this.canPersist = opts.canPersist; + this.address = opts.address; } - override toJSON(): DebugProtocol.InstructionBreakpoint { - const result = super.toJSON(); - result.instructionReference = this.instructionReference; - result.offset = this.offset; - return result; + toDAP(): DebugProtocol.InstructionBreakpoint { + return { + instructionReference: this.instructionReference, + condition: this.condition, + hitCondition: this.hitCondition, + mode: this.mode, + offset: this.offset, + }; + } + + override toJSON(): IInstructionBreakpointOptions & { id: string } { + return { + ...super.toJSON(), + instructionReference: this.instructionReference, + offset: this.offset, + canPersist: this.canPersist, + address: this.address, + }; } get supported(): boolean { @@ -1250,6 +1362,10 @@ export class ThreadAndSessionIds implements ITreeElement { } } +interface IBreakpointModeInternal extends DebugProtocol.BreakpointMode { + firstFromDebugType: string; +} + export class DebugModel extends Disposable implements IDebugModel { private sessions: IDebugSession[]; @@ -1258,6 +1374,7 @@ export class DebugModel extends Disposable implements IDebugModel { private readonly _onDidChangeBreakpoints = this._register(new Emitter()); private readonly _onDidChangeCallStack = this._register(new Emitter()); private readonly _onDidChangeWatchExpressions = this._register(new Emitter()); + private readonly _breakpointModes = new Map(); private breakpoints!: Breakpoint[]; private functionBreakpoints!: FunctionBreakpoint[]; private exceptionBreakpoints!: ExceptionBreakpoint[]; @@ -1492,24 +1609,33 @@ export class DebugModel extends Disposable implements IDebugModel { return this.instructionBreakpoints; } - setExceptionBreakpointsForSession(sessionId: string, data: DebugProtocol.ExceptionBreakpointsFilter[]): void { - if (data) { - let didChangeBreakpoints = false; - data.forEach(d => { - let ebp = this.exceptionBreakpoints.filter((exbp) => exbp.matches(d)).pop(); + setExceptionBreakpointsForSession(sessionId: string, filters: DebugProtocol.ExceptionBreakpointsFilter[]): void { + if (!filters) { + return; + } - if (!ebp) { - didChangeBreakpoints = true; - ebp = new ExceptionBreakpoint(d.filter, d.label, !!d.default, !!d.supportsCondition, undefined /* condition */, d.description, d.conditionDescription); - this.exceptionBreakpoints.push(ebp); - } + let didChangeBreakpoints = false; + filters.forEach((d) => { + let ebp = this.exceptionBreakpoints.filter((exbp) => exbp.matches(d)).pop(); - ebp.setSupportedSession(sessionId, true); - }); - - if (didChangeBreakpoints) { - this._onDidChangeBreakpoints.fire(undefined); + if (!ebp) { + didChangeBreakpoints = true; + ebp = new ExceptionBreakpoint({ + filter: d.filter, + label: d.label, + enabled: !!d.default, + supportsCondition: !!d.supportsCondition, + description: d.description, + conditionDescription: d.conditionDescription, + }); + this.exceptionBreakpoints.push(ebp); } + + ebp.setSupportedSession(sessionId, true); + }); + + if (didChangeBreakpoints) { + this._onDidChangeBreakpoints.fire(undefined); } } @@ -1539,7 +1665,19 @@ export class DebugModel extends Disposable implements IDebugModel { addBreakpoints(uri: uri, rawData: IBreakpointData[], fireEvent = true): IBreakpoint[] { const newBreakpoints = rawData.map(rawBp => { - return new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled === false ? false : true, rawBp.condition, rawBp.hitCondition, rawBp.logMessage, undefined, this.textFileService, this.uriIdentityService, this.logService, rawBp.id, rawBp.triggeredBy); + return new Breakpoint({ + uri, + lineNumber: rawBp.lineNumber, + column: rawBp.column, + enabled: rawBp.enabled ?? true, + condition: rawBp.condition, + hitCondition: rawBp.hitCondition, + logMessage: rawBp.logMessage, + triggeredBy: rawBp.triggeredBy, + adapterData: undefined, + mode: rawBp.mode, + modeLabel: rawBp.modeLabel, + }, this.textFileService, this.uriIdentityService, this.logService, rawBp.id); }); this.breakpoints = this.breakpoints.concat(newBreakpoints); this.breakpointsActivated = true; @@ -1635,6 +1773,37 @@ export class DebugModel extends Disposable implements IDebugModel { return undefined; } + getBreakpointModes(forBreakpointType: 'source' | 'exception' | 'data' | 'instruction'): DebugProtocol.BreakpointMode[] { + return [...this._breakpointModes.values()].filter(mode => mode.appliesTo.includes(forBreakpointType)); + } + + registerBreakpointModes(debugType: string, modes: DebugProtocol.BreakpointMode[]) { + for (const mode of modes) { + const key = `${mode.mode}/${mode.label}`; + const rec = this._breakpointModes.get(key); + if (rec) { + for (const target of mode.appliesTo) { + if (!rec.appliesTo.includes(target)) { + rec.appliesTo.push(target); + } + } + } else { + const duplicate = [...this._breakpointModes.values()].find(r => r !== rec && r.label === mode.label); + if (duplicate) { + duplicate.label = `${duplicate.label} (${duplicate.firstFromDebugType})`; + } + + this._breakpointModes.set(key, { + mode: mode.mode, + label: duplicate ? `${mode.label} (${debugType})` : mode.label, + firstFromDebugType: debugType, + description: mode.description, + appliesTo: mode.appliesTo.slice(), // avoid later mutations + }); + } + } + } + private sortAndDeDup(): void { this.breakpoints = this.breakpoints.sort((first, second) => { if (first.uri.toString() !== second.uri.toString()) { @@ -1703,8 +1872,8 @@ export class DebugModel extends Disposable implements IDebugModel { this._onDidChangeBreakpoints.fire({ changed: changed, sessionOnly: false }); } - addFunctionBreakpoint(functionName: string, id?: string): IFunctionBreakpoint { - const newFunctionBreakpoint = new FunctionBreakpoint(functionName, true, undefined, undefined, undefined, id); + addFunctionBreakpoint(functionName: string, id?: string, mode?: string): IFunctionBreakpoint { + const newFunctionBreakpoint = new FunctionBreakpoint({ name: functionName, mode }, id); this.functionBreakpoints.push(newFunctionBreakpoint); this._onDidChangeBreakpoints.fire({ added: [newFunctionBreakpoint], sessionOnly: false }); @@ -1739,8 +1908,8 @@ export class DebugModel extends Disposable implements IDebugModel { this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); } - addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType, id?: string): void { - const newDataBreakpoint = new DataBreakpoint(label, dataId, canPersist, true, undefined, undefined, undefined, accessTypes, accessType, id); + addDataBreakpoint(opts: IDataBreakpointOptions, id?: string): void { + const newDataBreakpoint = new DataBreakpoint(opts, id); this.dataBreakpoints.push(newDataBreakpoint); this._onDidChangeBreakpoints.fire({ added: [newDataBreakpoint], sessionOnly: false }); } @@ -1770,8 +1939,8 @@ export class DebugModel extends Disposable implements IDebugModel { this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); } - addInstructionBreakpoint(instructionReference: string, offset: number, address: bigint, condition?: string, hitCondition?: string): void { - const newInstructionBreakpoint = new InstructionBreakpoint(instructionReference, offset, false, true, hitCondition, condition, undefined, address); + addInstructionBreakpoint(opts: IInstructionBreakpointOptions): void { + const newInstructionBreakpoint = new InstructionBreakpoint(opts); this.instructionBreakpoints.push(newInstructionBreakpoint); this._onDidChangeBreakpoints.fire({ added: [newInstructionBreakpoint], sessionOnly: true }); } diff --git a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts index 07a0024f988..b00a4fd466a 100644 --- a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts @@ -52,10 +52,11 @@ declare module DebugProtocol { This raw error might be interpreted by the client and is not shown in the UI. Some predefined values exist. Values: - 'cancelled': request was cancelled. + 'cancelled': the request was cancelled. + 'notStopped': the request may be retried once the adapter is in a 'stopped' state. etc. */ - message?: 'cancelled' | string; + message?: 'cancelled' | 'notStopped' | string; /** Contains request result if success is true and error details if success is false. */ body?: any; } @@ -71,7 +72,8 @@ declare module DebugProtocol { /** Cancel request; value of command field is 'cancel'. The `cancel` request is used by the client in two situations: - to indicate that it is no longer interested in the result produced by a specific request issued earlier - - to cancel a progress sequence. Clients should only call this request if the corresponding capability `supportsCancelRequest` is true. + - to cancel a progress sequence. + Clients should only call this request if the corresponding capability `supportsCancelRequest` is true. This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honoring this request but there are no guarantees. The `cancel` request may return an error if it could not cancel an operation but a client should refrain from presenting this error to end users. The request that got cancelled still needs to send a response back. This can either be a normal result (`success` attribute true) or an error response (`success` attribute false and the `message` set to `cancelled`). @@ -230,7 +232,7 @@ declare module DebugProtocol { A non-empty `output` attribute is shown as the unindented end of the group. */ group?: 'start' | 'startCollapsed' | 'end'; - /** If an attribute `variablesReference` exists and its value is > 0, the output contains objects which can be retrieved by passing `variablesReference` to the `variables` request. The value should be less than or equal to 2147483647 (2^31-1). */ + /** If an attribute `variablesReference` exists and its value is > 0, the output contains objects which can be retrieved by passing `variablesReference` to the `variables` request as long as execution remains suspended. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference?: number; /** The source location where the output was produced. */ source?: Source; @@ -430,7 +432,7 @@ declare module DebugProtocol { /** Arguments for `runInTerminal` request. */ interface RunInTerminalRequestArguments { - /** What kind of terminal to launch. */ + /** What kind of terminal to launch. Defaults to `integrated` if not specified. */ kind?: 'integrated' | 'external'; /** Title of the terminal. */ title?: string; @@ -676,7 +678,7 @@ declare module DebugProtocol { /** Arguments for `breakpointLocations` request. */ interface BreakpointLocationsArguments { - /** The source location of the breakpoints; either `source.path` or `source.reference` must be specified. */ + /** The source location of the breakpoints; either `source.path` or `source.sourceReference` must be specified. */ source: Source; /** Start line of range to search possible breakpoint locations in. If only the line is specified, the request returns all possible locations in that line. */ line: number; @@ -763,8 +765,7 @@ declare module DebugProtocol { } /** SetExceptionBreakpoints request; value of command field is 'setExceptionBreakpoints'. - The request configures the debugger's response to thrown exceptions. - If an exception is configured to break, a `stopped` event is fired (with reason `exception`). + The request configures the debugger's response to thrown exceptions. Each of the `filters`, `filterOptions`, and `exceptionOptions` in the request are independent configurations to a debug adapter indicating a kind of exception to catch. An exception thrown in a program should result in a `stopped` event from the debug adapter (with reason `exception`) if any of the configured filters match. Clients should only call this request if the corresponding capability `exceptionBreakpointFilters` returns one or more filters. */ interface SetExceptionBreakpointsRequest extends Request { @@ -786,7 +787,7 @@ declare module DebugProtocol { /** Response to `setExceptionBreakpoints` request. The response contains an array of `Breakpoint` objects with information about each exception breakpoint or filter. The `Breakpoint` objects are in the same order as the elements of the `filters`, `filterOptions`, `exceptionOptions` arrays given as arguments. If both `filters` and `filterOptions` are given, the returned array must start with `filters` information first, followed by `filterOptions` information. - The `verified` property of a `Breakpoint` object signals whether the exception breakpoint or filter could be successfully created and whether the condition or hit count expressions are valid. In case of an error the `message` property explains the problem. The `id` property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events. + The `verified` property of a `Breakpoint` object signals whether the exception breakpoint or filter could be successfully created and whether the condition is valid. In case of an error the `message` property explains the problem. The `id` property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events. For backward compatibility both the `breakpoints` array and the enclosing `body` are optional. If these elements are missing a client is not able to show problems for individual exception breakpoints or filters. */ interface SetExceptionBreakpointsResponse extends Response { @@ -809,18 +810,22 @@ declare module DebugProtocol { /** Arguments for `dataBreakpointInfo` request. */ interface DataBreakpointInfoArguments { - /** Reference to the variable container if the data breakpoint is requested for a child of the container. */ + /** Reference to the variable container if the data breakpoint is requested for a child of the container. The `variablesReference` must have been obtained in the current suspended state. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference?: number; /** The name of the variable's child to obtain data breakpoint information for. If `variablesReference` isn't specified, this can be an expression. */ name: string; + /** When `name` is an expression, evaluate it in the scope of this stack frame. If not specified, the expression is evaluated in the global scope. When `variablesReference` is specified, this property has no effect. */ + frameId?: number; + /** The mode of the desired breakpoint. If defined, this must be one of the `breakpointModes` the debug adapter advertised in its `Capabilities`. */ + mode?: string; } /** Response to `dataBreakpointInfo` request. */ interface DataBreakpointInfoResponse extends Response { body: { - /** An identifier for the data on which a data breakpoint can be registered with the `setDataBreakpoints` request or null if no data breakpoint is available. */ + /** An identifier for the data on which a data breakpoint can be registered with the `setDataBreakpoints` request or null if no data breakpoint is available. If a `variablesReference` or `frameId` is passed, the `dataId` is valid in the current suspended state, otherwise it's valid indefinitely. See 'Lifetime of Object References' in the Overview section for details. Breakpoints set using the `dataId` in the `setDataBreakpoints` request may outlive the lifetime of the associated `dataId`. */ dataId: string | null; /** UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available. */ description: string; @@ -1032,7 +1037,7 @@ declare module DebugProtocol { } /** RestartFrame request; value of command field is 'restartFrame'. - The request restarts execution of the specified stackframe. + The request restarts execution of the specified stack frame. The debug adapter first sends the response and then a `stopped` event (with reason `restart`) after the restart has completed. Clients should only call this request if the corresponding capability `supportsRestartFrame` is true. */ @@ -1043,7 +1048,7 @@ declare module DebugProtocol { /** Arguments for `restartFrame` request. */ interface RestartFrameArguments { - /** Restart this stackframe. */ + /** Restart the stack frame identified by `frameId`. The `frameId` must have been obtained in the current suspended state. See 'Lifetime of Object References' in the Overview section for details. */ frameId: number; } @@ -1120,7 +1125,7 @@ declare module DebugProtocol { /** Response to `stackTrace` request. */ interface StackTraceResponse extends Response { body: { - /** The frames of the stackframe. If the array has length zero, there are no stackframes available. + /** The frames of the stack frame. If the array has length zero, there are no stack frames available. This means that there is no location information available. */ stackFrames: StackFrame[]; @@ -1130,7 +1135,7 @@ declare module DebugProtocol { } /** Scopes request; value of command field is 'scopes'. - The request returns the variable scopes for a given stackframe ID. + The request returns the variable scopes for a given stack frame ID. */ interface ScopesRequest extends Request { // command: 'scopes'; @@ -1139,14 +1144,14 @@ declare module DebugProtocol { /** Arguments for `scopes` request. */ interface ScopesArguments { - /** Retrieve the scopes for this stackframe. */ + /** Retrieve the scopes for the stack frame identified by `frameId`. The `frameId` must have been obtained in the current suspended state. See 'Lifetime of Object References' in the Overview section for details. */ frameId: number; } /** Response to `scopes` request. */ interface ScopesResponse extends Response { body: { - /** The scopes of the stackframe. If the array has length zero, there are no scopes available. */ + /** The scopes of the stack frame. If the array has length zero, there are no scopes available. */ scopes: Scope[]; }; } @@ -1162,13 +1167,17 @@ declare module DebugProtocol { /** Arguments for `variables` request. */ interface VariablesArguments { - /** The Variable reference. */ + /** The variable for which to retrieve its children. The `variablesReference` must have been obtained in the current suspended state. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference: number; /** Filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */ filter?: 'indexed' | 'named'; - /** The index of the first variable to return; if omitted children start at 0. */ + /** The index of the first variable to return; if omitted children start at 0. + The attribute is only honored by a debug adapter if the corresponding capability `supportsVariablePaging` is true. + */ start?: number; - /** The number of variables to return. If count is missing or 0, all variables are returned. */ + /** The number of variables to return. If count is missing or 0, all variables are returned. + The attribute is only honored by a debug adapter if the corresponding capability `supportsVariablePaging` is true. + */ count?: number; /** Specifies details on how to format the Variable values. The attribute is only honored by a debug adapter if the corresponding capability `supportsValueFormattingOptions` is true. @@ -1195,7 +1204,7 @@ declare module DebugProtocol { /** Arguments for `setVariable` request. */ interface SetVariableArguments { - /** The reference of the variable container. */ + /** The reference of the variable container. The `variablesReference` must have been obtained in the current suspended state. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference: number; /** The name of the variable in the container. */ name: string; @@ -1212,9 +1221,7 @@ declare module DebugProtocol { value: string; /** The type of the new value. Typically shown in the UI when hovering over the value. */ type?: string; - /** If `variablesReference` is > 0, the new value is structured and its children can be retrieved by passing `variablesReference` to the `variables` request. - The value should be less than or equal to 2147483647 (2^31-1). - */ + /** If `variablesReference` is > 0, the new value is structured and its children can be retrieved by passing `variablesReference` to the `variables` request as long as execution remains suspended. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference?: number; /** The number of named child variables. The client can use this information to present the variables in a paged UI and fetch them in chunks. @@ -1226,6 +1233,11 @@ declare module DebugProtocol { The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; + /** A memory reference to a location appropriate for this result. + For pointer type eval results, this is generally a reference to the memory address contained in the pointer. + This attribute may be returned by a debug adapter if corresponding capability `supportsMemoryReferences` is true. + */ + memoryReference?: string; }; } @@ -1356,16 +1368,16 @@ declare module DebugProtocol { frameId?: number; /** The context in which the evaluate request is used. Values: - 'variables': evaluate is called from a variables view context. 'watch': evaluate is called from a watch view context. 'repl': evaluate is called from a REPL context. 'hover': evaluate is called to generate the debug hover contents. This value should only be used if the corresponding capability `supportsEvaluateForHovers` is true. 'clipboard': evaluate is called to generate clipboard contents. This value should only be used if the corresponding capability `supportsClipboardContext` is true. + 'variables': evaluate is called from a variables view context. etc. */ - context?: 'variables' | 'watch' | 'repl' | 'hover' | 'clipboard' | string; + context?: 'watch' | 'repl' | 'hover' | 'clipboard' | 'variables' | string; /** Specifies details on how to format the result. The attribute is only honored by a debug adapter if the corresponding capability `supportsValueFormattingOptions` is true. */ @@ -1383,9 +1395,7 @@ declare module DebugProtocol { type?: string; /** Properties of an evaluate result that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; - /** If `variablesReference` is > 0, the evaluate result is structured and its children can be retrieved by passing `variablesReference` to the `variables` request. - The value should be less than or equal to 2147483647 (2^31-1). - */ + /** If `variablesReference` is > 0, the evaluate result is structured and its children can be retrieved by passing `variablesReference` to the `variables` request as long as execution remains suspended. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference: number; /** The number of named child variables. The client can use this information to present the variables in a paged UI and fetch them in chunks. @@ -1399,7 +1409,7 @@ declare module DebugProtocol { indexedVariables?: number; /** A memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. - This attribute should be returned by a debug adapter if corresponding capability `supportsMemoryReferences` is true. + This attribute may be returned by a debug adapter if corresponding capability `supportsMemoryReferences` is true. */ memoryReference?: string; }; @@ -1439,9 +1449,7 @@ declare module DebugProtocol { type?: string; /** Properties of a value that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; - /** If `variablesReference` is > 0, the value is structured and its children can be retrieved by passing `variablesReference` to the `variables` request. - The value should be less than or equal to 2147483647 (2^31-1). - */ + /** If `variablesReference` is > 0, the evaluate result is structured and its children can be retrieved by passing `variablesReference` to the `variables` request as long as execution remains suspended. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference?: number; /** The number of named child variables. The client can use this information to present the variables in a paged UI and fetch them in chunks. @@ -1453,6 +1461,11 @@ declare module DebugProtocol { The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; + /** A memory reference to a location appropriate for this result. + For pointer type eval results, this is generally a reference to the memory address contained in the pointer. + This attribute may be returned by a debug adapter if corresponding capability `supportsMemoryReferences` is true. + */ + memoryReference?: string; }; } @@ -1596,7 +1609,7 @@ declare module DebugProtocol { This can be used to determine the number of bytes that should be skipped before a subsequent `readMemory` request succeeds. */ unreadableBytes?: number; - /** The bytes read from memory, encoded using base64. */ + /** The bytes read from memory, encoded using base64. If the decoded length of `data` is less than the requested `count` in the original `readMemory` request, and `unreadableBytes` is zero or omitted, then the client should assume it's reached the end of readable memory. */ data?: string; }; } @@ -1667,6 +1680,42 @@ declare module DebugProtocol { }; } + /** DataAddressBreakpointInfo request; value of command field is 'DataAddressBreakpointInfo'. + Obtains information on a possible data breakpoint that could be set on a memory address or memory address range. + + Clients should only call this request if the corresponding capability `supportsDataAddressInfo` is true. + */ + interface DataAddressBreakpointInfoRequest extends Request { + // command: 'DataAddressBreakpointInfo'; + arguments: DataAddressBreakpointInfoArguments; + } + + /** Arguments for `dataAddressBreakpointInfo` request. */ + interface DataAddressBreakpointInfoArguments { + /** The address of the data for which to obtain breakpoint information. + Treated as a hex value if prefixed with `0x`, or as a decimal value otherwise. + */ + address?: string; + /** If passed, requests breakpoint information for an exclusive byte range rather than a single address. The range extends the given number of `bytes` from the start `address`. + Treated as a hex value if prefixed with `0x`, or as a decimal value otherwise. + */ + bytes?: string; + } + + /** Response to `dataAddressBreakpointInfo` request. */ + interface DataAddressBreakpointInfoResponse extends Response { + body: { + /** An identifier for the data on which a data breakpoint can be registered with the `setDataBreakpoints` request or null if no data breakpoint is available. If a `variablesReference` or `frameId` is passed, the `dataId` is valid in the current suspended state, otherwise it's valid indefinitely. See 'Lifetime of Object References' in the Overview section for details. Breakpoints set using the `dataId` in the `setDataBreakpoints` request may outlive the lifetime of the associated `dataId`. */ + dataId: string | null; + /** UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available. */ + description: string; + /** Attribute lists the available access types for a potential data breakpoint. A UI client could surface this information. */ + accessTypes?: DataBreakpointAccessType[]; + /** Attribute indicates that a potential data breakpoint could be persisted across sessions. */ + canPersist?: boolean; + }; + } + /** Information about the capabilities of a debug adapter. */ interface Capabilities { /** The debug adapter supports the `configurationDone` request. */ @@ -1739,6 +1788,8 @@ declare module DebugProtocol { supportsBreakpointLocationsRequest?: boolean; /** The debug adapter supports the `clipboard` context value in the `evaluate` request. */ supportsClipboardContext?: boolean; + /** The debug adapter supports the `dataAddressBreakpointInfo` request. */ + supportsDataAddressInfo?: boolean; /** The debug adapter supports stepping granularities (argument `granularity`) for the stepping requests. */ supportsSteppingGranularity?: boolean; /** The debug adapter supports adding breakpoints based on instruction references. */ @@ -1747,6 +1798,11 @@ declare module DebugProtocol { supportsExceptionFilterOptions?: boolean; /** The debug adapter supports the `singleThread` property on the execution requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, `stepBack`). */ supportsSingleThreadExecutionRequests?: boolean; + /** Modes of breakpoints supported by the debug adapter, such as 'hardware' or 'software'. If present, the client may allow the user to select a mode and include it in its `setBreakpoints` request. + + Clients may present the first applicable mode in this array as the 'default' mode in gestures that set breakpoints. + */ + breakpointModes?: BreakpointMode[]; } /** An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for configuring how exceptions are dealt with. */ @@ -1767,7 +1823,7 @@ declare module DebugProtocol { /** A structured message object. Used to return errors from requests. */ interface Message { - /** Unique identifier for the message. */ + /** Unique (within a debug adapter implementation) identifier for the message. The purpose of these error IDs is to help extension authors that have the requirement that every user visible error message needs a corresponding error number, so that users or customer support can find information about the specific error more easily. */ id: number; /** A format string for the message. Embedded variables have the form `{name}`. If variable name starts with an underscore character, the variable does not contain user data (PII) and can be safely used for telemetry purposes. @@ -1833,13 +1889,6 @@ declare module DebugProtocol { width?: number; } - /** The ModulesViewDescriptor is the container for all declarative configuration options of a module view. - For now it only specifies the columns to be shown in the modules view. - */ - interface ModulesViewDescriptor { - columns: ColumnDescriptor[]; - } - /** A Thread */ interface Thread { /** Unique identifier for the thread. */ @@ -1884,7 +1933,7 @@ declare module DebugProtocol { /** A Stackframe contains the source location. */ interface StackFrame { /** An identifier for the stack frame. It must be unique across all threads. - This id can be used to retrieve the scopes of the frame with the `scopes` request or to restart the execution of a stackframe. + This id can be used to retrieve the scopes of the frame with the `scopes` request or to restart the execution of a stack frame. */ id: number; /** The name of the stack frame, typically a method name. */ @@ -1899,7 +1948,7 @@ declare module DebugProtocol { endLine?: number; /** End position of the range covered by the stack frame. It is measured in UTF-16 code units and the client capability `columnsStartAt1` determines whether it is 0- or 1-based. */ endColumn?: number; - /** Indicates whether this frame can be restarted with the `restart` request. Clients should only use this if the debug adapter supports the `restart` request and the corresponding capability `supportsRestartRequest` is true. */ + /** Indicates whether this frame can be restarted with the `restart` request. Clients should only use this if the debug adapter supports the `restart` request and the corresponding capability `supportsRestartRequest` is true. If a debug adapter has this capability, then `canRestart` defaults to `true` if the property is absent. */ canRestart?: boolean; /** A memory reference for the current instruction pointer in this frame. */ instructionPointerReference?: string; @@ -1923,7 +1972,7 @@ declare module DebugProtocol { etc. */ presentationHint?: 'arguments' | 'locals' | 'registers' | string; - /** The variables of this scope can be retrieved by passing the value of `variablesReference` to the `variables` request. */ + /** The variables of this scope can be retrieved by passing the value of `variablesReference` to the `variables` request as long as execution remains suspended. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference: number; /** The number of named variables in this scope. The client can use this information to present the variables in a paged UI and fetch them in chunks. @@ -1971,7 +2020,7 @@ declare module DebugProtocol { presentationHint?: VariablePresentationHint; /** The evaluatable name of this variable which can be passed to the `evaluate` request to fetch the variable's value. */ evaluateName?: string; - /** If `variablesReference` is > 0, the variable is structured and its children can be retrieved by passing `variablesReference` to the `variables` request. */ + /** If `variablesReference` is > 0, the variable is structured and its children can be retrieved by passing `variablesReference` to the `variables` request as long as execution remains suspended. See 'Lifetime of Object References' in the Overview section for details. */ variablesReference: number; /** The number of named child variables. The client can use this information to present the children in a paged UI and fetch them in chunks. @@ -1981,8 +2030,10 @@ declare module DebugProtocol { The client can use this information to present the children in a paged UI and fetch them in chunks. */ indexedVariables?: number; - /** The memory reference for the variable if the variable represents executable code, such as a function pointer. - This attribute is only required if the corresponding capability `supportsMemoryReferences` is true. + /** A memory reference associated with this variable. + For pointer type variables, this is generally a reference to the memory address contained in the pointer. + For executable data, this reference may later be used in a `disassemble` request. + This attribute may be returned by a debug adapter if corresponding capability `supportsMemoryReferences` is true. */ memoryReference?: string; } @@ -2011,8 +2062,8 @@ declare module DebugProtocol { 'constant': Indicates that the object is a constant. 'readOnly': Indicates that the object is read only. 'rawString': Indicates that the object is a raw string. - 'hasObjectId': Indicates that the object can have an Object ID created for it. - 'canHaveObjectId': Indicates that the object has an Object ID associated with it. + 'hasObjectId': Indicates that the object can have an Object ID created for it. This is a vestigial attribute that is used by some clients; 'Object ID's are not specified in the protocol. + 'canHaveObjectId': Indicates that the object has an Object ID associated with it. This is a vestigial attribute that is used by some clients; 'Object ID's are not specified in the protocol. 'hasSideEffects': Indicates that the evaluation had side effects. 'hasDataBreakpoint': Indicates that the object has its value tracked by a data breakpoint. etc. @@ -2054,13 +2105,17 @@ declare module DebugProtocol { /** The expression that controls how many hits of the breakpoint are ignored. The debug adapter is expected to interpret the expression as needed. The attribute is only honored by a debug adapter if the corresponding capability `supportsHitConditionalBreakpoints` is true. + If both this property and `condition` are specified, `hitCondition` should be evaluated only if the `condition` is met, and the debug adapter should stop only if both conditions are met. */ hitCondition?: string; /** If this attribute exists and is non-empty, the debug adapter must not 'break' (stop) but log the message instead. Expressions within `{}` are interpolated. The attribute is only honored by a debug adapter if the corresponding capability `supportsLogPoints` is true. + If either `hitCondition` or `condition` is specified, then the message should only be logged if those conditions are met. */ logMessage?: string; + /** The mode of this breakpoint. If defined, this must be one of the `breakpointModes` the debug adapter advertised in its `Capabilities`. */ + mode?: string; } /** Properties of a breakpoint passed to the `setFunctionBreakpoints` request. */ @@ -2101,7 +2156,7 @@ declare module DebugProtocol { This should be a memory or instruction pointer reference from an `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`. */ instructionReference: string; - /** The offset from the instruction reference. + /** The offset from the instruction reference in bytes. This can be negative. */ offset?: number; @@ -2114,6 +2169,8 @@ declare module DebugProtocol { The attribute is only honored by a debug adapter if the corresponding capability `supportsHitConditionalBreakpoints` is true. */ hitCondition?: string; + /** The mode of this breakpoint. If defined, this must be one of the `breakpointModes` the debug adapter advertised in its `Capabilities`. */ + mode?: string; } /** Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests. */ @@ -2144,6 +2201,12 @@ declare module DebugProtocol { This can be negative. */ offset?: number; + /** A machine-readable explanation of why a breakpoint may not be verified. If a breakpoint is verified or a specific reason is not known, the adapter should omit this property. Possible values include: + + - `pending`: Indicates a breakpoint might be verified in the future, but the adapter cannot verify it in the current state. + - `failed`: Indicates a breakpoint was not able to be verified, and the adapter does not believe it can be verified without intervention. + */ + reason?: 'pending' | 'failed'; } /** The granularity of one 'step' in the stepping requests `next`, `stepIn`, `stepOut`, and `stepBack`. @@ -2259,6 +2322,8 @@ declare module DebugProtocol { The exception breaks into the debugger if the result of the condition is true. */ condition?: string; + /** The mode of this exception breakpoint. If defined, this must be one of the `breakpointModes` the debug adapter advertised in its `Capabilities`. */ + mode?: string; } /** An `ExceptionOptions` assigns configuration options to a set of exceptions. */ @@ -2328,6 +2393,11 @@ declare module DebugProtocol { endLine?: number; /** The end column of the range that corresponds to this instruction, if any. */ endColumn?: number; + /** A hint for how to present the instruction in the UI. + + A value of `invalid` may be used to indicate this instruction is 'filler' and cannot be reached by the program. For example, unreadable memory addresses may be presented is 'invalid.' + */ + presentationHint?: 'normal' | 'invalid'; } /** Logical areas that can be invalidated by the `invalidated` event. @@ -2339,5 +2409,27 @@ declare module DebugProtocol { etc. */ type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables' | string; + + /** A `BreakpointMode` is provided as a option when setting breakpoints on sources or instructions. */ + interface BreakpointMode { + /** The internal ID of the mode. This value is passed to the `setBreakpoints` request. */ + mode: string; + /** The name of the breakpoint mode. This is shown in the UI. */ + label: string; + /** A help text providing additional information about the breakpoint mode. This string is typically shown as a hover and can be translated. */ + description?: string; + /** Describes one or more type of breakpoint this mode applies to. */ + appliesTo: BreakpointModeApplicability[]; + } + + /** Describes one or more type of breakpoint a `BreakpointMode` applies to. This is a non-exhaustive enumeration and may expand as future breakpoint types are added. + Values: + 'source': In `SourceBreakpoint`s + 'exception': In exception breakpoints applied in the `ExceptionFilterOptions` + 'data': In data breakpoints requested in the the `DataBreakpointInfo` request + 'instruction': In `InstructionBreakpoint`s + etc. + */ + type BreakpointModeApplicability = 'source' | 'exception' | 'data' | 'instruction' | string; } diff --git a/src/vs/workbench/contrib/debug/common/debugStorage.ts b/src/vs/workbench/contrib/debug/common/debugStorage.ts index dd267f90358..b7e57fff654 100644 --- a/src/vs/workbench/contrib/debug/common/debugStorage.ts +++ b/src/vs/workbench/contrib/debug/common/debugStorage.ts @@ -65,8 +65,9 @@ export class DebugStorage extends Disposable { private loadBreakpoints(): Breakpoint[] { let result: Breakpoint[] | undefined; try { - result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { - return new Breakpoint(URI.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.logMessage, breakpoint.adapterData, this.textFileService, this.uriIdentityService, this.logService, breakpoint.id, breakpoint.triggeredBy); + result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: ReturnType) => { + breakpoint.uri = URI.revive(breakpoint.uri); + return new Breakpoint(breakpoint, this.textFileService, this.uriIdentityService, this.logService, breakpoint.id); }); } catch (e) { } @@ -76,8 +77,8 @@ export class DebugStorage extends Disposable { private loadFunctionBreakpoints(): FunctionBreakpoint[] { let result: FunctionBreakpoint[] | undefined; try { - result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => { - return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition, fb.condition, fb.logMessage, fb.id); + result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: ReturnType) => { + return new FunctionBreakpoint(fb, fb.id); }); } catch (e) { } @@ -87,8 +88,8 @@ export class DebugStorage extends Disposable { private loadExceptionBreakpoints(): ExceptionBreakpoint[] { let result: ExceptionBreakpoint[] | undefined; try { - result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => { - return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled, exBreakpoint.supportsCondition, exBreakpoint.condition, exBreakpoint.description, exBreakpoint.conditionDescription, !!exBreakpoint.fallback); + result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: ReturnType) => { + return new ExceptionBreakpoint(exBreakpoint, exBreakpoint.id); }); } catch (e) { } @@ -98,8 +99,8 @@ export class DebugStorage extends Disposable { private loadDataBreakpoints(): DataBreakpoint[] { let result: DataBreakpoint[] | undefined; try { - result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: any) => { - return new DataBreakpoint(dbp.description, dbp.dataId, true, dbp.enabled, dbp.hitCondition, dbp.condition, dbp.logMessage, dbp.accessTypes, dbp.accessType, dbp.id); + result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: ReturnType) => { + return new DataBreakpoint(dbp, dbp.id); }); } catch (e) { } diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index 1b7bc10db56..b85e544f9bb 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -291,7 +291,7 @@ suite('Debug - Breakpoints', () => { let eventCount = 0; disposables.add(model.onDidChangeBreakpoints(() => eventCount++)); //address: string, offset: number, condition?: string, hitCondition?: string - model.addInstructionBreakpoint('0xCCCCFFFF', 0, 0n); + model.addInstructionBreakpoint({ instructionReference: '0xCCCCFFFF', offset: 0, address: 0n, canPersist: false }); assert.strictEqual(eventCount, 1); let instructionBreakpoints = model.getInstructionBreakpoints(); @@ -299,7 +299,7 @@ suite('Debug - Breakpoints', () => { assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF'); assert.strictEqual(instructionBreakpoints[0].offset, 0); - model.addInstructionBreakpoint('0xCCCCEEEE', 1, 0n); + model.addInstructionBreakpoint({ instructionReference: '0xCCCCEEEE', offset: 1, address: 0n, canPersist: false }); assert.strictEqual(eventCount, 2); instructionBreakpoints = model.getInstructionBreakpoints(); assert.strictEqual(instructionBreakpoints.length, 2); @@ -313,8 +313,8 @@ suite('Debug - Breakpoints', () => { let eventCount = 0; disposables.add(model.onDidChangeBreakpoints(() => eventCount++)); - model.addDataBreakpoint('label', 'id', true, ['read'], 'read', '1'); - model.addDataBreakpoint('second', 'secondId', false, ['readWrite'], 'readWrite', '2'); + model.addDataBreakpoint({ description: 'label', dataId: 'id', canPersist: true, accessTypes: ['read'], accessType: 'read' }, '1'); + model.addDataBreakpoint({ description: 'second', dataId: 'secondId', canPersist: false, accessTypes: ['readWrite'], accessType: 'readWrite' }, '2'); model.updateDataBreakpoint('1', { condition: 'aCondition' }); model.updateDataBreakpoint('2', { hitCondition: '10' }); const dataBreakpoints = model.getDataBreakpoints(); @@ -374,7 +374,7 @@ suite('Debug - Breakpoints', () => { assert.strictEqual(result.message, 'Disabled Logpoint'); assert.strictEqual(result.icon.id, 'debug-breakpoint-log-disabled'); - model.addDataBreakpoint('label', 'id', true, ['read'], 'read'); + model.addDataBreakpoint({ description: 'label', canPersist: true, accessTypes: ['read'], accessType: 'read', dataId: 'id' }); const dataBreakpoints = model.getDataBreakpoints(); result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0], ls, model); assert.strictEqual(result.message, 'Data Breakpoint'); diff --git a/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts index 96e097d1205..26c5549841b 100644 --- a/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts @@ -18,7 +18,7 @@ suite('DebugModel', () => { suite('FunctionBreakpoint', () => { test('Id is saved', () => { - const fbp = new FunctionBreakpoint('function', true, 'hit condition', 'condition', 'log message'); + const fbp = new FunctionBreakpoint({ name: 'function', enabled: true, hitCondition: 'hit condition', condition: 'condition', logMessage: 'log message' }); const strigified = JSON.stringify(fbp); const parsed = JSON.parse(strigified); assert.equal(parsed.id, fbp.getId()); @@ -27,10 +27,17 @@ suite('DebugModel', () => { suite('ExceptionBreakpoint', () => { test('Restored matches new', () => { - const ebp = new ExceptionBreakpoint('id', 'label', true, true, 'condition', 'description', 'condition description', false); + const ebp = new ExceptionBreakpoint({ + conditionDescription: 'condition description', + description: 'description', + filter: 'condition', + label: 'label', + supportsCondition: true, + enabled: true, + }, 'id'); const strigified = JSON.stringify(ebp); const parsed = JSON.parse(strigified); - const newEbp = new ExceptionBreakpoint(parsed.filter, parsed.label, parsed.enabled, parsed.supportsCondition, parsed.condition, parsed.description, parsed.conditionDescription, !!parsed.fallback); + const newEbp = new ExceptionBreakpoint(parsed); assert.ok(ebp.matches(newEbp)); }); }); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 5dca0f132e5..617f46d449f 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -15,6 +15,7 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { AdapterEndEvent, IAdapterManager, IBreakpoint, IBreakpointData, IBreakpointUpdateData, IConfig, IConfigurationManager, IDataBreakpoint, IDebugModel, IDebugService, IDebugSession, IDebugSessionOptions, IDebugger, IExceptionBreakpoint, IExceptionInfo, IFunctionBreakpoint, IInstructionBreakpoint, ILaunch, IMemoryRegion, INewReplElementData, IRawModelUpdate, IRawStoppedDetails, IReplElement, IStackFrame, IThread, IViewModel, LoadedSourceEvent, State } from 'vs/workbench/contrib/debug/common/debug'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; +import { IInstructionBreakpointOptions } from 'vs/workbench/contrib/debug/common/debugModel'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; @@ -85,7 +86,7 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } - addInstructionBreakpoint(instructionReference: string, offset: number, address: bigint, condition?: string, hitCondition?: string): Promise { + addInstructionBreakpoint(opts: IInstructionBreakpointOptions): Promise { throw new Error('Method not implemented.'); }