Merge pull request #125737 from xisui-MSFT/dev/xisui/disassembly

Disassembly view
This commit is contained in:
Andre Weinand 2021-07-22 11:28:41 +02:00 committed by GitHub
commit 4f5f5de28f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1159 additions and 81 deletions

View file

@ -5,7 +5,7 @@
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI as uri, UriComponents } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto, IStartDebuggingOptions, IDebugConfiguration
@ -339,7 +339,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return undefined;
}
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint>)): Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto> {
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>)): Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto> {
return bps.map(bp => {
if ('name' in bp) {
const fbp = <IFunctionBreakpoint>bp;

View file

@ -6,8 +6,8 @@
import * as resources from 'vs/base/common/resources';
import * as dom from 'vs/base/browser/dom';
import { Action, IAction } from 'vs/base/common/actions';
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugModel, IDataBreakpoint, BREAKPOINTS_VIEW_ID, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, IBaseBreakpoint, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_INPUT_FOCUSED } from 'vs/workbench/contrib/debug/common/debug';
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugModel, IDataBreakpoint, BREAKPOINTS_VIEW_ID, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, IBaseBreakpoint, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_INPUT_FOCUSED, IInstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint, InstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@ -43,6 +43,8 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Codicon } from 'vs/base/common/codicons';
import { equals } from 'vs/base/common/arrays';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
import { DisassemblyView } from 'vs/workbench/contrib/debug/browser/disassemblyView';
const $ = dom.$;
@ -57,10 +59,10 @@ function createCheckbox(): HTMLInputElement {
const MAX_VISIBLE_BREAKPOINTS = 9;
export function getExpandedBodySize(model: IDebugModel, countLimit: number): number {
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length;
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length + model.getInstructionBreakpoints().length;
return Math.min(countLimit, length) * 22;
}
type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint;
type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint | IInstructionBreakpoint;
interface InputBoxData {
breakpoint: IFunctionBreakpoint | IExceptionBreakpoint;
@ -120,7 +122,8 @@ export class BreakpointsView extends ViewPane {
new ExceptionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService),
this.instantiationService.createInstance(FunctionBreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType),
this.instantiationService.createInstance(DataBreakpointsRenderer),
new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService, this.labelService)
new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService, this.labelService),
this.instantiationService.createInstance(InstructionBreakpointsRenderer),
], {
identityProvider: { getId: (element: IEnablement) => element.getId() },
multipleSelectionSupport: false,
@ -142,6 +145,8 @@ export class BreakpointsView extends ViewPane {
await this.debugService.removeFunctionBreakpoints(element.getId());
} else if (element instanceof DataBreakpoint) {
await this.debugService.removeDataBreakpoints(element.getId());
} else if (element instanceof InstructionBreakpoint) {
await this.debugService.removeInstructionBreakpoints(element.instructionReference);
}
});
@ -157,6 +162,10 @@ export class BreakpointsView extends ViewPane {
if (e.element instanceof Breakpoint) {
openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService);
}
if (e.element instanceof InstructionBreakpoint) {
const disassemblyView = await this.editorService.openEditor(DisassemblyViewInput.instance);
(disassemblyView as DisassemblyView).goToAddress(e.element.instructionReference);
}
if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.inputBoxData?.breakpoint) {
// double click
this.renderInputBox({ breakpoint: e.element, type: 'name' });
@ -214,7 +223,8 @@ export class BreakpointsView extends ViewPane {
private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
const element = e.element;
const type = element instanceof Breakpoint ? 'breakpoint' : element instanceof ExceptionBreakpoint ? 'exceptionBreakpoint' :
element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' : undefined;
element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' :
element instanceof InstructionBreakpoint ? 'instructionBreakpoint' : undefined;
this.breakpointItemType.set(type);
const session = this.debugService.getViewModel().focusedSession;
const conditionSupported = element instanceof ExceptionBreakpoint ? element.supportsCondition : (!session || !!session.capabilities.supportsConditionalBreakpoints);
@ -288,7 +298,7 @@ export class BreakpointsView extends ViewPane {
private get elements(): BreakpointItem[] {
const model = this.debugService.getModel();
const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints());
const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()).concat(model.getInstructionBreakpoints());
return elements as BreakpointItem[];
}
@ -326,6 +336,9 @@ class BreakpointsDelegate implements IListVirtualDelegate<BreakpointItem> {
if (element instanceof DataBreakpoint) {
return DataBreakpointsRenderer.ID;
}
if (element instanceof InstructionBreakpoint) {
return InstructionBreakpointsRenderer.ID;
}
return '';
}
@ -362,6 +375,10 @@ interface IDataBreakpointTemplateData extends IBaseBreakpointWithIconTemplateDat
accessType: HTMLElement;
}
interface IInstructionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
address: HTMLElement;
}
interface IFunctionBreakpointInputTemplateData {
inputBox: InputBox;
checkbox: HTMLInputElement;
@ -673,6 +690,71 @@ class DataBreakpointsRenderer implements IListRenderer<DataBreakpoint, IDataBrea
}
}
class InstructionBreakpointsRenderer implements IListRenderer<IInstructionBreakpoint, IInstructionBreakpointTemplateData> {
constructor(
@IDebugService private readonly debugService: IDebugService,
@ILabelService private readonly labelService: ILabelService
) {
// noop
}
static readonly ID = 'instructionBreakpoints';
get templateId() {
return InstructionBreakpointsRenderer.ID;
}
renderTemplate(container: HTMLElement): IInstructionBreakpointTemplateData {
const data: IInstructionBreakpointTemplateData = Object.create(null);
data.breakpoint = dom.append(container, $('.breakpoint'));
data.icon = $('.icon');
data.checkbox = createCheckbox();
data.toDispose = [];
data.elementDisposable = [];
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
}));
dom.append(data.breakpoint, data.icon);
dom.append(data.breakpoint, data.checkbox);
data.name = dom.append(data.breakpoint, $('span.name'));
data.address = dom.append(data.breakpoint, $('span.file-path'));
data.actionBar = new ActionBar(data.breakpoint);
data.toDispose.push(data.actionBar);
return data;
}
renderElement(breakpoint: IInstructionBreakpoint, index: number, data: IInstructionBreakpointTemplateData): void {
data.context = breakpoint;
data.breakpoint.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());
data.name.textContent = breakpoint.instructionReference;
data.checkbox.checked = breakpoint.enabled;
const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService);
data.icon.className = ThemeIcon.asClassName(icon);
data.breakpoint.title = breakpoint.message || message || '';
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
if (debugActive && !breakpoint.verified) {
data.breakpoint.classList.add('disabled');
}
}
disposeElement(_element: IInstructionBreakpoint, _index: number, templateData: IInstructionBreakpointTemplateData): void {
dispose(templateData.elementDisposable);
}
disposeTemplate(templateData: IInstructionBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
}
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IFunctionBreakpointInputTemplateData> {
constructor(
@ -927,7 +1009,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
}
export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, icon: ThemeIcon } {
export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: BreakpointItem, labelService?: ILabelService): { message?: string, icon: ThemeIcon } {
const debugActive = state === State.Running || state === State.Stopped;
const breakpointIcon = breakpoint instanceof DataBreakpoint ? icons.dataBreakpoint : breakpoint instanceof FunctionBreakpoint ? icons.functionBreakpoint : breakpoint.logMessage ? icons.logBreakpoint : icons.breakpoint;
@ -985,6 +1067,32 @@ export function getBreakpointMessageAndIcon(state: State, breakpointsActivated:
};
}
if (breakpoint instanceof InstructionBreakpoint) {
if (!breakpoint.supported) {
return {
icon: breakpointIcon.unverified,
message: localize('instructionBreakpointUnsupported', "Instruction breakpoints not supported by this debug type"),
};
}
const messages: string[] = [];
if (breakpoint.message) {
messages.push(breakpoint.message);
} else if (breakpoint.instructionReference) {
messages.push(localize('instructionBreakpointAtAddress', "Instruction breakpoint at address {0}", breakpoint.instructionReference));
} else {
messages.push(localize('instructionBreakpoint', "Instruction breakpoint"));
}
if (breakpoint.hitCondition) {
messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
}
return {
icon: breakpointIcon.regular,
message: appendMessage(messages.join('\n'))
};
}
if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) {
const messages: string[] = [];
@ -1099,6 +1207,8 @@ registerAction2(class extends Action2 {
await debugService.removeFunctionBreakpoints(breakpoint.getId());
} else if (breakpoint instanceof DataBreakpoint) {
await debugService.removeDataBreakpoints(breakpoint.getId());
} else if (breakpoint instanceof InstructionBreakpoint) {
await debugService.removeInstructionBreakpoints(breakpoint.instructionReference);
}
}
});

View file

@ -18,8 +18,8 @@ import { distinct } from 'vs/base/common/arrays';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons';
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
export const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
export const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.

View file

@ -16,7 +16,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import {
IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, DISASSEMBLY_VIEW_ID,
} from 'vs/workbench/contrib/debug/common/debug';
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService';
@ -50,6 +50,10 @@ import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors';
import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution';
import { FileAccess } from 'vs/base/common/network';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
import { EditorExtensions } from 'vs/workbench/common/editor';
import { DisassemblyView, DisassemblyViewContribution } from 'vs/workbench/contrib/debug/browser/disassemblyView';
import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
const debugCategory = nls.localize('debugCategory', "Debug");
registerColors();
@ -64,6 +68,7 @@ if (isWeb) {
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisassemblyViewContribution, LifecyclePhase.Eventually);
// Register Quick Access
Registry.as<IQuickAccessRegistry>(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({
@ -373,6 +378,13 @@ viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('brea
viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
// Register disassembly view
Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane(
EditorPaneDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")),
[new SyncDescriptor(DisassemblyViewInput)]
);
// Register configuration
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({

View file

@ -8,7 +8,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IListService } from 'vs/platform/list/browser/listService';
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@ -16,7 +16,7 @@ import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys';
@ -160,7 +160,12 @@ CommandsRegistry.registerCommand({
CommandsRegistry.registerCommand({
id: STEP_BACK_ID,
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, thread => thread.stepBack());
const contextKeyService = accessor.get(IContextKeyService);
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepBack('instruction'));
} else {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepBack());
}
}
});
@ -256,8 +261,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
primary: isWeb ? (KeyMod.Alt | KeyCode.F10) : KeyCode.F10, // Browsers do not allow F10 to be binded so we have to bind an alternative
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const contextKeyService = accessor.get(IContextKeyService);
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction'));
} else {
getThreadAndRun(accessor, context, (thread: IThread) => thread.next());
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -267,8 +277,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
// Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times
when: CONTEXT_DEBUG_STATE.notEqualsTo('inactive'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const contextKeyService = accessor.get(IContextKeyService);
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn('instruction'));
} else {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn());
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -277,8 +292,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
primary: KeyMod.Shift | KeyCode.F11,
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const contextKeyService = accessor.get(IContextKeyService);
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut('instruction'));
} else {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut());
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({

View file

@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, EditorAction, IActionOptions, EditorAction2 } from 'vs/editor/browser/editorExtensions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_EXCEPTION_WIDGET_VISIBLE, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST } from 'vs/workbench/contrib/debug/common/debug';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView';
@ -26,6 +26,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { raceTimeout } from 'vs/base/common/async';
import { registerAction2, MenuId } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
class ToggleBreakpointAction extends EditorAction2 {
constructor() {
@ -53,6 +54,7 @@ class ToggleBreakpointAction extends EditorAction2 {
}
async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise<void> {
// TODO: add disassembly F9
if (editor.hasModel()) {
const debugService = accessor.get(IDebugService);
const modelUri = editor.getModel().uri;
@ -133,6 +135,32 @@ class LogPointAction extends EditorAction2 {
}
}
class OpenDisassemblyViewAction extends EditorAction {
public static readonly ID = 'editor.debug.action.openDisassemblyView';
public static readonly LABEL = nls.localize('openDisassemblyView', "Open Disassembly View");
constructor() {
super({
id: OpenDisassemblyViewAction.ID,
label: OpenDisassemblyViewAction.LABEL,
alias: 'Debug: Go to Disassembly',
precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST),
contextMenuOpts: {
group: 'debug',
order: 5
}
});
}
async run(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise<void> {
if (editor.hasModel()) {
const editorService = accessor.get(IEditorService);
editorService.openEditor(DisassemblyViewInput.instance, { pinned: true });
}
}
}
export class RunToCursorAction extends EditorAction {
public static readonly ID = 'editor.debug.action.runToCursor';
@ -504,6 +532,7 @@ class CloseExceptionWidgetAction extends EditorAction {
registerAction2(ToggleBreakpointAction);
registerAction2(ConditionalBreakpointAction);
registerAction2(LogPointAction);
registerEditorAction(OpenDisassemblyViewAction);
registerEditorAction(RunToCursorAction);
registerEditorAction(StepIntoTargetsAction);
registerEditorAction(SelectionToReplAction);

View file

@ -15,7 +15,7 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { DebugModel, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { DebugModel, FunctionBreakpoint, Breakpoint, DataBreakpoint, InstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel';
import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager';
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files';
@ -30,7 +30,7 @@ import { IAction, Action } from 'vs/base/common/actions';
import { deepClone, equals } from 'vs/base/common/objects';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID, IAdapterManager, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID, IAdapterManager, IExceptionBreakpoint, CONTEXT_DISASSEMBLY_VIEW_FOCUS } from 'vs/workbench/contrib/debug/common/debug';
import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
@ -51,6 +51,7 @@ import { DEBUG_CONFIGURE_COMMAND_ID, DEBUG_CONFIGURE_LABEL } from 'vs/workbench/
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
import { IEditorInput } from 'vs/workbench/common/editor';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
export class DebugService implements IDebugService {
declare readonly _serviceBrand: undefined;
@ -72,6 +73,7 @@ export class DebugService implements IDebugService {
private inDebugMode!: IContextKey<boolean>;
private debugUx!: IContextKey<string>;
private breakpointsExist!: IContextKey<boolean>;
private disassemblyViewFocus!: IContextKey<boolean>;
private breakpointsToSendOnResourceSaved: Set<URI>;
private initializing = false;
private _initializingOptions: IDebugSessionOptions | undefined;
@ -122,6 +124,8 @@ export class DebugService implements IDebugService {
this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService);
this.debugUx.set(this.debugStorage.loadDebugUxState());
this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService);
// Need to set disassemblyViewFocus here to make it in the same context as the debug event handlers
this.disassemblyViewFocus = CONTEXT_DISASSEMBLY_VIEW_FOCUS.bindTo(contextKeyService);
});
this.chosenEnvironments = this.debugStorage.loadChosenEnvironments();
@ -177,6 +181,16 @@ export class DebugService implements IDebugService {
}
}));
this.toDispose.push(this.model.onDidChangeBreakpoints(() => setBreakpointsExistContext()));
this.toDispose.push(editorService.onDidActiveEditorChange(() => {
this.contextKeyService.bufferChangeEvents(() => {
if (editorService.activeEditor === DisassemblyViewInput.instance) {
this.disassemblyViewFocus.set(true);
} else {
this.disassemblyViewFocus.reset();
}
});
}));
}
getModel(): IDebugModel {
@ -823,6 +837,9 @@ export class DebugService implements IDebugService {
if (stackFrame) {
const editor = await stackFrame.openInEditor(this.editorService, true);
if (editor) {
if (editor.input === DisassemblyViewInput.instance) {
// Go to address is invoked via setFocus
} else {
const control = editor.getControl();
if (stackFrame && isCodeEditor(control) && control.hasModel()) {
const model = control.getModel();
@ -835,6 +852,7 @@ export class DebugService implements IDebugService {
}
}
}
}
if (session) {
this.debugType.set(session.configuration.type);
} else {
@ -885,6 +903,8 @@ export class DebugService implements IDebugService {
await this.sendFunctionBreakpoints();
} else if (breakpoint instanceof DataBreakpoint) {
await this.sendDataBreakpoints();
} else if (breakpoint instanceof InstructionBreakpoint) {
await this.sendInstructionBreakpoints();
} else {
await this.sendExceptionBreakpoints();
}
@ -966,6 +986,19 @@ export class DebugService implements IDebugService {
await this.sendDataBreakpoints();
}
async addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): Promise<void> {
this.model.addInstructionBreakpoint(address, offset, condition, hitCondition);
this.debugStorage.storeBreakpoints(this.model);
await this.sendInstructionBreakpoints();
this.debugStorage.storeBreakpoints(this.model);
}
async removeInstructionBreakpoints(address?: string): Promise<void> {
this.model.removeInstructionBreakpoints(address);
this.debugStorage.storeBreakpoints(this.model);
await this.sendInstructionBreakpoints();
}
setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void {
this.model.setExceptionBreakpoints(data);
this.debugStorage.storeBreakpoints(this.model);
@ -981,6 +1014,7 @@ export class DebugService implements IDebugService {
await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session)));
await this.sendFunctionBreakpoints(session);
await this.sendDataBreakpoints(session);
await this.sendInstructionBreakpoints(session);
// send exception breakpoints at the end since some debug adapters rely on the order
await this.sendExceptionBreakpoints(session);
}
@ -1010,6 +1044,16 @@ export class DebugService implements IDebugService {
});
}
private async sendInstructionBreakpoints(session?: IDebugSession): Promise<void> {
const breakpointsToSend = this.model.getInstructionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated());
await sendToOneOrAllSessions(this.model, session, async s => {
if (s.capabilities.supportsInstructionBreakpoints) {
await s.sendInstructionBreakpoints(breakpointsToSend);
}
});
}
private sendExceptionBreakpoints(session?: IDebugSession): Promise<void> {
const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);

View file

@ -10,7 +10,7 @@ import severity from 'vs/base/common/severity';
import { Event, Emitter } from 'vs/base/common/event';
import { Position, IPosition } from 'vs/editor/common/core/position';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { mixin } from 'vs/base/common/objects';
import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel';
@ -36,6 +36,7 @@ import { filterExceptionsFromTelemetry } from 'vs/workbench/contrib/debug/common
import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
export class DebugSession implements IDebugSession {
@ -83,7 +84,8 @@ export class DebugSession implements IDebugSession {
@ILifecycleService lifecycleService: ILifecycleService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICustomEndpointTelemetryService private readonly customEndpointTelemetryService: ICustomEndpointTelemetryService
@ICustomEndpointTelemetryService private readonly customEndpointTelemetryService: ICustomEndpointTelemetryService,
@IContextKeyService contextKeyService: IContextKeyService
) {
this._options = options || {};
if (this.hasSeparateRepl()) {
@ -451,6 +453,23 @@ export class DebugSession implements IDebugSession {
}
}
async sendInstructionBreakpoints(instructionBreakpoints: IInstructionBreakpoint[]): Promise<void> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'instruction breakpoints'));
}
if (this.raw.readyForBreakpoints) {
const response = await this.raw.setInstructionBreakpoints({ breakpoints: instructionBreakpoints });
if (response && response.body) {
const data = new Map<string, DebugProtocol.Breakpoint>();
for (let i = 0; i < instructionBreakpoints.length; i++) {
data.set(instructionBreakpoints[i].getId(), response.body.breakpoints[i]);
}
this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
}
}
}
async breakpointsLocations(uri: URI, lineNumber: number): Promise<IPosition[]> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'breakpoints locations'));
@ -540,36 +559,47 @@ export class DebugSession implements IDebugSession {
await this.raw.restartFrame({ frameId }, threadId);
}
async next(threadId: number): Promise<void> {
private setLastSteppingGranularity(threadId: number, granularity?: DebugProtocol.SteppingGranularity) {
const thread = this.getThread(threadId);
if (thread) {
thread.lastSteppingGranularity = granularity;
}
}
async next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'next'));
}
await this.raw.next({ threadId });
this.setLastSteppingGranularity(threadId, granularity);
await this.raw.next({ threadId, granularity });
}
async stepIn(threadId: number, targetId?: number): Promise<void> {
async stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepIn'));
}
await this.raw.stepIn({ threadId, targetId });
this.setLastSteppingGranularity(threadId, granularity);
await this.raw.stepIn({ threadId, targetId, granularity });
}
async stepOut(threadId: number): Promise<void> {
async stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepOut'));
}
await this.raw.stepOut({ threadId });
this.setLastSteppingGranularity(threadId, granularity);
await this.raw.stepOut({ threadId, granularity });
}
async stepBack(threadId: number): Promise<void> {
async stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepBack'));
}
await this.raw.stepBack({ threadId });
this.setLastSteppingGranularity(threadId, granularity);
await this.raw.stepBack({ threadId, granularity });
}
async continue(threadId: number): Promise<void> {
@ -690,6 +720,15 @@ export class DebugSession implements IDebugSession {
return this.raw.cancel({ progressId });
}
async disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise<DebugProtocol.DisassembledInstruction[] | undefined> {
if (!this.raw) {
return Promise.reject(new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'disassemble')));
}
const response = await this.raw.disassemble({ memoryReference, offset, instructionOffset, instructionCount, resolveSymbols: true });
return response?.body?.instructions;
}
//---- threads
getThread(threadId: number): Thread | undefined {
@ -837,6 +876,7 @@ export class DebugSession implements IDebugSession {
// Second retrieves the rest of the call stack. For performance reasons #25605
const promises = this.model.fetchCallStack(<Thread>thread);
const focus = async () => {
// Don't switch view when DisassemblyView is in focus.
if (!event.body.preserveFocusHint && thread.getCallStack().length) {
await this.debugService.focusStackFrame(undefined, thread);
if (thread.stoppedDetails) {

View file

@ -0,0 +1,618 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
import { Dimension, append, $, addStandardDisposableListener } from 'vs/base/browser/dom';
import { ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { WorkbenchTable } from 'vs/platform/list/browser/listService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, DISASSEMBLY_VIEW_ID, IDebugService, IInstructionBreakpoint, State } from 'vs/workbench/contrib/debug/common/debug';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
import { createStringBuilder } from 'vs/editor/common/core/stringBuilder';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { dispose, Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { topStackFrameColor } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
import { Color } from 'vs/base/common/color';
import { InstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
interface IDisassembledInstructionEntry {
allowBreakpoint: boolean;
isBreakpointSet: boolean;
instruction: DebugProtocol.DisassembledInstruction;
instructionAddress?: bigint;
}
export class DisassemblyView extends EditorPane {
private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50;
// Used in instruction renderer
private _fontInfo: BareFontInfo;
private _disassembledInstructions: WorkbenchTable<IDisassembledInstructionEntry> | undefined;
private _onDidChangeStackFrame: Emitter<void>;
private _previousDebuggingState: State;
private _instructionBpList: readonly IInstructionBreakpoint[] = [];
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IDebugService private readonly _debugService: IDebugService,
@IEditorService editorService: IEditorService,
) {
super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService);
this._disassembledInstructions = undefined;
this._onDidChangeStackFrame = new Emitter<void>();
this._previousDebuggingState = _debugService.state;
this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio());
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('editor')) {
this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio());
this._disassembledInstructions?.rerender();
}
}));
}
get fontInfo() { return this._fontInfo; }
get currentInstructionAddress() {
return this._debugService.getViewModel().focusedStackFrame?.instructionPointerReference;
}
get onDidChangeStackFrame() { return this._onDidChangeStackFrame.event; }
protected createEditor(parent: HTMLElement): void {
const lineHeight = this.fontInfo.lineHeight;
const delegate = new class implements ITableVirtualDelegate<IDisassembledInstructionEntry>{
headerRowHeight: number = 0; // No header
getHeight(row: IDisassembledInstructionEntry): number {
return lineHeight;
}
};
const instructionRenderer = this._register(this._instantiationService.createInstance(InstructionRenderer, this));
this._disassembledInstructions = this._register(this._instantiationService.createInstance(WorkbenchTable,
'DisassemblyView', parent, delegate,
[
{
label: '',
tooltip: '',
weight: 0,
minimumWidth: this.fontInfo.lineHeight,
maximumWidth: this.fontInfo.lineHeight,
templateId: BreakpointRenderer.TEMPLATE_ID,
project(row: IDisassembledInstructionEntry): IDisassembledInstructionEntry { return row; }
},
{
label: 'instructions',
tooltip: '',
weight: 0.3,
templateId: InstructionRenderer.TEMPLATE_ID,
project(row: IDisassembledInstructionEntry): IDisassembledInstructionEntry { return row; }
},
],
[
this._instantiationService.createInstance(BreakpointRenderer, this),
instructionRenderer,
],
{
identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address },
horizontalScrolling: false,
overrideStyles: {
listBackground: editorBackground
},
multipleSelectionSupport: false,
setRowLineHeight: false,
openOnSingleClick: false,
accessibilityProvider: new AccessibilityProvider()
}
)) as WorkbenchTable<IDisassembledInstructionEntry>;
this.reloadDisassembly();
this._register(this._disassembledInstructions.onDidScroll(e => {
if (e.oldScrollTop > e.scrollTop && e.scrollTop < e.height) {
const topElement = Math.floor(e.scrollTop / this.fontInfo.lineHeight) + DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD;
this.scrollUp_LoadDisassembledInstructions(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then((success) => {
if (success) {
this._disassembledInstructions!.reveal(topElement, 0);
}
});
} else if (e.oldScrollTop < e.scrollTop && e.scrollTop + e.height > e.scrollHeight - e.height) {
this.scrollDown_LoadDisassembledInstructions(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD);
}
}));
this._register(this._debugService.getViewModel().onDidFocusStackFrame((stackFrame) => {
if (this._disassembledInstructions) {
this.goToAddress();
this._onDidChangeStackFrame.fire();
}
}));
// refresh breakpoints view
this._register(this._debugService.getModel().onDidChangeBreakpoints(bpEvent => {
if (bpEvent && this._disassembledInstructions) {
// draw viewable BP
let changed = false;
bpEvent.added?.forEach((bp) => {
if (bp instanceof InstructionBreakpoint) {
const index = this.getIndexFromAddress(bp.instructionReference);
if (index >= 0) {
this._disassembledInstructions!.row(index).isBreakpointSet = true;
changed = true;
}
}
});
bpEvent.removed?.forEach((bp) => {
if (bp instanceof InstructionBreakpoint) {
const index = this.getIndexFromAddress(bp.instructionReference);
if (index >= 0) {
this._disassembledInstructions!.row(index).isBreakpointSet = false;
changed = true;
}
}
});
// get an updated list so that items beyond the current range would render when reached.
this._instructionBpList = this._debugService.getModel().getInstructionBreakpoints();
if (changed) {
this._onDidChangeStackFrame.fire();
}
}
}));
this._register(this._debugService.onDidChangeState(e => {
if ((e === State.Running || e === State.Stopped) &&
(this._previousDebuggingState !== State.Running && this._previousDebuggingState !== State.Stopped)) {
// Just started debugging, clear the view
this._disassembledInstructions?.splice(0, this._disassembledInstructions.length);
}
this._previousDebuggingState = e;
}));
}
layout(dimension: Dimension): void {
if (this._disassembledInstructions) {
this._disassembledInstructions.layout(dimension.height);
}
}
/**
* Go to the address provided. If no address is provided, reveal the address of the currently focused stack frame.
*/
goToAddress(address?: string): void {
if (!this._disassembledInstructions) {
return;
}
if (!address) {
address = this.currentInstructionAddress;
}
if (!address) {
return;
}
const index = this.getIndexFromAddress(address);
if (index >= 0) {
// If the row is out of the viewport, reveal it
const topElement = Math.floor(this._disassembledInstructions.scrollTop / this.fontInfo.lineHeight);
const bottomElement = Math.floor((this._disassembledInstructions.scrollTop + this._disassembledInstructions.renderHeight) / this.fontInfo.lineHeight);
if (index > topElement && index < bottomElement) {
// Inside the viewport, don't do anything here
return;
} else if (index <= topElement && index > topElement - 5) {
// Not too far from top, review it at the top
return this._disassembledInstructions.reveal(index, 0);
} else if (index >= bottomElement && index < bottomElement + 5) {
// Not too far from bottom, review it at the bottom
return this._disassembledInstructions.reveal(index, 1);
} else {
// Far from the current viewport, reveal it
return this._disassembledInstructions.reveal(index, 0.5);
}
} else if (this._debugService.state === State.Stopped) {
// Address is not provided or not in the table currently, clear the table
// and reload if we are in the state where we can load disassembly.
return this.reloadDisassembly(address);
}
}
private async scrollUp_LoadDisassembledInstructions(instructionCount: number): Promise<boolean> {
if (this._disassembledInstructions && this._disassembledInstructions.length > 0) {
const address: string | undefined = this._disassembledInstructions?.row(0).instruction.address;
return this.loadDisassembledInstructions(address, -instructionCount, instructionCount - 1);
}
return false;
}
private async scrollDown_LoadDisassembledInstructions(instructionCount: number): Promise<boolean> {
if (this._disassembledInstructions && this._disassembledInstructions.length > 0) {
const address: string | undefined = this._disassembledInstructions?.row(this._disassembledInstructions?.length - 1).instruction.address;
return this.loadDisassembledInstructions(address, 1, instructionCount);
}
return false;
}
private async loadDisassembledInstructions(address: string | undefined, instructionOffset: number, instructionCount: number): Promise<boolean> {
// if address is null, then use current stack frame.
if (!address) {
address = this.currentInstructionAddress;
}
if (!address) {
return false;
}
// console.log(`DisassemblyView: loadDisassembledInstructions ${address}, ${instructionOffset}, ${instructionCount}`);
const session = this._debugService.getViewModel().focusedSession;
const resultEntries = await session?.disassemble(address, 0, instructionOffset, instructionCount);
if (session && resultEntries && this._disassembledInstructions) {
const newEntries: IDisassembledInstructionEntry[] = [];
for (let i = 0; i < resultEntries.length; i++) {
const found = this._instructionBpList.find(p => p.instructionReference === resultEntries[i].address);
newEntries.push({ allowBreakpoint: true, isBreakpointSet: found !== undefined, instruction: resultEntries[i] });
}
// request is either at the start or end
if (instructionOffset >= 0) {
this._disassembledInstructions.splice(this._disassembledInstructions.length, 0, newEntries);
} else {
this._disassembledInstructions.splice(0, 0, newEntries);
}
return true;
}
return false;
}
private getIndexFromAddress(instructionAddress: string): number {
if (this._disassembledInstructions && this._disassembledInstructions.length > 0) {
const address = BigInt(instructionAddress);
if (address) {
let startIndex = 0;
let endIndex = this._disassembledInstructions.length - 1;
const start = this._disassembledInstructions.row(startIndex);
const end = this._disassembledInstructions.row(endIndex);
this.ensureAddressParsed(start);
this.ensureAddressParsed(end);
if (start.instructionAddress! > address ||
end.instructionAddress! < address) {
return -1;
} else if (start.instructionAddress! === address) {
return startIndex;
} else if (end.instructionAddress! === address) {
return endIndex;
}
while (endIndex > startIndex) {
const midIndex = Math.floor((endIndex - startIndex) / 2) + startIndex;
const mid = this._disassembledInstructions.row(midIndex);
this.ensureAddressParsed(mid);
if (mid.instructionAddress! > address) {
endIndex = midIndex;
} else if (mid.instructionAddress! < address) {
startIndex = midIndex;
} else {
return midIndex;
}
}
return startIndex;
}
}
return -1;
}
private ensureAddressParsed(entry: IDisassembledInstructionEntry) {
if (entry.instructionAddress !== undefined) {
return;
} else {
entry.instructionAddress = BigInt(entry.instruction.address);
}
}
/**
* Clears the table and reload instructions near the target address
*/
private reloadDisassembly(targetAddress?: string) {
if (this._disassembledInstructions) {
this._disassembledInstructions.splice(0, this._disassembledInstructions.length);
this._instructionBpList = this._debugService.getModel().getInstructionBreakpoints();
this.loadDisassembledInstructions(targetAddress, -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2).then(() => {
// on load, set the target instruction in the middle of the page.
if (this._disassembledInstructions!.length > 0) {
this._disassembledInstructions!.reveal(Math.floor(this._disassembledInstructions!.length / 2), 0.5);
}
});
}
}
}
interface IBreakpointColumnTemplateData {
container: HTMLElement,
icon: HTMLElement,
disposables: IDisposable[]
}
class BreakpointRenderer implements ITableRenderer<IDisassembledInstructionEntry, IBreakpointColumnTemplateData> {
static readonly TEMPLATE_ID = 'breakpoint';
templateId: string = BreakpointRenderer.TEMPLATE_ID;
private readonly _breakpointIcon = 'codicon-' + icons.breakpoint.regular.id;
private readonly _breakpointHintIcon = 'codicon-' + icons.debugBreakpointHint.id;
private readonly _debugStackframe = 'codicon-' + icons.debugStackframe.id;
// private readonly _debugStackframeFocused = 'codicon-' + icons.debugStackframeFocused.id;
constructor(
private readonly _disassemblyView: DisassemblyView,
@IDebugService private readonly _debugService: IDebugService
) {
}
renderTemplate(container: HTMLElement): IBreakpointColumnTemplateData {
const icon = append(container, $('.disassembly-view'));
icon.classList.add('codicon');
icon.style.display = 'flex';
icon.style.alignItems = 'center';
icon.style.justifyContent = 'center';
return { container, icon, disposables: [] };
}
renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void {
const rerenderDebugStackframe = () => {
if (element.instruction.address === this._disassemblyView.currentInstructionAddress) {
templateData.icon.classList.add(this._debugStackframe);
} else {
templateData.icon.classList.remove(this._debugStackframe);
}
templateData.icon.classList.remove(this._breakpointHintIcon);
if (element.isBreakpointSet) {
templateData.icon.classList.add(this._breakpointIcon);
} else {
templateData.icon.classList.remove(this._breakpointIcon);
}
};
rerenderDebugStackframe();
templateData.disposables.push(this._disassemblyView.onDidChangeStackFrame(rerenderDebugStackframe));
// TODO: see getBreakpointMessageAndIcon in vs\workbench\contrib\debug\browser\breakpointEditorContribution.ts
// for more types of breakpoint icons
if (element.allowBreakpoint) {
templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseover', () => {
templateData.icon.classList.add(this._breakpointHintIcon);
}));
templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseout', () => {
templateData.icon.classList.remove(this._breakpointHintIcon);
}));
templateData.disposables.push(addStandardDisposableListener(templateData.container, 'click', () => {
// click show hint while waiting for BP to resolve.
templateData.icon.classList.add(this._breakpointHintIcon);
if (element.isBreakpointSet) {
this._debugService.removeInstructionBreakpoints(element.instruction.address);
} else if (element.allowBreakpoint && !element.isBreakpointSet) {
this._debugService.addInstructionBreakpoint(element.instruction.address, 0);
}
}));
}
}
disposeElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void {
dispose(templateData.disposables);
templateData.disposables = [];
}
disposeTemplate(templateData: IBreakpointColumnTemplateData): void { }
}
interface IInstructionColumnTemplateData {
// TODO: hover widget?
instruction: HTMLElement;
disposables: IDisposable[];
}
class InstructionRenderer extends Disposable implements ITableRenderer<IDisassembledInstructionEntry, IInstructionColumnTemplateData> {
static readonly TEMPLATE_ID = 'instruction';
private static readonly INSTRUCTION_ADDR_MIN_LENGTH = 25;
private static readonly INSTRUCTION_BYTES_MIN_LENGTH = 30;
templateId: string = InstructionRenderer.TEMPLATE_ID;
private _topStackFrameColor: Color | undefined;
// private _focusedStackFrameColor: Color | undefined;
constructor(
private readonly _disassemblyView: DisassemblyView,
@IThemeService themeService: IThemeService
) {
super();
this._topStackFrameColor = themeService.getColorTheme().getColor(topStackFrameColor);
// this._focusedStackFrameColor = themeService.getColorTheme().getColor(focusedStackFrameColor);
this._register(themeService.onDidColorThemeChange(e => {
this._topStackFrameColor = e.getColor(topStackFrameColor);
// this._focusedStackFrameColor = e.getColor(focusedStackFrameColor);
}));
}
renderTemplate(container: HTMLElement): IInstructionColumnTemplateData {
const instruction = append(container, $('.instruction'));
this.applyFontInfo(instruction);
return { instruction, disposables: [] };
}
renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void {
const instruction = element.instruction;
const sb = createStringBuilder(10000);
sb.appendASCIIString(instruction.address);
let spacesToAppend = 10;
if (instruction.address.length < InstructionRenderer.INSTRUCTION_ADDR_MIN_LENGTH) {
spacesToAppend = InstructionRenderer.INSTRUCTION_ADDR_MIN_LENGTH - instruction.address.length;
}
for (let i = 0; i < spacesToAppend; i++) {
sb.appendASCII(0x00A0);
}
if (instruction.instructionBytes) {
sb.appendASCIIString(instruction.instructionBytes);
spacesToAppend = 10;
if (instruction.instructionBytes.length < InstructionRenderer.INSTRUCTION_BYTES_MIN_LENGTH) {
spacesToAppend = InstructionRenderer.INSTRUCTION_BYTES_MIN_LENGTH - instruction.instructionBytes.length;
}
for (let i = 0; i < spacesToAppend; i++) {
sb.appendASCII(0x00A0);
}
}
sb.appendASCIIString(instruction.instruction);
const innerText = sb.build();
templateData.instruction.innerText = innerText;
const rerenderBackground = () => {
if (element.instruction.address === this._disassemblyView.currentInstructionAddress) {
templateData.instruction.style.background = this._topStackFrameColor?.toString() || 'transparent';
} else {
templateData.instruction.style.background = 'transparent';
}
};
rerenderBackground();
templateData.disposables.push(this._disassemblyView.onDidChangeStackFrame(rerenderBackground));
}
disposeElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void {
dispose(templateData.disposables);
templateData.disposables = [];
}
disposeTemplate(templateData: IInstructionColumnTemplateData): void { }
private applyFontInfo(element: HTMLElement) {
const fontInfo = this._disassemblyView.fontInfo;
element.style.fontFamily = fontInfo.getMassagedFontFamily();
element.style.fontWeight = fontInfo.fontWeight;
element.style.fontSize = fontInfo.fontSize + 'px';
element.style.fontFeatureSettings = fontInfo.fontFeatureSettings;
element.style.letterSpacing = fontInfo.letterSpacing + 'px';
}
}
class AccessibilityProvider implements IListAccessibilityProvider<IDisassembledInstructionEntry> {
getWidgetAriaLabel(): string {
return localize('disassemblyView', "Disassembly View");
}
getAriaLabel(element: IDisassembledInstructionEntry): string | null {
let label = '';
if (element.isBreakpointSet) {
label += localize('breakpointIsSet', "Breakpoint is set");
} else if (element.allowBreakpoint) {
label += localize('breakpointAllowed', "Can set breakpoint");
}
const instruction = element.instruction;
label += `, ${localize('instructionAddress', "Instruction address")}: ${instruction.address}`;
if (instruction.instructionBytes) {
label += `, ${localize('instructionBytes', "Instruction bytes")}: ${instruction.instructionBytes}`;
}
label += `, ${localize(`instructionText`, "Instruction")}: ${instruction.instruction}`;
return label;
}
}
export class DisassemblyViewContribution implements IWorkbenchContribution {
private readonly _onDidActiveEditorChangeListener: IDisposable;
private _onDidChangeModelLanguage: IDisposable | undefined;
private _languageSupportsDisassemleRequest: IContextKey<boolean> | undefined;
constructor(
@IEditorService editorService: IEditorService,
@IDebugService debugService: IDebugService,
@IContextKeyService contextKeyService: IContextKeyService
) {
contextKeyService.bufferChangeEvents(() => {
this._languageSupportsDisassemleRequest = CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST.bindTo(contextKeyService);
});
const onDidActiveEditorChangeListener = () => {
if (this._onDidChangeModelLanguage) {
this._onDidChangeModelLanguage.dispose();
this._onDidChangeModelLanguage = undefined;
}
const activeTextEditorControl = editorService.activeTextEditorControl;
if (isCodeEditor(activeTextEditorControl)) {
const language = activeTextEditorControl.getModel()?.getLanguageIdentifier().language;
// TODO: instead of using idDebuggerInterestedInLanguage, have a specific ext point for languages
// support disassembly
this._languageSupportsDisassemleRequest?.set(!!language && debugService.getAdapterManager().isDebuggerInterestedInLanguage(language));
this._onDidChangeModelLanguage = activeTextEditorControl.onDidChangeModelLanguage(e => {
this._languageSupportsDisassemleRequest?.set(debugService.getAdapterManager().isDebuggerInterestedInLanguage(e.newLanguage));
});
} else {
this._languageSupportsDisassemleRequest?.set(false);
}
};
onDidActiveEditorChangeListener();
this._onDidActiveEditorChangeListener = editorService.onDidActiveEditorChange(onDidActiveEditorChangeListener);
}
dispose(): void {
this._onDidActiveEditorChangeListener.dispose();
this._onDidChangeModelLanguage?.dispose();
}
}

View file

@ -497,6 +497,22 @@ export class RawDebugSession implements IDisposable {
return Promise.reject(new Error('goto is not supported'));
}
async setInstructionBreakpoints(args: DebugProtocol.SetInstructionBreakpointsArguments): Promise<DebugProtocol.SetInstructionBreakpointsResponse | undefined> {
if (this.capabilities.supportsInstructionBreakpoints) {
return await this.send('setInstructionBreakpoints', args);
}
return Promise.reject(new Error('setInstructionBreakpoints is not supported'));
}
async disassemble(args: DebugProtocol.DisassembleArguments): Promise<DebugProtocol.DisassembleResponse | undefined> {
if (this.capabilities.supportsDisassembleRequest) {
return await this.send('disassemble', args);
}
return Promise.reject(new Error('disassemble is not supported'));
}
cancel(args: DebugProtocol.CancelArguments): Promise<DebugProtocol.CancelResponse | undefined> {
return this.send('cancel', args);
}

View file

@ -11,7 +11,7 @@ import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
import { IEditorPane, ITextEditorPane } from 'vs/workbench/common/editor';
import { IEditorPane } from 'vs/workbench/common/editor';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { Range, IRange } from 'vs/editor/common/core/range';
@ -34,6 +34,7 @@ export const WATCH_VIEW_ID = 'workbench.debug.watchExpressionsView';
export const CALLSTACK_VIEW_ID = 'workbench.debug.callStackView';
export const LOADED_SCRIPTS_VIEW_ID = 'workbench.debug.loadedScriptsView';
export const BREAKPOINTS_VIEW_ID = 'workbench.debug.breakPointsView';
export const DISASSEMBLY_VIEW_ID = 'workbench.debug.disassemblyView';
export const DEBUG_PANEL_ID = 'workbench.panel.repl';
export const REPL_VIEW_ID = 'workbench.panel.repl.view';
export const DEBUG_SERVICE_ID = 'debugService';
@ -80,6 +81,9 @@ export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey<boolean>
export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey<boolean>('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") });
export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey<boolean>('multiSessionRepl', false, { type: 'boolean', description: nls.localize('multiSessionRepl', "True when there is more than 1 debug console.") });
export const CONTEXT_MULTI_SESSION_DEBUG = new RawContextKey<boolean>('multiSessionDebug', false, { type: 'boolean', description: nls.localize('multiSessionDebug', "True when there is more than 1 active debug session.") });
export const CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED = new RawContextKey<boolean>('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") });
export const CONTEXT_DISASSEMBLY_VIEW_FOCUS = new RawContextKey<boolean>('disassemblyViewFocus', false, { type: 'boolean', description: nls.localize('disassemblyViewFocus', "True when the Disassembly View is focused.") });
export const CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST = new RawContextKey<boolean>('languageSupportsDisassembleRequest', false, { type: 'boolean', description: nls.localize('languageSupportsDisassembleRequest', "True when the language in the current editor supports disassemble request.") });
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint';
@ -258,6 +262,7 @@ export interface IDebugSession extends ITreeElement {
sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise<void>;
dataBreakpointInfo(name: string, variablesReference?: number): Promise<IDataBreakpointInfoResponse | undefined>;
sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise<void>;
sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise<void>;
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void>;
breakpointsLocations(uri: uri, lineNumber: number): Promise<IPosition[]>;
getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined;
@ -269,13 +274,14 @@ export interface IDebugSession extends ITreeElement {
evaluate(expression: string, frameId?: number, context?: string): Promise<DebugProtocol.EvaluateResponse | undefined>;
customRequest(request: string, args: any): Promise<DebugProtocol.Response | undefined>;
cancel(progressId: string): Promise<DebugProtocol.CancelResponse | undefined>;
disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise<DebugProtocol.DisassembledInstruction[] | undefined>;
restartFrame(frameId: number, threadId: number): Promise<void>;
next(threadId: number): Promise<void>;
stepIn(threadId: number, targetId?: number): Promise<void>;
next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
stepInTargets(frameId: number): Promise<{ id: number, label: string }[] | undefined>;
stepOut(threadId: number): Promise<void>;
stepBack(threadId: number): Promise<void>;
stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
continue(threadId: number): Promise<void>;
reverseContinue(threadId: number): Promise<void>;
pause(threadId: number): Promise<void>;
@ -342,10 +348,10 @@ export interface IThread extends ITreeElement {
*/
readonly stopped: boolean;
next(): Promise<any>;
stepIn(): Promise<any>;
stepOut(): Promise<any>;
stepBack(): Promise<any>;
next(granularity?: DebugProtocol.SteppingGranularity): Promise<any>;
stepIn(granularity?: DebugProtocol.SteppingGranularity): Promise<any>;
stepOut(granularity?: DebugProtocol.SteppingGranularity): Promise<any>;
stepBack(granularity?: DebugProtocol.SteppingGranularity): Promise<any>;
continue(): Promise<any>;
pause(): Promise<any>;
terminate(): Promise<any>;
@ -367,12 +373,13 @@ export interface IStackFrame extends ITreeElement {
readonly range: IRange;
readonly source: Source;
readonly canRestart: boolean;
readonly instructionPointerReference?: string;
getScopes(): Promise<IScope[]>;
getMostSpecificScopes(range: IRange): Promise<ReadonlyArray<IScope>>;
forgetScopes(): void;
restart(): Promise<any>;
toString(): string;
openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): Promise<ITextEditorPane | undefined>;
openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined>;
equals(other: IStackFrame): boolean;
}
@ -436,6 +443,12 @@ export interface IDataBreakpoint extends IBaseBreakpoint {
readonly accessType: DebugProtocol.DataBreakpointAccessType;
}
export interface IInstructionBreakpoint extends IBaseBreakpoint {
// instructionReference is the instruction 'address' from the debugger.
readonly instructionReference: string;
readonly offset?: number;
}
export interface IExceptionInfo {
readonly id?: string;
readonly description?: string;
@ -485,6 +498,7 @@ export interface IDebugModel extends ITreeElement {
getFunctionBreakpoints(): ReadonlyArray<IFunctionBreakpoint>;
getDataBreakpoints(): ReadonlyArray<IDataBreakpoint>;
getExceptionBreakpoints(): ReadonlyArray<IExceptionBreakpoint>;
getInstructionBreakpoints(): ReadonlyArray<IInstructionBreakpoint>;
getWatchExpressions(): ReadonlyArray<IExpression & IEvaluate>;
onDidChangeBreakpoints: Event<IBreakpointsChangeEvent | undefined>;
@ -496,9 +510,9 @@ export interface IDebugModel extends ITreeElement {
* An event describing a change to the set of [breakpoints](#debug.Breakpoint).
*/
export interface IBreakpointsChangeEvent {
added?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint>;
removed?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint>;
changed?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint>;
added?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>;
removed?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>;
changed?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>;
sessionOnly: boolean;
}
@ -892,6 +906,18 @@ export interface IDebugService {
*/
removeDataBreakpoints(id?: string): Promise<void>;
/**
* Adds a new instruction breakpoint.
*/
addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): Promise<void>;
/**
* Removes all instruction breakpoints. If address is passed only removes the instruction breakpoint with the passed address.
* The address should be the address string supplied by the debugger from the "Disassemble" request.
* Notifies debug adapter of breakpoint changes.
*/
removeInstructionBreakpoints(address?: string): Promise<void>;
setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise<void>;
setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void;
@ -956,6 +982,7 @@ export interface IDebugService {
* Gets the current view model.
*/
getViewModel(): IViewModel;
}
// Editor interfaces

View file

@ -14,16 +14,17 @@ import { distinct, lastIndex } from 'vs/base/common/arrays';
import { Range, IRange } from 'vs/editor/common/core/range';
import {
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel,
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint, IInstructionBreakpoint
} from 'vs/workbench/contrib/debug/common/debug';
import { Source, UNKNOWN_SOURCE_LABEL, getUriFromSource } from 'vs/workbench/contrib/debug/common/debugSource';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextEditorPane } from 'vs/workbench/common/editor';
import { IEditorPane } from 'vs/workbench/common/editor';
import { mixin } from 'vs/base/common/objects';
import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
interface IDebugProtocolVariableWithContext extends DebugProtocol.Variable {
__vscodeVariableMenuContext?: string;
@ -321,14 +322,15 @@ export class StackFrame implements IStackFrame {
private scopes: Promise<Scope[]> | undefined;
constructor(
public thread: IThread,
public thread: Thread,
public frameId: number,
public source: Source,
public name: string,
public presentationHint: string | undefined,
public range: IRange,
private index: number,
public canRestart: boolean
public canRestart: boolean,
public instructionPointerReference?: string
) { }
getId(): string {
@ -385,7 +387,14 @@ export class StackFrame implements IStackFrame {
return sourceToString === UNKNOWN_SOURCE_LABEL ? this.name : `${this.name} (${sourceToString})`;
}
async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<ITextEditorPane | undefined> {
async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined> {
const threadStopReason = this.thread.stoppedDetails?.reason;
if (this.instructionPointerReference &&
(threadStopReason === 'instruction breakpoint' ||
(threadStopReason === 'step' && this.thread.lastSteppingGranularity === 'instruction'))) {
return editorService.openEditor(DisassemblyViewInput.instance, { pinned: true });
}
if (this.source.available) {
return this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned);
}
@ -404,6 +413,7 @@ export class Thread implements IThread {
public stoppedDetails: IRawStoppedDetails | undefined;
public stopped: boolean;
public reachedEndOfCallStack = false;
public lastSteppingGranularity: DebugProtocol.SteppingGranularity | undefined;
constructor(public session: IDebugSession, public name: string, public threadId: number) {
this.callStack = [];
@ -491,7 +501,7 @@ export class Thread implements IThread {
rsf.column,
rsf.endLine || rsf.line,
rsf.endColumn || rsf.column
), startFrame + index, typeof rsf.canRestart === 'boolean' ? rsf.canRestart : true);
), startFrame + index, typeof rsf.canRestart === 'boolean' ? rsf.canRestart : true, rsf.instructionPointerReference);
});
} catch (err) {
if (this.stoppedDetails) {
@ -518,20 +528,20 @@ export class Thread implements IThread {
return Promise.resolve(undefined);
}
next(): Promise<any> {
return this.session.next(this.threadId);
next(granularity?: DebugProtocol.SteppingGranularity): Promise<any> {
return this.session.next(this.threadId, granularity);
}
stepIn(): Promise<any> {
return this.session.stepIn(this.threadId);
stepIn(granularity?: DebugProtocol.SteppingGranularity): Promise<any> {
return this.session.stepIn(this.threadId, undefined, granularity);
}
stepOut(): Promise<any> {
return this.session.stepOut(this.threadId);
stepOut(granularity?: DebugProtocol.SteppingGranularity): Promise<any> {
return this.session.stepOut(this.threadId, granularity);
}
stepBack(): Promise<any> {
return this.session.stepBack(this.threadId);
stepBack(granularity?: DebugProtocol.SteppingGranularity): Promise<any> {
return this.session.stepBack(this.threadId, granularity);
}
continue(): Promise<any> {
@ -568,6 +578,7 @@ interface IBreakpointSessionData extends DebugProtocol.Breakpoint {
supportsLogPoints: boolean;
supportsFunctionBreakpoints: boolean;
supportsDataBreakpoints: boolean;
supportsInstructionBreakpoints: boolean
sessionId: string;
}
@ -577,7 +588,8 @@ function toBreakpointSessionData(data: DebugProtocol.Breakpoint, capabilities: D
supportsHitConditionalBreakpoints: !!capabilities.supportsHitConditionalBreakpoints,
supportsLogPoints: !!capabilities.supportsLogPoints,
supportsFunctionBreakpoints: !!capabilities.supportsFunctionBreakpoints,
supportsDataBreakpoints: !!capabilities.supportsDataBreakpoints
supportsDataBreakpoints: !!capabilities.supportsDataBreakpoints,
supportsInstructionBreakpoints: !!capabilities.supportsInstructionBreakpoints
}, data);
}
@ -761,7 +773,6 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
return true;
}
override setSessionData(sessionId: string, data: IBreakpointSessionData | undefined): void {
super.setSessionData(sessionId, data);
if (!this._adapterData) {
@ -908,6 +919,41 @@ export class ExceptionBreakpoint extends BaseBreakpoint implements IExceptionBre
}
}
export class InstructionBreakpoint extends BaseBreakpoint implements IInstructionBreakpoint {
constructor(
public instructionReference: string,
public offset: number,
public canPersist: boolean,
enabled: boolean,
hitCondition: string | undefined,
condition: string | undefined,
logMessage: string | undefined,
id = generateUuid()
) {
super(enabled, hitCondition, condition, logMessage, id);
}
override toJSON(): any {
const result = super.toJSON();
result.instructionReference = this.instructionReference;
result.offset = this.offset;
return result;
}
get supported(): boolean {
if (!this.data) {
return true;
}
return this.data.supportsInstructionBreakpoints;
}
override toString(): string {
return this.instructionReference;
}
}
export class ThreadAndSessionIds implements ITreeElement {
constructor(public sessionId: string, public threadId: number) { }
@ -929,6 +975,7 @@ export class DebugModel implements IDebugModel {
private exceptionBreakpoints: ExceptionBreakpoint[];
private dataBreakopints: DataBreakpoint[];
private watchExpressions: Expression[];
private instructionBreakpoints: InstructionBreakpoint[];
constructor(
debugStorage: DebugStorage,
@ -940,6 +987,7 @@ export class DebugModel implements IDebugModel {
this.exceptionBreakpoints = debugStorage.loadExceptionBreakpoints();
this.dataBreakopints = debugStorage.loadDataBreakpoints();
this.watchExpressions = debugStorage.loadWatchExpressions();
this.instructionBreakpoints = [];
this.sessions = [];
}
@ -1095,6 +1143,10 @@ export class DebugModel implements IDebugModel {
return this.exceptionBreakpoints;
}
getInstructionBreakpoints(): IInstructionBreakpoint[] {
return this.instructionBreakpoints;
}
setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void {
if (data) {
if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) =>
@ -1197,6 +1249,16 @@ export class DebugModel implements IDebugModel {
}
}
});
this.instructionBreakpoints.forEach(ibp => {
if (!data) {
ibp.setSessionData(sessionId, undefined);
} else {
const ibpData = data.get(ibp.getId());
if (ibpData) {
ibp.setSessionData(sessionId, toBreakpointSessionData(ibpData, capabilites));
}
}
});
this._onDidChangeBreakpoints.fire({
sessionOnly: true
@ -1229,9 +1291,9 @@ export class DebugModel implements IDebugModel {
}
setEnablement(element: IEnablement, enable: boolean): void {
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof ExceptionBreakpoint || element instanceof DataBreakpoint) {
const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint> = [];
if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof DataBreakpoint)) {
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof ExceptionBreakpoint || element instanceof DataBreakpoint || element instanceof InstructionBreakpoint) {
const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint> = [];
if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof DataBreakpoint || element instanceof InstructionBreakpoint)) {
changed.push(element);
}
@ -1245,7 +1307,7 @@ export class DebugModel implements IDebugModel {
}
enableOrDisableAllBreakpoints(enable: boolean): void {
const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint> = [];
const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint> = [];
this.breakpoints.forEach(bp => {
if (bp.enabled !== enable) {
@ -1265,6 +1327,13 @@ export class DebugModel implements IDebugModel {
}
dbp.enabled = enable;
});
this.instructionBreakpoints.forEach(ibp => {
if (ibp.enabled !== enable) {
changed.push(ibp);
}
ibp.enabled = enable;
});
if (enable) {
this.breakpointsActivated = true;
}
@ -1326,6 +1395,24 @@ export class DebugModel implements IDebugModel {
this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false });
}
addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): void {
const newInstructionBreakpoint = new InstructionBreakpoint(address, offset, false, true, hitCondition, condition, undefined);
this.instructionBreakpoints.push(newInstructionBreakpoint);
this._onDidChangeBreakpoints.fire({ added: [newInstructionBreakpoint], sessionOnly: true });
}
removeInstructionBreakpoints(address?: string): void {
let removed: InstructionBreakpoint[];
if (address) {
removed = this.instructionBreakpoints.filter(fbp => fbp.instructionReference === address);
this.instructionBreakpoints = this.instructionBreakpoints.filter(fbp => fbp.instructionReference !== address);
} else {
removed = this.instructionBreakpoints;
this.instructionBreakpoints = [];
}
this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false });
}
getWatchExpressions(): Expression[] {
return this.watchExpressions;
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils';
@ -30,6 +30,7 @@ export class ViewModel implements IViewModel {
private setVariableSupported!: IContextKey<boolean>;
private multiSessionDebug!: IContextKey<boolean>;
private terminateDebuggeeSuported!: IContextKey<boolean>;
private disassembleRequestSupported!: IContextKey<boolean>;
constructor(private contextKeyService: IContextKeyService) {
contextKeyService.bufferChangeEvents(() => {
@ -43,6 +44,7 @@ export class ViewModel implements IViewModel {
this.setVariableSupported = CONTEXT_SET_VARIABLE_SUPPORTED.bindTo(contextKeyService);
this.multiSessionDebug = CONTEXT_MULTI_SESSION_DEBUG.bindTo(contextKeyService);
this.terminateDebuggeeSuported = CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED.bindTo(contextKeyService);
this.disassembleRequestSupported = CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED.bindTo(contextKeyService);
});
}
@ -78,6 +80,7 @@ export class ViewModel implements IViewModel {
this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false);
this.setVariableSupported.set(session ? !!session.capabilities.supportsSetVariable : false);
this.terminateDebuggeeSuported.set(session ? !!session.capabilities.supportTerminateDebuggee : false);
this.disassembleRequestSupported.set(!!stackFrame?.instructionPointerReference && !!session?.capabilities.supportsDisassembleRequest);
const attach = !!session && isSessionAttach(session);
this.focusedSessionIsAttach.set(attach);
});

View file

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { localize } from 'vs/nls';
export class DisassemblyViewInput extends EditorInput {
static readonly ID = 'debug.disassemblyView.input';
override get typeId(): string {
return DisassemblyViewInput.ID;
}
static _instance: DisassemblyViewInput;
static get instance() {
if (!DisassemblyViewInput._instance || DisassemblyViewInput._instance.isDisposed()) {
DisassemblyViewInput._instance = new DisassemblyViewInput();
}
return DisassemblyViewInput._instance;
}
readonly resource = undefined;
override getName(): string {
return localize('disassemblyInputName', "Disassembly");
}
override matches(other: unknown): boolean {
return other instanceof DisassemblyViewInput;
}
}

View file

@ -224,6 +224,28 @@ suite('Debug - Breakpoints', () => {
assert.strictEqual(exceptionBreakpoints[1].enabled, false);
});
test('instruction breakpoints', () => {
let eventCount = 0;
model.onDidChangeBreakpoints(() => eventCount++);
//address: string, offset: number, condition?: string, hitCondition?: string
model.addInstructionBreakpoint('0xCCCCFFFF', 0);
assert.strictEqual(eventCount, 1);
let instructionBreakpoints = model.getInstructionBreakpoints();
assert.strictEqual(instructionBreakpoints.length, 1);
assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF');
assert.strictEqual(instructionBreakpoints[0].offset, 0);
model.addInstructionBreakpoint('0xCCCCEEEE', 1);
assert.strictEqual(eventCount, 2);
instructionBreakpoints = model.getInstructionBreakpoints();
assert.strictEqual(instructionBreakpoints.length, 2);
assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF');
assert.strictEqual(instructionBreakpoints[0].offset, 0);
assert.strictEqual(instructionBreakpoints[1].instructionReference, '0xCCCCEEEE');
assert.strictEqual(instructionBreakpoints[1].offset, 1);
});
test('data breakpoints', () => {
let eventCount = 0;
model.onDidChangeBreakpoints(() => eventCount++);

View file

@ -38,7 +38,7 @@ export function createMockSession(model: DebugModel, name = 'mockSession', optio
}
};
}
} as IDebugService, undefined!, undefined!, new TestConfigurationService({ debug: { console: { collapseIdenticalLines: true } } }), undefined!, mockWorkspaceContextService, undefined!, undefined!, undefined!, mockUriIdentityService, new TestInstantiationService(), undefined!);
} as IDebugService, undefined!, undefined!, new TestConfigurationService({ debug: { console: { collapseIdenticalLines: true } } }), undefined!, mockWorkspaceContextService, undefined!, undefined!, undefined!, mockUriIdentityService, new TestInstantiationService(), undefined!, undefined!);
}
function createTwoStackFrames(session: DebugSession): { firstStackFrame: StackFrame, secondStackFrame: StackFrame } {
@ -378,7 +378,7 @@ suite('Debug - CallStack', () => {
override get state(): State {
return State.Stopped;
}
}(generateUuid(), { resolved: { name: 'stoppedSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, mockWorkspaceContextService, undefined!, undefined!, undefined!, mockUriIdentityService, new TestInstantiationService(), undefined!);
}(generateUuid(), { resolved: { name: 'stoppedSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, mockWorkspaceContextService, undefined!, undefined!, undefined!, mockUriIdentityService, new TestInstantiationService(), undefined!, undefined!);
const runningSession = createMockSession(model);
model.addSession(runningSession);

View file

@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate, IAdapterManager, IRawStoppedDetails } from 'vs/workbench/contrib/debug/common/debug';
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate, IAdapterManager, IRawStoppedDetails, IInstructionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import Severity from 'vs/base/common/severity';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
@ -86,6 +86,14 @@ export class MockDebugService implements IDebugService {
throw new Error('not implemented');
}
addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): Promise<void> {
throw new Error('Method not implemented.');
}
removeInstructionBreakpoints(address?: string): Promise<void> {
throw new Error('Method not implemented.');
}
setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string): Promise<void> {
throw new Error('Method not implemented.');
}
@ -327,6 +335,9 @@ export class MockSession implements IDebugSession {
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void> {
throw new Error('Method not implemented.');
}
sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise<void> {
throw new Error('Method not implemented.');
}
getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined {
throw new Error('Method not implemented.');
}
@ -351,16 +362,16 @@ export class MockSession implements IDebugSession {
restartFrame(frameId: number, threadId: number): Promise<void> {
throw new Error('Method not implemented.');
}
next(threadId: number): Promise<void> {
next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
throw new Error('Method not implemented.');
}
stepIn(threadId: number, targetId?: number): Promise<void> {
stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
throw new Error('Method not implemented.');
}
stepOut(threadId: number): Promise<void> {
stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
throw new Error('Method not implemented.');
}
stepBack(threadId: number): Promise<void> {
stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
throw new Error('Method not implemented.');
}
continue(threadId: number): Promise<void> {
@ -381,6 +392,9 @@ export class MockSession implements IDebugSession {
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse> {
throw new Error('Method not implemented.');
}
disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise<DebugProtocol.DisassembledInstruction[] | undefined> {
throw new Error('Method not implemented.');
}
terminate(restart = false): Promise<void> {
throw new Error('Method not implemented.');