mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 13:46:13 +00:00
Merge pull request #125737 from xisui-MSFT/dev/xisui/disassembly
Disassembly view
This commit is contained in:
commit
4f5f5de28f
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
618
src/vs/workbench/contrib/debug/browser/disassemblyView.ts
Normal file
618
src/vs/workbench/contrib/debug/browser/disassemblyView.ts
Normal 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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++);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.');
|
||||
|
|
Loading…
Reference in a new issue