From 45bdf6bb6f6dff2ac009d5ebee6fbab8402d2d33 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Wed, 2 Jun 2021 14:17:58 -0700 Subject: [PATCH 01/49] disassembly view: initial commit --- .../debug/browser/debug.contribution.ts | 11 +- .../debug/browser/debugAdapterManager.ts | 12 +- .../debug/browser/debugEditorActions.ts | 29 +++ .../contrib/debug/browser/disassemblyView.ts | 199 ++++++++++++++++++ .../workbench/contrib/debug/common/debug.ts | 2 + .../contrib/debug/common/debugSchemas.ts | 25 +++ 6 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/browser/disassemblyView.ts diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index c7fffde5224fe..8861d16e41f4c 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -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,9 @@ 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 { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor'; +import { EditorExtensions } from 'vs/workbench/common/editor'; +import { DisassemblyView, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); @@ -373,6 +376,12 @@ 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 disassmelby editor +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), + [new SyncDescriptor(DisassemblyViewInput)] +); + // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 7f527846f8242..7c319f99c8627 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -18,7 +18,7 @@ import { IDebugConfiguration, IConfig, IDebugAdapterDescriptorFactory, IDebugAda import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { launchSchema, debuggersExtPoint, breakpointsExtPoint, presentationSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { launchSchema, debuggersExtPoint, breakpointsExtPoint, presentationSchema, disassemblyExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; @@ -41,6 +41,7 @@ export class AdapterManager implements IAdapterManager { private readonly _onDidRegisterDebugger = new Emitter(); private readonly _onDidDebuggersExtPointRead = new Emitter(); private breakpointModeIdsSet = new Set(); + private disassemblyModeIdsSet = new Set(); constructor( @IEditorService private readonly editorService: IEditorService, @@ -155,6 +156,15 @@ export class AdapterManager implements IAdapterManager { added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); }); }); + + disassemblyExtPoint.setHandler((extensions, delta) => { + delta.removed.forEach(removed => { + removed.value.forEach(disassembly => this.disassemblyModeIdsSet.delete(disassembly.language)); + }); + delta.added.forEach(added => { + added.value.forEach(disassembly => this.disassemblyModeIdsSet.add(disassembly.language)); + }); + }); } registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 51e666b94e2cf..e03d2f378f780 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -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/browser/disassemblyView'; class ToggleBreakpointAction extends EditorAction2 { constructor() { @@ -133,6 +134,33 @@ class LogPointAction extends EditorAction2 { } } +class GoToDisassemblyAction extends EditorAction { + + public static readonly ID = 'editor.debug.action.goToDisassembly'; + public static readonly LABEL = nls.localize('goToDisassembly', "Go to Disassembly"); + + constructor() { + super({ + id: GoToDisassemblyAction.ID, + label: GoToDisassemblyAction.LABEL, + alias: 'Debug: Go to Disassembly', + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), + contextMenuOpts: { + group: 'debug', + order: 5 + } + }); + } + + async run(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise { + const position = editor.getPosition(); + if (position && editor.hasModel()) { + const editorService = accessor.get(IEditorService); + editorService.openEditor(DisassemblyViewInput.instance); + } + } +} + 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(GoToDisassemblyAction); registerEditorAction(RunToCursorAction); registerEditorAction(StepIntoTargetsAction); registerEditorAction(SelectionToReplAction); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts new file mode 100644 index 0000000000000..b3d94382b4769 --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Dimension, append, $ } from 'vs/base/browser/dom'; +import { ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +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 { EditorInput } from 'vs/workbench/common/editor'; +import { DISASSEMBLY_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; + +interface IDisassembledInstructionEntry { + allowBreakpoint: boolean; + isBreakpointSet: boolean; + instruction: DebugProtocol.DisassembledInstruction; +} + +export class DisassemblyView extends EditorPane { + + private _editorOptions: IEditorOptions; + private _disassembledInstructions: WorkbenchTable | null; + + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { + super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); + + this._editorOptions = configurationService.getValue('editor'); + configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor')) { + this._editorOptions = configurationService.getValue('editor'); + // TODO: refresh view + } + }); + + this._disassembledInstructions = null; + } + + protected createEditor(parent: HTMLElement): void { + const lineHeight = this._editorOptions.lineHeight!; + const delegate = new class implements ITableVirtualDelegate{ + headerRowHeight: number = 0; // No header + getHeight(row: IDisassembledInstructionEntry): number { + return lineHeight; + } + }; + + this._disassembledInstructions = this._register(this._instantiationService.createInstance(WorkbenchTable, + 'DisassemblyView', parent, delegate, + [ + { + label: '', + tooltip: '', + weight: 0, + minimumWidth: 40, + maximumWidth: 40, + 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._instantiationService.createInstance(InstructionRenderer), + ], + { + identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address }, + horizontalScrolling: false, + overrideStyles: { + listBackground: editorBackground + }, + multipleSelectionSupport: false, + setRowLineHeight: false, + openOnSingleClick: false, + } + )) as WorkbenchTable; + + this.loadDisassembledInstructions('0x00005000'); + } + + layout(dimension: Dimension): void { + if (this._disassembledInstructions) { + this._disassembledInstructions.layout(dimension.height); + } + } + + private loadDisassembledInstructions(address: string): void { + const newEntries: IDisassembledInstructionEntry[] = []; + for (let i = 0; i < 50; i++) { + newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: { address, instruction: 'instruction instruction, instruction' } }); + } + if (this._disassembledInstructions) { + // TODO: append/insert + this._disassembledInstructions.splice(0, this._disassembledInstructions.length, newEntries); + } + } + +} + +interface IBreakpointColumnTemplateData { + icon: HTMLImageElement +} + +class BreakpointRenderer implements ITableRenderer { + + static readonly TEMPLATE_ID = 'breakpoint'; + + templateId: string = BreakpointRenderer.TEMPLATE_ID; + + renderTemplate(container: HTMLElement): IBreakpointColumnTemplateData { + const icon = append(container, $('img.icon')); + return { icon }; + } + + renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void { + if (element.isBreakpointSet) { + // TODO: breakpoint icon + } + } + disposeTemplate(templateData: IBreakpointColumnTemplateData): void { } + +} + +interface IInstructionColumnTemplateData { + // TODO: hover widget? + instruction: HTMLElement; +} + +class InstructionRenderer implements ITableRenderer { + + static readonly TEMPLATE_ID = 'instruction'; + + templateId: string = InstructionRenderer.TEMPLATE_ID; + + renderTemplate(container: HTMLElement): IInstructionColumnTemplateData { + const instruction = append(container, $('instruction')); + return { instruction }; + } + + renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void { + const instruction = element.instruction; + templateData.instruction.innerText = `${instruction.address}\t${instruction.instructionBytes}\t${instruction.instruction}`; + } + + disposeTemplate(templateData: IInstructionColumnTemplateData): void { } + +} + +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('extensionsInputName', "Running Extensions"); + } + + override canSplit(): boolean { + return false; + } + + override matches(other: unknown): boolean { + return other instanceof DisassemblyViewInput; + } +} diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 68999dc311b59..00dd54bb18130 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -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,7 @@ export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") }); export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('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('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('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") }); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; diff --git a/src/vs/workbench/contrib/debug/common/debugSchemas.ts b/src/vs/workbench/contrib/debug/common/debugSchemas.ts index a32cb7dd2e2f7..721ccecfb2419 100644 --- a/src/vs/workbench/contrib/debug/common/debugSchemas.ts +++ b/src/vs/workbench/contrib/debug/common/debugSchemas.ts @@ -127,6 +127,31 @@ export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registe } }); +export interface IDisassemblyContribution { + language: string; +} + +// disassembly extension point +export const disassemblyExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'disassembly', + jsonSchema: { + description: nls.localize('vscode.extension.contributes.disassembly', 'Contributes disassembly.'), + type: 'array', + defaultSnippets: [{ body: [{ language: '' }] }], + items: { + type: 'object', + additionalProperties: false, + defaultSnippets: [{ body: { language: '' } }], + properties: { + language: { + description: nls.localize('vscode.extension.contributes.disassembly.language', "Allow disassembly view for this language."), + type: 'string' + }, + } + } + } +}); + // debug general schema export const presentationSchema: IJSONSchema = { From aada2f44f8f3cc37eff847940fc566617b1de8dd Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 3 Jun 2021 16:24:59 -0700 Subject: [PATCH 02/49] Rendering table, start to work on scrolling --- .../contrib/debug/browser/disassemblyView.ts | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index b3d94382b4769..8a267ad2bf60c 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -3,9 +3,10 @@ * 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, $ } from 'vs/base/browser/dom'; import { ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +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'; @@ -15,7 +16,7 @@ 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 { EditorInput } from 'vs/workbench/common/editor'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DISASSEMBLY_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; interface IDisassembledInstructionEntry { @@ -26,7 +27,7 @@ interface IDisassembledInstructionEntry { export class DisassemblyView extends EditorPane { - private _editorOptions: IEditorOptions; + private _fontInfo: BareFontInfo; private _disassembledInstructions: WorkbenchTable | null; @@ -39,11 +40,11 @@ export class DisassemblyView extends EditorPane { ) { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); - this._editorOptions = configurationService.getValue('editor'); + this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor')) { - this._editorOptions = configurationService.getValue('editor'); - // TODO: refresh view + this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); + this._disassembledInstructions?.rerender(); } }); @@ -51,7 +52,7 @@ export class DisassemblyView extends EditorPane { } protected createEditor(parent: HTMLElement): void { - const lineHeight = this._editorOptions.lineHeight!; + const lineHeight = this._fontInfo.lineHeight; const delegate = new class implements ITableVirtualDelegate{ headerRowHeight: number = 0; // No header getHeight(row: IDisassembledInstructionEntry): number { @@ -186,11 +187,7 @@ export class DisassemblyViewInput extends EditorInput { readonly resource = undefined; override getName(): string { - return localize('extensionsInputName', "Running Extensions"); - } - - override canSplit(): boolean { - return false; + return localize('disassemblyInputName', "Disassembly"); } override matches(other: unknown): boolean { From a83ef3a31c4cdf5a8d0b4ffec548e1bfecdaf310 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Fri, 4 Jun 2021 03:37:00 -0700 Subject: [PATCH 03/49] First Compile --- .../api/browser/mainThreadDebugService.ts | 4 +- .../contrib/debug/browser/debugService.ts | 23 +++++ .../contrib/debug/browser/debugSession.ts | 43 ++++++++-- .../contrib/debug/browser/rawDebugSession.ts | 16 ++++ .../workbench/contrib/debug/common/debug.ts | 23 ++++- .../contrib/debug/common/debugModel.ts | 85 ++++++++++++++++++- .../contrib/debug/test/browser/mockDebug.ts | 13 ++- 7 files changed, 188 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 60c3a74c01703..cbde4c4482615 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -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 @@ -337,7 +337,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return undefined; } - private convertToDto(bps: (ReadonlyArray)): Array { + private convertToDto(bps: (ReadonlyArray)): Array { return bps.map(bp => { if ('name' in bp) { const fbp = bp; diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index c599e144f2a1f..5a0452e3cd2d5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -959,6 +959,19 @@ export class DebugService implements IDebugService { await this.sendDataBreakpoints(); } + async addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): Promise { + this.model.addInstructionBreakpoint(address, offset, condition, hitCondition); + this.debugStorage.storeBreakpoints(this.model); + await this.sendInstructionBreakpoints(); + this.debugStorage.storeBreakpoints(this.model); + } + + async removeInstructionBreakpoints(id?: string): Promise { + this.model.removeInstructionBreakpoints(id); + this.debugStorage.storeBreakpoints(this.model); + await this.sendInstructionBreakpoints(); + } + setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void { this.model.setExceptionBreakpoints(data); this.debugStorage.storeBreakpoints(this.model); @@ -1003,6 +1016,16 @@ export class DebugService implements IDebugService { }); } + private async sendInstructionBreakpoints(session?: IDebugSession): Promise { + const breakpointsToSend = this.model.getInstructionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); + + await sendToOneOrAllSessions(this.model, session, async s => { + if (s.capabilities.supportsDataBreakpoints) { + await s.sendInstructionBreakpoints(breakpointsToSend); + } + }); + } + private sendExceptionBreakpoints(session?: IDebugSession): Promise { const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 058ab97e71106..5ffdc5473f049 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -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'; @@ -447,6 +447,23 @@ export class DebugSession implements IDebugSession { } } + async sendInstructionBreakpoints(instructionBreakpoints: IInstructionBreakpoint[]): Promise { + 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(); + 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 { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'breakpoints locations')); @@ -536,36 +553,36 @@ export class DebugSession implements IDebugSession { await this.raw.restartFrame({ frameId }, threadId); } - async next(threadId: number): Promise { + async next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'next')); } - await this.raw.next({ threadId }); + await this.raw.next({ threadId, granularity }); } - async stepIn(threadId: number, targetId?: number): Promise { + async stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepIn')); } - await this.raw.stepIn({ threadId, targetId }); + await this.raw.stepIn({ threadId, targetId, granularity }); } - async stepOut(threadId: number): Promise { + async stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepOut')); } - await this.raw.stepOut({ threadId }); + await this.raw.stepOut({ threadId, granularity }); } - async stepBack(threadId: number): Promise { + async stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepBack')); } - await this.raw.stepBack({ threadId }); + await this.raw.stepBack({ threadId, granularity }); } async continue(threadId: number): Promise { @@ -686,6 +703,14 @@ export class DebugSession implements IDebugSession { return this.raw.cancel({ progressId }); } + async disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + if (!this.raw) { + return Promise.reject(new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'disassemble'))); + } + + return this.raw.disassemble({ memoryReference, offset, instructionOffset, instructionCount, resolveSymbols: true }); + } + //---- threads getThread(threadId: number): Thread | undefined { diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 125cc091e2e39..a3533aee1ed28 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -497,6 +497,22 @@ export class RawDebugSession implements IDisposable { return Promise.reject(new Error('goto is not supported')); } + async setInstructionBreakpoints(args: DebugProtocol.SetInstructionBreakpointsArguments): Promise { + if (this.capabilities.supportsDisassembleRequest) { + return await this.send('setInstructionBreakpoints', args); + } + + return Promise.reject(new Error('setInstructionBreakpoints is not supported')); + } + + async disassemble(args: DebugProtocol.DisassembleArguments): Promise { + if (this.capabilities.supportsDisassembleRequest) { + return await this.send('disassemble', args); + } + + return Promise.reject(new Error('disassemble is not supported')); + } + cancel(args: DebugProtocol.CancelArguments): Promise { return this.send('cancel', args); } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 00dd54bb18130..1c2a9efdecf55 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -255,6 +255,7 @@ export interface IDebugSession extends ITreeElement { sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise; dataBreakpointInfo(name: string, variablesReference?: number): Promise; sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise; + sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise; sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; breakpointsLocations(uri: uri, lineNumber: number): Promise; getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined; @@ -433,6 +434,11 @@ export interface IDataBreakpoint extends IBaseBreakpoint { readonly accessType: DebugProtocol.DataBreakpointAccessType; } +export interface IInstructionBreakpoint extends IBaseBreakpoint { + readonly instructionReference: string; + readonly offset?: number; +} + export interface IExceptionInfo { readonly id?: string; readonly description?: string; @@ -493,9 +499,9 @@ export interface IDebugModel extends ITreeElement { * An event describing a change to the set of [breakpoints](#debug.Breakpoint). */ export interface IBreakpointsChangeEvent { - added?: Array; - removed?: Array; - changed?: Array; + added?: Array; + removed?: Array; + changed?: Array; sessionOnly: boolean; } @@ -887,6 +893,17 @@ export interface IDebugService { */ removeDataBreakpoints(id?: string): Promise; + /** + * Adds a new instruction breakpoint. + */ + addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): Promise; + + /** + * Removes all instruction breakpoints. If id is passed only removes the instruction breakpoint with the passed id. + * Notifies debug adapter of breakpoint changes. + */ + removeInstructionBreakpoints(id?: string): Promise; + setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise; setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index cf63563b1976e..4d9f9ba9f72c8 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -14,7 +14,7 @@ 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'; @@ -568,6 +568,7 @@ interface IBreakpointSessionData extends DebugProtocol.Breakpoint { supportsLogPoints: boolean; supportsFunctionBreakpoints: boolean; supportsDataBreakpoints: boolean; + supportsInstructionBreakpoints: boolean sessionId: string; } @@ -577,7 +578,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 +763,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 +909,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 +965,7 @@ export class DebugModel implements IDebugModel { private exceptionBreakpoints: ExceptionBreakpoint[]; private dataBreakopints: DataBreakpoint[]; private watchExpressions: Expression[]; + private instructionBreakpoints: InstructionBreakpoint[]; constructor( debugStorage: DebugStorage, @@ -940,6 +977,7 @@ export class DebugModel implements IDebugModel { this.exceptionBreakpoints = debugStorage.loadExceptionBreakpoints(); this.dataBreakopints = debugStorage.loadDataBreakpoints(); this.watchExpressions = debugStorage.loadWatchExpressions(); + this.instructionBreakpoints = []; this.sessions = []; } @@ -1095,6 +1133,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 +1239,16 @@ export class DebugModel implements IDebugModel { } } }); + this.instructionBreakpoints.forEach(ibp => { + if (!data) { + ibp.setSessionData(sessionId, undefined); + } else { + const dbpData = data.get(ibp.getId()); + if (dbpData) { + ibp.setSessionData(sessionId, toBreakpointSessionData(dbpData, capabilites)); + } + } + }); this._onDidChangeBreakpoints.fire({ sessionOnly: true @@ -1245,7 +1297,7 @@ export class DebugModel implements IDebugModel { } enableOrDisableAllBreakpoints(enable: boolean): void { - const changed: Array = []; + const changed: Array = []; this.breakpoints.forEach(bp => { if (bp.enabled !== enable) { @@ -1265,6 +1317,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 +1385,24 @@ export class DebugModel implements IDebugModel { this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); } + addInstructionBreakpoint(instructionReference: string, offset: number, condition?: string, hitCondition?: string): void { + const newInstructionBreakpoint = new InstructionBreakpoint(instructionReference, offset, false, true, hitCondition, condition, undefined); + this.instructionBreakpoints.push(newInstructionBreakpoint); + this._onDidChangeBreakpoints.fire({ added: [newInstructionBreakpoint], sessionOnly: true }); + } + + removeInstructionBreakpoints(id?: string): void { + let removed: InstructionBreakpoint[]; + if (id) { + removed = this.instructionBreakpoints.filter(fbp => fbp.getId() === id); + this.instructionBreakpoints = this.instructionBreakpoints.filter(fbp => fbp.getId() !== id); + } else { + removed = this.instructionBreakpoints; + this.instructionBreakpoints = []; + } + this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); + } + getWatchExpressions(): Expression[] { return this.watchExpressions; } diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index ef687d5cbdf1e..42698b296de56 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -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 { + throw new Error('Method not implemented.'); + } + + removeInstructionBreakpoints(address?: string): Promise { + throw new Error('Method not implemented.'); + } + setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string): Promise { throw new Error('Method not implemented.'); } @@ -323,6 +331,9 @@ export class MockSession implements IDebugSession { sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise { throw new Error('Method not implemented.'); } + sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise { + throw new Error('Method not implemented.'); + } getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined { throw new Error('Method not implemented.'); } From 0e7e274dd15e15ce3760235cb0a28f53aa391ab7 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Mon, 7 Jun 2021 15:52:32 -0700 Subject: [PATCH 04/49] Infinite scrolling --- .../contrib/debug/browser/disassemblyView.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 8a267ad2bf60c..b481fd8d334e8 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -27,6 +27,8 @@ interface IDisassembledInstructionEntry { export class DisassemblyView extends EditorPane { + private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50; + private _fontInfo: BareFontInfo; private _disassembledInstructions: WorkbenchTable | null; @@ -96,7 +98,18 @@ export class DisassemblyView extends EditorPane { } )) as WorkbenchTable; - this.loadDisassembledInstructions('0x00005000'); + this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2, '0x00005000'); + this._disassembledInstructions.reveal(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, 0.5); + + 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.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, '0x00004000'); + this._disassembledInstructions!.reveal(topElement, 0); + } else if (e.oldScrollTop < e.scrollTop && e.scrollTop + e.height > e.scrollHeight - e.height) { + this.loadDisassembledInstructions(this._disassembledInstructions!.length, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, '0x00004000'); + } + }); } layout(dimension: Dimension): void { @@ -105,14 +118,14 @@ export class DisassemblyView extends EditorPane { } } - private loadDisassembledInstructions(address: string): void { + private loadDisassembledInstructions(index: number, numInstructions: number, address: string): void { const newEntries: IDisassembledInstructionEntry[] = []; - for (let i = 0; i < 50; i++) { - newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: { address, instruction: 'instruction instruction, instruction' } }); + for (let i = 0; i < numInstructions; i++) { + newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: { address: `${address}${i}`, instruction: 'instruction instruction, instruction' } }); } if (this._disassembledInstructions) { // TODO: append/insert - this._disassembledInstructions.splice(0, this._disassembledInstructions.length, newEntries); + this._disassembledInstructions.splice(index, 0, newEntries); } } From f4015e15c8f29b2f63ee9d2988e6e39b897aad5a Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 10 Jun 2021 11:27:09 -0700 Subject: [PATCH 05/49] Breakpoints work, but can still be improved --- .../contrib/debug/browser/disassemblyView.ts | 65 +++++++++++++++++-- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index b481fd8d334e8..46d7923eadd6f 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -14,10 +14,11 @@ 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 { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DISASSEMBLY_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; interface IDisassembledInstructionEntry { allowBreakpoint: boolean; @@ -132,7 +133,8 @@ export class DisassemblyView extends EditorPane { } interface IBreakpointColumnTemplateData { - icon: HTMLImageElement + container: HTMLElement, + icon: HTMLElement | undefined } class BreakpointRenderer implements ITableRenderer { @@ -142,16 +144,65 @@ class BreakpointRenderer implements ITableRenderer('img.icon')); - return { icon }; + return { container, icon: undefined }; } renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void { - if (element.isBreakpointSet) { - // TODO: breakpoint icon + // TODO: see getBreakpointMessageAndIcon in vs\workbench\contrib\debug\browser\breakpointEditorContribution.ts + // for more types of breakpoint icons + if (element.allowBreakpoint) { + if (element.isBreakpointSet) { + templateData.container.className = ThemeIcon.asClassName(icons.breakpoint.regular); + } else { + templateData.container.onmouseover = () => { + if (!templateData.icon) { + templateData.icon = append(templateData.container, $('.icon')); + } + templateData.icon.className = ThemeIcon.asClassName(icons.debugBreakpointHint); + }; + templateData.container.onmouseout = () => { + if (templateData.icon) { + templateData.container.removeChild(templateData.icon); + templateData.icon = undefined; + } + }; + } + + templateData.container.onclick = () => { + if (element.isBreakpointSet) { + element.isBreakpointSet = false; + if (templateData.icon) { + templateData.container.removeChild(templateData.icon); + templateData.icon = undefined; + } + templateData.container.onmouseover = () => { + templateData.icon = append(templateData.container, $('.icon')); + templateData.icon.className = ThemeIcon.asClassName(icons.debugBreakpointHint); + }; + templateData.container.onmouseout = () => { + if (templateData.icon) { + templateData.container.removeChild(templateData.icon); + templateData.icon = undefined; + } + }; + } else if (element.allowBreakpoint) { + element.isBreakpointSet = true; + if (!templateData.icon) { + templateData.icon = append(templateData.container, $('.icon')); + } + templateData.icon.className = ThemeIcon.asClassName(icons.breakpoint.regular); + templateData.container.onmouseover = null; + templateData.container.onmouseout = null; + } + }; } } - disposeTemplate(templateData: IBreakpointColumnTemplateData): void { } + + disposeTemplate(templateData: IBreakpointColumnTemplateData): void { + templateData.container.onclick = null; + templateData.container.onmouseover = null; + templateData.container.onmouseout = null; + } } From 01dd48e6e2d1df6dcf0d52707320adcd423697d7 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 10 Jun 2021 17:24:14 -0700 Subject: [PATCH 06/49] Improve breakpoints UI --- .../contrib/debug/browser/disassemblyView.ts | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 46d7923eadd6f..6eb3dea4c41f9 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -14,7 +14,7 @@ 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, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DISASSEMBLY_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; @@ -134,7 +134,7 @@ export class DisassemblyView extends EditorPane { interface IBreakpointColumnTemplateData { container: HTMLElement, - icon: HTMLElement | undefined + icon: HTMLElement } class BreakpointRenderer implements ITableRenderer { @@ -144,55 +144,37 @@ class BreakpointRenderer implements ITableRenderer { - if (!templateData.icon) { - templateData.icon = append(templateData.container, $('.icon')); - } - templateData.icon.className = ThemeIcon.asClassName(icons.debugBreakpointHint); - }; - templateData.container.onmouseout = () => { - if (templateData.icon) { - templateData.container.removeChild(templateData.icon); - templateData.icon = undefined; - } - }; + templateData.icon.classList.add(breakpointIcon); } + templateData.container.onmouseover = () => { + templateData.icon.classList.add(breakpointHintIcon); + }; + + templateData.container.onmouseout = () => { + templateData.icon.classList.remove(breakpointHintIcon); + }; + templateData.container.onclick = () => { if (element.isBreakpointSet) { element.isBreakpointSet = false; - if (templateData.icon) { - templateData.container.removeChild(templateData.icon); - templateData.icon = undefined; - } - templateData.container.onmouseover = () => { - templateData.icon = append(templateData.container, $('.icon')); - templateData.icon.className = ThemeIcon.asClassName(icons.debugBreakpointHint); - }; - templateData.container.onmouseout = () => { - if (templateData.icon) { - templateData.container.removeChild(templateData.icon); - templateData.icon = undefined; - } - }; + templateData.icon.classList.remove(breakpointIcon); } else if (element.allowBreakpoint) { element.isBreakpointSet = true; - if (!templateData.icon) { - templateData.icon = append(templateData.container, $('.icon')); - } - templateData.icon.className = ThemeIcon.asClassName(icons.breakpoint.regular); - templateData.container.onmouseover = null; - templateData.container.onmouseout = null; + templateData.icon.classList.add(breakpointIcon); } }; } From fc3e9cd0ed4c6e9a6c0e426b83163012e608559a Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Wed, 9 Jun 2021 17:42:08 -0700 Subject: [PATCH 07/49] Add instruction to viewer --- .../debug/browser/debugEditorActions.ts | 4 +- .../contrib/debug/browser/debugService.ts | 4 ++ .../contrib/debug/browser/debugSession.ts | 5 ++- .../contrib/debug/browser/disassemblyView.ts | 41 ++++++++++++------- .../workbench/contrib/debug/common/debug.ts | 2 + .../contrib/debug/common/debugModel.ts | 5 ++- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index e03d2f378f780..24b87629351e9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -144,7 +144,9 @@ class GoToDisassemblyAction extends EditorAction { id: GoToDisassemblyAction.ID, label: GoToDisassemblyAction.LABEL, alias: 'Debug: Go to Disassembly', - precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), + // precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), + precondition: ContextKeyExpr.and(PanelFocusContext.toNegated(), EditorContextKeys.editorTextFocus), + contextMenuOpts: { group: 'debug', order: 5 diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 5a0452e3cd2d5..b3a635b5ff92c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -991,6 +991,10 @@ export class DebugService implements IDebugService { await this.sendExceptionBreakpoints(session); } + async getInstructions(session: IDebugSession, memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + session.disassemble(memoryReference, offset, instructionOffset, instructionCount); + } + private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { const breakpointsToSend = this.model.getBreakpoints({ uri: modelUri, enabledOnly: true }); await sendToOneOrAllSessions(this.model, session, s => s.sendBreakpoints(modelUri, breakpointsToSend, sourceModified)); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 5ffdc5473f049..9c6654c01e41e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -703,12 +703,13 @@ export class DebugSession implements IDebugSession { return this.raw.cancel({ progressId }); } - async disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + async disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { if (!this.raw) { return Promise.reject(new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'disassemble'))); } - return this.raw.disassemble({ memoryReference, offset, instructionOffset, instructionCount, resolveSymbols: true }); + const response = await this.raw.disassemble({ memoryReference, offset, instructionOffset, instructionCount, resolveSymbols: true }); + return response?.body?.instructions; } //---- threads diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 6eb3dea4c41f9..869447faafa7e 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -17,7 +17,7 @@ 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 { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { DISASSEMBLY_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { DISASSEMBLY_VIEW_ID, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; interface IDisassembledInstructionEntry { @@ -32,16 +32,18 @@ export class DisassemblyView extends EditorPane { private _fontInfo: BareFontInfo; private _disassembledInstructions: WorkbenchTable | null; - + private _debugService: IDebugService; constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IDebugService debugService: IDebugService ) { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); + this._debugService = debugService; this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); configurationService.onDidChangeConfiguration(e => { @@ -99,16 +101,16 @@ export class DisassemblyView extends EditorPane { } )) as WorkbenchTable; - this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2, '0x00005000'); + this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2); this._disassembledInstructions.reveal(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, 0.5); 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.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, '0x00004000'); + this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); this._disassembledInstructions!.reveal(topElement, 0); } else if (e.oldScrollTop < e.scrollTop && e.scrollTop + e.height > e.scrollHeight - e.height) { - this.loadDisassembledInstructions(this._disassembledInstructions!.length, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, '0x00004000'); + this.loadDisassembledInstructions(this._disassembledInstructions!.length, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); } }); } @@ -119,17 +121,26 @@ export class DisassemblyView extends EditorPane { } } - private loadDisassembledInstructions(index: number, numInstructions: number, address: string): void { - const newEntries: IDisassembledInstructionEntry[] = []; - for (let i = 0; i < numInstructions; i++) { - newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: { address: `${address}${i}`, instruction: 'instruction instruction, instruction' } }); - } - if (this._disassembledInstructions) { - // TODO: append/insert - this._disassembledInstructions.splice(index, 0, newEntries); + private loadDisassembledInstructions(offset: number, instructionCount: number): void { + const session = this._debugService.getViewModel().focusedSession; + const frame = this._debugService.getViewModel().focusedStackFrame; + const instructionAddress = frame?.instructionPointerReference!; + + const resultEntries = await session?.disassemble(instructionAddress, offset, 0, instructionCount); + + if (resultEntries) { + const newEntries: IDisassembledInstructionEntry[] = []; + + for (let i = 0; i < resultEntries.length; i++) { + newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: resultEntries[i] }); + } + + if (this._disassembledInstructions) { + // TODO: append/insert + this._disassembledInstructions.splice(0, this._disassembledInstructions.length, newEntries); + } } } - } interface IBreakpointColumnTemplateData { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 1c2a9efdecf55..904aefba1555e 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -267,6 +267,7 @@ export interface IDebugSession extends ITreeElement { evaluate(expression: string, frameId?: number, context?: string): Promise; customRequest(request: string, args: any): Promise; cancel(progressId: string): Promise; + disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise; restartFrame(frameId: number, threadId: number): Promise; next(threadId: number): Promise; @@ -365,6 +366,7 @@ export interface IStackFrame extends ITreeElement { readonly range: IRange; readonly source: Source; readonly canRestart: boolean; + readonly instructionPointerReference?: string; getScopes(): Promise; getMostSpecificScopes(range: IRange): Promise>; forgetScopes(): void; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 4d9f9ba9f72c8..dcebbebb6f42e 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -328,7 +328,8 @@ export class StackFrame implements IStackFrame { public presentationHint: string | undefined, public range: IRange, private index: number, - public canRestart: boolean + public canRestart: boolean, + public instructionPointerReference?: string ) { } getId(): string { @@ -491,7 +492,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) { From b55f201becf448568f108ceeb4707269a994c0d9 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Fri, 11 Jun 2021 01:56:43 -0700 Subject: [PATCH 08/49] Add debug to view. --- .../contrib/debug/browser/debugService.ts | 15 ++++++- .../contrib/debug/browser/disassemblyView.ts | 39 ++++++++++++------- .../workbench/contrib/debug/common/debug.ts | 3 ++ .../contrib/debug/test/browser/mockDebug.ts | 7 ++++ 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index b3a635b5ff92c..65b5b2dcf66e5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -991,8 +991,19 @@ export class DebugService implements IDebugService { await this.sendExceptionBreakpoints(session); } - async getInstructions(session: IDebugSession, memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { - session.disassemble(memoryReference, offset, instructionOffset, instructionCount); + async getDisassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + return new Promise((resolve, reject) => { + const result: DebugProtocol.DisassembledInstruction[] = []; + + for (let i = 0; i < instructionCount; i++) { + result.push({ address: `${memoryReference}+${i + offset}`, instruction: `instruction1, instruction2, instruction3` }); + } + + resolve(result); + }); + + // const session = this.getViewModel().focusedStackFrame + // session.disassemble(memoryReference, offset, instructionOffset, instructionCount); } private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 869447faafa7e..fbef7893ea61d 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -33,6 +33,8 @@ export class DisassemblyView extends EditorPane { private _fontInfo: BareFontInfo; private _disassembledInstructions: WorkbenchTable | null; private _debugService: IDebugService; + // private _currentAddress: string | undefined; + private _lastOffset: number = 0; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -101,14 +103,18 @@ export class DisassemblyView extends EditorPane { } )) as WorkbenchTable; - this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2); - this._disassembledInstructions.reveal(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, 0.5); + this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; + this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => + this._disassembledInstructions?.reveal(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, 0.5) + ); 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.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); - this._disassembledInstructions!.reveal(topElement, 0); + this._lastOffset -= DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; + this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => + this._disassembledInstructions!.reveal(topElement, 0) + ); } else if (e.oldScrollTop < e.scrollTop && e.scrollTop + e.height > e.scrollHeight - e.height) { this.loadDisassembledInstructions(this._disassembledInstructions!.length, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); } @@ -121,23 +127,28 @@ export class DisassemblyView extends EditorPane { } } - private loadDisassembledInstructions(offset: number, instructionCount: number): void { - const session = this._debugService.getViewModel().focusedSession; - const frame = this._debugService.getViewModel().focusedStackFrame; - const instructionAddress = frame?.instructionPointerReference!; + private async loadDisassembledInstructions(offset: number, instructionCount: number): Promise { + /* if (!this._currentAddress) { + const frame = this._debugService.getViewModel().focusedStackFrame; + this._currentAddress = frame?.instructionPointerReference!; + } */ - const resultEntries = await session?.disassemble(instructionAddress, offset, 0, instructionCount); - - if (resultEntries) { + // const session = this._debugService.getViewModel().focusedSession; + // session?.disassemble(this._currentAddress, offset, 0, instructionCount) + const resultEntries = await this._debugService.getDisassemble('0x12345678', offset, 0, instructionCount); + if (resultEntries && this._disassembledInstructions) { const newEntries: IDisassembledInstructionEntry[] = []; for (let i = 0; i < resultEntries.length; i++) { newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: resultEntries[i] }); } - if (this._disassembledInstructions) { - // TODO: append/insert - this._disassembledInstructions.splice(0, this._disassembledInstructions.length, newEntries); + // request is either at the end or start + if (offset >= 0) { + this._disassembledInstructions.splice(offset, 0, newEntries); + } + else { + this._disassembledInstructions.splice(0, 0, newEntries); } } } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 904aefba1555e..6cc7054226f06 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -961,6 +961,8 @@ export interface IDebugService { */ sourceIsNotAvailable(uri: uri): void; + getDisassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise; + /** * Gets the current debug model. */ @@ -970,6 +972,7 @@ export interface IDebugService { * Gets the current view model. */ getViewModel(): IViewModel; + } // Editor interfaces diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 42698b296de56..a927e52d862fa 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -157,6 +157,10 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + getDisassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + throw new Error('not implemented'); + } + logToRepl(session: IDebugSession, value: string): void { } sourceIsNotAvailable(uri: uri): void { } @@ -388,6 +392,9 @@ export class MockSession implements IDebugSession { loadSource(resource: uri): Promise { throw new Error('Method not implemented.'); } + disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + throw new Error('Method not implemented.'); + } terminate(restart = false): Promise { throw new Error('Method not implemented.'); From 16c268e17a80430a7e62148913dac959748242e3 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Sat, 12 Jun 2021 11:50:25 -0700 Subject: [PATCH 09/49] Fix scrolling --- .../contrib/debug/browser/disassemblyView.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index fbef7893ea61d..8daeec125a8fc 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -103,10 +103,13 @@ export class DisassemblyView extends EditorPane { } )) as WorkbenchTable; - this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; - this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => - this._disassembledInstructions?.reveal(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, 0.5) - ); + this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 0.5; + this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => { + // on load, set the target instruction in the middle of the page. + if (this._disassembledInstructions!.length > 0) { + this._disassembledInstructions?.reveal(this._disassembledInstructions!.length * 0.5, 0.5); + } + }); this._disassembledInstructions.onDidScroll(e => { if (e.oldScrollTop > e.scrollTop && e.scrollTop < e.height) { @@ -116,7 +119,7 @@ export class DisassemblyView extends EditorPane { this._disassembledInstructions!.reveal(topElement, 0) ); } else if (e.oldScrollTop < e.scrollTop && e.scrollTop + e.height > e.scrollHeight - e.height) { - this.loadDisassembledInstructions(this._disassembledInstructions!.length, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); + this.loadDisassembledInstructions(this._disassembledInstructions!.length + this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); } }); } @@ -128,13 +131,6 @@ export class DisassemblyView extends EditorPane { } private async loadDisassembledInstructions(offset: number, instructionCount: number): Promise { - /* if (!this._currentAddress) { - const frame = this._debugService.getViewModel().focusedStackFrame; - this._currentAddress = frame?.instructionPointerReference!; - } */ - - // const session = this._debugService.getViewModel().focusedSession; - // session?.disassemble(this._currentAddress, offset, 0, instructionCount) const resultEntries = await this._debugService.getDisassemble('0x12345678', offset, 0, instructionCount); if (resultEntries && this._disassembledInstructions) { const newEntries: IDisassembledInstructionEntry[] = []; @@ -143,9 +139,9 @@ export class DisassemblyView extends EditorPane { newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: resultEntries[i] }); } - // request is either at the end or start + // request is either at the start or end if (offset >= 0) { - this._disassembledInstructions.splice(offset, 0, newEntries); + this._disassembledInstructions.splice(this._disassembledInstructions.length, 0, newEntries); } else { this._disassembledInstructions.splice(0, 0, newEntries); From 82739729dfc9206bbc09679b45d05fc13cbb9b05 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Sun, 13 Jun 2021 00:34:25 -0700 Subject: [PATCH 10/49] Re-enable disassemble. --- .../contrib/debug/browser/debugService.ts | 15 ++++++++++----- .../contrib/debug/browser/disassemblyView.ts | 4 +++- src/vs/workbench/contrib/debug/common/debug.ts | 2 +- .../contrib/debug/test/browser/mockDebug.ts | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 65b5b2dcf66e5..de17f037a7199 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -991,19 +991,24 @@ export class DebugService implements IDebugService { await this.sendExceptionBreakpoints(session); } - async getDisassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + async getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise { + const session = this.getViewModel().focusedSession; + if (session) { + const frame = this.getViewModel().focusedStackFrame; + if (frame?.instructionPointerReference) { + return session.disassemble(frame?.instructionPointerReference, offset, instructionOffset, instructionCount); + } + } + return new Promise((resolve, reject) => { const result: DebugProtocol.DisassembledInstruction[] = []; for (let i = 0; i < instructionCount; i++) { - result.push({ address: `${memoryReference}+${i + offset}`, instruction: `instruction1, instruction2, instruction3` }); + result.push({ address: `${i + offset}`, instruction: `debugger is not availible.` }); } resolve(result); }); - - // const session = this.getViewModel().focusedStackFrame - // session.disassemble(memoryReference, offset, instructionOffset, instructionCount); } private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 8daeec125a8fc..913343b912a30 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -56,6 +56,8 @@ export class DisassemblyView extends EditorPane { }); this._disassembledInstructions = null; + + } protected createEditor(parent: HTMLElement): void { @@ -131,7 +133,7 @@ export class DisassemblyView extends EditorPane { } private async loadDisassembledInstructions(offset: number, instructionCount: number): Promise { - const resultEntries = await this._debugService.getDisassemble('0x12345678', offset, 0, instructionCount); + const resultEntries = await this._debugService.getDisassemble(offset, 0, instructionCount); if (resultEntries && this._disassembledInstructions) { const newEntries: IDisassembledInstructionEntry[] = []; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 6cc7054226f06..40a88a628d754 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -961,7 +961,7 @@ export interface IDebugService { */ sourceIsNotAvailable(uri: uri): void; - getDisassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise; + getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise; /** * Gets the current debug model. diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index a927e52d862fa..acafa0731b502 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -157,7 +157,7 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } - getDisassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise { + getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise { throw new Error('not implemented'); } From 17ffdc71da1f67750167423d43f34a06fc50a092 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Mon, 14 Jun 2021 17:30:25 -0700 Subject: [PATCH 11/49] Some fixes and resolving some comments --- .../debug/browser/debug.contribution.ts | 2 +- .../debug/browser/debugEditorActions.ts | 16 +++++------ .../contrib/debug/browser/disassemblyView.ts | 27 ++++++++++--------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 8861d16e41f4c..b441011670dbc 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -376,7 +376,7 @@ 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 disassmelby editor +// Register disassmebly editor Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), [new SyncDescriptor(DisassemblyViewInput)] diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 24b87629351e9..d64a1f5fdff01 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -134,19 +134,17 @@ class LogPointAction extends EditorAction2 { } } -class GoToDisassemblyAction extends EditorAction { +class OpenDisassemblyViewAction extends EditorAction { - public static readonly ID = 'editor.debug.action.goToDisassembly'; - public static readonly LABEL = nls.localize('goToDisassembly', "Go to Disassembly"); + public static readonly ID = 'editor.debug.action.openDisassemblyView'; + public static readonly LABEL = nls.localize('openDisassemblyView', "Open Disassembly View"); constructor() { super({ - id: GoToDisassemblyAction.ID, - label: GoToDisassemblyAction.LABEL, + 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), - precondition: ContextKeyExpr.and(PanelFocusContext.toNegated(), EditorContextKeys.editorTextFocus), - + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), contextMenuOpts: { group: 'debug', order: 5 @@ -534,7 +532,7 @@ class CloseExceptionWidgetAction extends EditorAction { registerAction2(ToggleBreakpointAction); registerAction2(ConditionalBreakpointAction); registerAction2(LogPointAction); -registerEditorAction(GoToDisassemblyAction); +registerEditorAction(OpenDisassemblyViewAction); registerEditorAction(RunToCursorAction); registerEditorAction(StepIntoTargetsAction); registerEditorAction(SelectionToReplAction); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 913343b912a30..c1b5b7586d435 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -31,7 +31,7 @@ export class DisassemblyView extends EditorPane { private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50; private _fontInfo: BareFontInfo; - private _disassembledInstructions: WorkbenchTable | null; + private _disassembledInstructions: WorkbenchTable | undefined; private _debugService: IDebugService; // private _currentAddress: string | undefined; private _lastOffset: number = 0; @@ -48,16 +48,14 @@ export class DisassemblyView extends EditorPane { this._debugService = debugService; this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); - configurationService.onDidChangeConfiguration(e => { + this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor')) { this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); this._disassembledInstructions?.rerender(); } - }); - - this._disassembledInstructions = null; - + })); + this._disassembledInstructions = undefined; } protected createEditor(parent: HTMLElement): void { @@ -102,18 +100,19 @@ export class DisassemblyView extends EditorPane { multipleSelectionSupport: false, setRowLineHeight: false, openOnSingleClick: false, + // TODO: accessibilityProvider } )) as WorkbenchTable; - this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 0.5; - this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => { + this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; + this.loadDisassembledInstructions(this._lastOffset, 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(this._disassembledInstructions!.length * 0.5, 0.5); + this._disassembledInstructions?.reveal(Math.floor(this._disassembledInstructions!.length / 2), 0.5); } }); - this._disassembledInstructions.onDidScroll(e => { + 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._lastOffset -= DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; @@ -123,7 +122,7 @@ export class DisassemblyView extends EditorPane { } else if (e.oldScrollTop < e.scrollTop && e.scrollTop + e.height > e.scrollHeight - e.height) { this.loadDisassembledInstructions(this._disassembledInstructions!.length + this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); } - }); + })); } layout(dimension: Dimension): void { @@ -133,7 +132,7 @@ export class DisassemblyView extends EditorPane { } private async loadDisassembledInstructions(offset: number, instructionCount: number): Promise { - const resultEntries = await this._debugService.getDisassemble(offset, 0, instructionCount); + const resultEntries = await this._debugService.getDisassemble(0, offset, instructionCount); if (resultEntries && this._disassembledInstructions) { const newEntries: IDisassembledInstructionEntry[] = []; @@ -170,6 +169,10 @@ class BreakpointRenderer implements ITableRenderer Date: Tue, 15 Jun 2021 16:49:47 -0700 Subject: [PATCH 12/49] Style changes --- .../contrib/debug/browser/disassemblyView.ts | 71 ++++++++++++++++--- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index c1b5b7586d435..49b72e3accb6a 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -19,6 +19,7 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DISASSEMBLY_VIEW_ID, IDebugService } 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'; interface IDisassembledInstructionEntry { allowBreakpoint: boolean; @@ -30,7 +31,9 @@ export class DisassemblyView extends EditorPane { private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50; - private _fontInfo: BareFontInfo; + // Used in instruction renderer + fontInfo: BareFontInfo; + private _disassembledInstructions: WorkbenchTable | undefined; private _debugService: IDebugService; // private _currentAddress: string | undefined; @@ -47,10 +50,10 @@ export class DisassemblyView extends EditorPane { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); this._debugService = debugService; - this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); + 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.fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); this._disassembledInstructions?.rerender(); } })); @@ -59,7 +62,7 @@ export class DisassemblyView extends EditorPane { } protected createEditor(parent: HTMLElement): void { - const lineHeight = this._fontInfo.lineHeight; + const lineHeight = this.fontInfo.lineHeight; const delegate = new class implements ITableVirtualDelegate{ headerRowHeight: number = 0; // No header getHeight(row: IDisassembledInstructionEntry): number { @@ -74,8 +77,8 @@ export class DisassemblyView extends EditorPane { label: '', tooltip: '', weight: 0, - minimumWidth: 40, - maximumWidth: 40, + minimumWidth: this.fontInfo.lineHeight, + maximumWidth: this.fontInfo.lineHeight, templateId: BreakpointRenderer.TEMPLATE_ID, project(row: IDisassembledInstructionEntry): IDisassembledInstructionEntry { return row; } }, @@ -89,11 +92,11 @@ export class DisassemblyView extends EditorPane { ], [ this._instantiationService.createInstance(BreakpointRenderer), - this._instantiationService.createInstance(InstructionRenderer), + this._instantiationService.createInstance(InstructionRenderer, this), ], { identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address }, - horizontalScrolling: false, + horizontalScrolling: true, overrideStyles: { listBackground: editorBackground }, @@ -114,7 +117,7 @@ export class DisassemblyView extends EditorPane { 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; + const topElement = Math.floor(e.scrollTop / this.fontInfo.lineHeight) + DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; this._lastOffset -= DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => this._disassembledInstructions!.reveal(topElement, 0) @@ -165,6 +168,11 @@ class BreakpointRenderer implements ITableRenderer { + private static readonly INSTRUCTION_ADDR_MIN_LENGTH = 25; + private static readonly INSTRUCTION_BYTES_MIN_LENGTH = 30; + static readonly TEMPLATE_ID = 'instruction'; templateId: string = InstructionRenderer.TEMPLATE_ID; + constructor(private readonly disassemblyView: DisassemblyView) { + } + renderTemplate(container: HTMLElement): IInstructionColumnTemplateData { - const instruction = append(container, $('instruction')); + const instruction = append(container, $('.instruction')); + this.applyFontInfo(instruction); return { instruction }; } renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void { const instruction = element.instruction; - templateData.instruction.innerText = `${instruction.address}\t${instruction.instructionBytes}\t${instruction.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; } 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'; + } + } export class DisassemblyViewInput extends EditorInput { From d2dc7423d5df1c4a314ae610f361b791ef8c3a6e Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Tue, 15 Jun 2021 15:51:18 -0700 Subject: [PATCH 13/49] Switch to using Address + Count for offset. --- .../contrib/debug/browser/disassemblyView.ts | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 49b72e3accb6a..3d9c3272a00f8 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -108,7 +108,7 @@ export class DisassemblyView extends EditorPane { )) as WorkbenchTable; this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; - this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2).then(() => { + this.loadDisassembledInstructions(undefined, this._lastOffset, 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); @@ -119,11 +119,13 @@ export class DisassemblyView extends EditorPane { if (e.oldScrollTop > e.scrollTop && e.scrollTop < e.height) { const topElement = Math.floor(e.scrollTop / this.fontInfo.lineHeight) + DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; this._lastOffset -= DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; - this.loadDisassembledInstructions(this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then(() => - this._disassembledInstructions!.reveal(topElement, 0) - ); + 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.loadDisassembledInstructions(this._disassembledInstructions!.length + this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); + this.scrollDown_LoadDisassembledInstructions(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); } })); } @@ -134,9 +136,28 @@ export class DisassemblyView extends EditorPane { } } - private async loadDisassembledInstructions(offset: number, instructionCount: number): Promise { - const resultEntries = await this._debugService.getDisassemble(0, offset, instructionCount); - if (resultEntries && this._disassembledInstructions) { + private async scrollUp_LoadDisassembledInstructions(instructionCount: number): Promise { + const address: string | undefined = this._disassembledInstructions?.row(0).instruction.address; + return this.loadDisassembledInstructions(address, instructionCount, instructionCount - 1); + } + + private async scrollDown_LoadDisassembledInstructions(instructionCount: number): Promise { + const address: string | undefined = this._disassembledInstructions?.row(this._disassembledInstructions?.length - 1).instruction.address; + return this.loadDisassembledInstructions(address, 1, instructionCount); + } + + private async loadDisassembledInstructions(address: string | undefined, instructionOffset: number, instructionCount: number): Promise { + // if address is null, then use current stackframe. + if (!address) { + const frame = this._debugService.getViewModel().focusedStackFrame; + if (frame?.instructionPointerReference) { + address = frame.instructionPointerReference; + } + } + + 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++) { @@ -144,13 +165,17 @@ export class DisassemblyView extends EditorPane { } // request is either at the start or end - if (offset >= 0) { + if (instructionOffset >= 0) { this._disassembledInstructions.splice(this._disassembledInstructions.length, 0, newEntries); } else { this._disassembledInstructions.splice(0, 0, newEntries); } + + return true; } + + return false; } } From 4b681e19b99810c3690a1663262c46d15aab05db Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Wed, 16 Jun 2021 00:46:59 -0700 Subject: [PATCH 14/49] Fix scroll up. --- .../contrib/debug/browser/disassemblyView.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 3d9c3272a00f8..92f04fdba2ed6 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -33,11 +33,10 @@ export class DisassemblyView extends EditorPane { // Used in instruction renderer fontInfo: BareFontInfo; + currentInstructionAddress: string = ''; private _disassembledInstructions: WorkbenchTable | undefined; private _debugService: IDebugService; - // private _currentAddress: string | undefined; - private _lastOffset: number = 0; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -91,7 +90,7 @@ export class DisassemblyView extends EditorPane { }, ], [ - this._instantiationService.createInstance(BreakpointRenderer), + this._instantiationService.createInstance(BreakpointRenderer, this), this._instantiationService.createInstance(InstructionRenderer, this), ], { @@ -107,8 +106,7 @@ export class DisassemblyView extends EditorPane { } )) as WorkbenchTable; - this._lastOffset = -DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; - this.loadDisassembledInstructions(undefined, this._lastOffset, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2).then(() => { + this.loadDisassembledInstructions(undefined, -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); @@ -118,7 +116,6 @@ export class DisassemblyView extends EditorPane { 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._lastOffset -= DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD; this.scrollUp_LoadDisassembledInstructions(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD).then((success) => { if (success) { this._disassembledInstructions!.reveal(topElement, 0); @@ -138,7 +135,7 @@ export class DisassemblyView extends EditorPane { private async scrollUp_LoadDisassembledInstructions(instructionCount: number): Promise { const address: string | undefined = this._disassembledInstructions?.row(0).instruction.address; - return this.loadDisassembledInstructions(address, instructionCount, instructionCount - 1); + return this.loadDisassembledInstructions(address, -instructionCount, instructionCount - 1); } private async scrollDown_LoadDisassembledInstructions(instructionCount: number): Promise { @@ -152,6 +149,7 @@ export class DisassemblyView extends EditorPane { const frame = this._debugService.getViewModel().focusedStackFrame; if (frame?.instructionPointerReference) { address = frame.instructionPointerReference; + this.currentInstructionAddress = address; } } @@ -187,9 +185,16 @@ interface IBreakpointColumnTemplateData { class BreakpointRenderer implements ITableRenderer { static readonly TEMPLATE_ID = 'breakpoint'; + private breakpointIcon = 'codicon-' + icons.breakpoint.regular.id; + private breakpointHintIcon = 'codicon-' + icons.debugBreakpointHint.id; + // private debugStackframe = 'codicon-' + icons.debugStackframe.id; + private debugStackframeFocused = 'codicon-' + icons.debugStackframeFocused.id; templateId: string = BreakpointRenderer.TEMPLATE_ID; + constructor(private readonly disassemblyView: DisassemblyView) { + } + renderTemplate(container: HTMLElement): IBreakpointColumnTemplateData { const icon = append(container, $('.disassembly-view')); icon.classList.add('codicon'); @@ -202,35 +207,32 @@ class BreakpointRenderer implements ITableRenderer { - templateData.icon.classList.add(breakpointHintIcon); + templateData.icon.classList.add(this.breakpointHintIcon); }; templateData.container.onmouseout = () => { - templateData.icon.classList.remove(breakpointHintIcon); + templateData.icon.classList.remove(this.breakpointHintIcon); }; templateData.container.onclick = () => { if (element.isBreakpointSet) { element.isBreakpointSet = false; - templateData.icon.classList.remove(breakpointIcon); + templateData.icon.classList.remove(this.breakpointIcon); } else if (element.allowBreakpoint) { element.isBreakpointSet = true; - templateData.icon.classList.add(breakpointIcon); + templateData.icon.classList.add(this.breakpointIcon); } }; } @@ -241,7 +243,6 @@ class BreakpointRenderer implements ITableRenderer Date: Wed, 16 Jun 2021 00:57:40 -0700 Subject: [PATCH 15/49] Fix BP/pos icon from appearing while scrolling. --- src/vs/workbench/contrib/debug/browser/disassemblyView.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 92f04fdba2ed6..27de965f96c3b 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -210,6 +210,9 @@ class BreakpointRenderer implements ITableRenderer { templateData.icon.classList.add(this.breakpointHintIcon); From 707ae1ae9e52fc5a3860e3c66e6dac1b8e4aece2 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Mon, 21 Jun 2021 16:58:44 -0700 Subject: [PATCH 16/49] Add set and remove InstructionBreakpoint. --- .../contrib/debug/browser/debugService.ts | 6 ++--- .../contrib/debug/browser/disassemblyView.ts | 24 ++++++++++++++----- .../workbench/contrib/debug/common/debug.ts | 5 ++-- .../contrib/debug/common/debugModel.ts | 16 ++++++------- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index de17f037a7199..339011bd24324 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -966,8 +966,8 @@ export class DebugService implements IDebugService { this.debugStorage.storeBreakpoints(this.model); } - async removeInstructionBreakpoints(id?: string): Promise { - this.model.removeInstructionBreakpoints(id); + async removeInstructionBreakpoints(address?: string): Promise { + this.model.removeInstructionBreakpoints(address); this.debugStorage.storeBreakpoints(this.model); await this.sendInstructionBreakpoints(); } @@ -1040,7 +1040,7 @@ export class DebugService implements IDebugService { const breakpointsToSend = this.model.getInstructionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); await sendToOneOrAllSessions(this.model, session, async s => { - if (s.capabilities.supportsDataBreakpoints) { + if (s.capabilities.supportsInstructionBreakpoints) { await s.sendInstructionBreakpoints(breakpointsToSend); } }); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 27de965f96c3b..df3367af8a1e3 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -69,6 +69,12 @@ export class DisassemblyView extends EditorPane { } }; + this._register(this._debugService.getViewModel().onDidFocusStackFrame((stackFrame) => { + this.currentInstructionAddress = stackFrame.stackFrame?.instructionPointerReference!; + + // TODO: rerender the marker + })); + this._disassembledInstructions = this._register(this._instantiationService.createInstance(WorkbenchTable, 'DisassemblyView', parent, delegate, [ @@ -192,7 +198,8 @@ class BreakpointRenderer implements ITableRenderer { if (element.isBreakpointSet) { - element.isBreakpointSet = false; - templateData.icon.classList.remove(this.breakpointIcon); - } else if (element.allowBreakpoint) { - element.isBreakpointSet = true; - templateData.icon.classList.add(this.breakpointIcon); + this.debugService.removeInstructionBreakpoints(element.instruction.address).then(() => { + element.isBreakpointSet = false; + templateData.icon.classList.remove(this.breakpointIcon); + }); + + } else if (element.allowBreakpoint && !element.isBreakpointSet) { + this.debugService.addInstructionBreakpoint(element.instruction.address, 0).then(() => { + element.isBreakpointSet = true; + templateData.icon.classList.add(this.breakpointIcon); + }); } }; } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 40a88a628d754..188ff0b386edb 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -437,6 +437,7 @@ export interface IDataBreakpoint extends IBaseBreakpoint { } export interface IInstructionBreakpoint extends IBaseBreakpoint { + // instructionReference is from debugProtocal and is address for purposes. readonly instructionReference: string; readonly offset?: number; } @@ -901,10 +902,10 @@ export interface IDebugService { addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): Promise; /** - * Removes all instruction breakpoints. If id is passed only removes the instruction breakpoint with the passed id. + * Removes all instruction breakpoints. If address is passed only removes the instruction breakpoint with the passed address. * Notifies debug adapter of breakpoint changes. */ - removeInstructionBreakpoints(id?: string): Promise; + removeInstructionBreakpoints(address?: string): Promise; setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index dcebbebb6f42e..52237fa58f13c 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -927,7 +927,7 @@ export class InstructionBreakpoint extends BaseBreakpoint implements IInstructio override toJSON(): any { const result = super.toJSON(); - result.instructionReference = this.instructionReference; + result.address = this.instructionReference; result.offset = this.offset; return result; } @@ -1298,7 +1298,7 @@ export class DebugModel implements IDebugModel { } enableOrDisableAllBreakpoints(enable: boolean): void { - const changed: Array = []; + const changed: Array = []; this.breakpoints.forEach(bp => { if (bp.enabled !== enable) { @@ -1386,17 +1386,17 @@ export class DebugModel implements IDebugModel { this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); } - addInstructionBreakpoint(instructionReference: string, offset: number, condition?: string, hitCondition?: string): void { - const newInstructionBreakpoint = new InstructionBreakpoint(instructionReference, offset, false, true, hitCondition, condition, undefined); + 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(id?: string): void { + removeInstructionBreakpoints(address?: string): void { let removed: InstructionBreakpoint[]; - if (id) { - removed = this.instructionBreakpoints.filter(fbp => fbp.getId() === id); - this.instructionBreakpoints = this.instructionBreakpoints.filter(fbp => fbp.getId() !== id); + 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 = []; From 5914a40a8dddd90d631622473e99fe43a759edf4 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Mon, 21 Jun 2021 18:17:58 -0700 Subject: [PATCH 17/49] Added accessiblity provider --- .../contrib/debug/browser/disassemblyView.ts | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index df3367af8a1e3..ad3861ff6e172 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -20,6 +20,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DISASSEMBLY_VIEW_ID, IDebugService } 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'; interface IDisassembledInstructionEntry { allowBreakpoint: boolean; @@ -108,7 +109,7 @@ export class DisassemblyView extends EditorPane { multipleSelectionSupport: false, setRowLineHeight: false, openOnSingleClick: false, - // TODO: accessibilityProvider + accessibilityProvider: new AccessibilityProvider() } )) as WorkbenchTable; @@ -181,6 +182,7 @@ export class DisassemblyView extends EditorPane { return false; } + } interface IBreakpointColumnTemplateData { @@ -262,6 +264,7 @@ class BreakpointRenderer implements ITableRenderer { + + 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; + } + } From 451797cc7b45081f47580cb2e05dc1d632098d96 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Tue, 22 Jun 2021 18:48:59 -0700 Subject: [PATCH 18/49] Properly set CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED --- src/vs/workbench/contrib/debug/browser/debugEditorActions.ts | 4 ++-- src/vs/workbench/contrib/debug/browser/disassemblyView.ts | 2 +- src/vs/workbench/contrib/debug/common/debugViewModel.ts | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index d64a1f5fdff01..f783cb3f2e1f8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -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 } 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'; @@ -144,7 +144,7 @@ class OpenDisassemblyViewAction extends EditorAction { 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), + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED), contextMenuOpts: { group: 'debug', order: 5 diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index ad3861ff6e172..499e43100fee9 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -102,7 +102,7 @@ export class DisassemblyView extends EditorPane { ], { identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address }, - horizontalScrolling: true, + horizontalScrolling: false, overrideStyles: { listBackground: editorBackground }, diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index 56485139cae8c..021362f06dece 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -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; private multiSessionDebug!: IContextKey; private terminateDebuggeeSuported!: IContextKey; + private disassembleRequestSupported!: IContextKey; 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(session ? !!session?.capabilities.supportsDisassembleRequest : false); const attach = !!session && isSessionAttach(session); this.focusedSessionIsAttach.set(attach); }); From cdc3714f1158028739cad8ab9df1b59742bf7f17 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Wed, 23 Jun 2021 16:28:55 -0700 Subject: [PATCH 19/49] Stack frame icon --- .../contrib/debug/browser/disassemblyView.ts | 180 ++++++++++++++---- 1 file changed, 145 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 499e43100fee9..39fee4dceffb9 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -17,15 +17,18 @@ 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 { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { DISASSEMBLY_VIEW_ID, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { DISASSEMBLY_VIEW_ID, IDebugService, 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 { IDisposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; interface IDisassembledInstructionEntry { allowBreakpoint: boolean; isBreakpointSet: boolean; instruction: DebugProtocol.DisassembledInstruction; + instructionAddress?: bigint; } export class DisassemblyView extends EditorPane { @@ -33,11 +36,11 @@ export class DisassemblyView extends EditorPane { private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50; // Used in instruction renderer - fontInfo: BareFontInfo; - currentInstructionAddress: string = ''; - + private _fontInfo: BareFontInfo; + private _currentInstructionAddress: string | undefined; private _disassembledInstructions: WorkbenchTable | undefined; - private _debugService: IDebugService; + private _onDidChangeStackFrame: Emitter; + private _privousDebuggingState: State; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -45,22 +48,29 @@ export class DisassemblyView extends EditorPane { @IStorageService storageService: IStorageService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IDebugService debugService: IDebugService + @IDebugService private readonly _debugService: IDebugService ) { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); - this._debugService = debugService; - this.fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); + 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._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); this._disassembledInstructions?.rerender(); } })); this._disassembledInstructions = undefined; + this._onDidChangeStackFrame = new Emitter(); + this._privousDebuggingState = _debugService.state; } + get fontInfo() { return this._fontInfo; } + + get currentInstructionAddress() { return this._currentInstructionAddress; } + + get onDidChangeStackFrame() { return this._onDidChangeStackFrame.event; } + protected createEditor(parent: HTMLElement): void { const lineHeight = this.fontInfo.lineHeight; const delegate = new class implements ITableVirtualDelegate{ @@ -70,12 +80,6 @@ export class DisassemblyView extends EditorPane { } }; - this._register(this._debugService.getViewModel().onDidFocusStackFrame((stackFrame) => { - this.currentInstructionAddress = stackFrame.stackFrame?.instructionPointerReference!; - - // TODO: rerender the marker - })); - this._disassembledInstructions = this._register(this._instantiationService.createInstance(WorkbenchTable, 'DisassemblyView', parent, delegate, [ @@ -113,12 +117,7 @@ export class DisassemblyView extends EditorPane { } )) as WorkbenchTable; - this.loadDisassembledInstructions(undefined, -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); - } - }); + this.reloadDisassembly(); this._register(this._disassembledInstructions.onDidScroll(e => { if (e.oldScrollTop > e.scrollTop && e.scrollTop < e.height) { @@ -132,6 +131,46 @@ export class DisassemblyView extends EditorPane { this.scrollDown_LoadDisassembledInstructions(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD); } })); + + this._register(this._debugService.getViewModel().onDidFocusStackFrame((stackFrame) => { + if (this._disassembledInstructions) { + this._currentInstructionAddress = stackFrame.stackFrame?.instructionPointerReference; + if (this._currentInstructionAddress) { + const index = this.getIndexFromAddress(this._currentInstructionAddress); + 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.ceil((this._disassembledInstructions.scrollTop + this._disassembledInstructions.renderHeight) / this.fontInfo.lineHeight); + if (index > topElement && index < bottomElement) { + // Inside the viewport, don't do anything here + } else if (index < topElement && index > topElement - 5) { + // Not too far from top, review it at the top + this._disassembledInstructions.reveal(index, 0); + } else if (index > bottomElement && index < bottomElement + 5) { + // Not too far from bottom, review it at the bottom + this._disassembledInstructions.reveal(index, 1); + } else { + // Far from the current viewport, reveal it + this._disassembledInstructions.reveal(index, 0.5); + } + } else { + // Stack frame is very far from what's shown in the view now. Clear the table and reload. + this.reloadDisassembly(); + } + } + } + + this._onDidChangeStackFrame.fire(); + })); + + this._register(this._debugService.onDidChangeState(e => { + if ((e === State.Running || e === State.Stopped) && + (this._privousDebuggingState !== State.Running && this._privousDebuggingState !== State.Stopped)) { + // Just started debugging, clear the view + this._disassembledInstructions?.splice(0, this._disassembledInstructions.length); + } + this._privousDebuggingState = e; + })); } layout(dimension: Dimension): void { @@ -156,7 +195,7 @@ export class DisassemblyView extends EditorPane { const frame = this._debugService.getViewModel().focusedStackFrame; if (frame?.instructionPointerReference) { address = frame.instructionPointerReference; - this.currentInstructionAddress = address; + this._currentInstructionAddress = address; } } @@ -172,8 +211,7 @@ export class DisassemblyView extends EditorPane { // request is either at the start or end if (instructionOffset >= 0) { this._disassembledInstructions.splice(this._disassembledInstructions.length, 0, newEntries); - } - else { + } else { this._disassembledInstructions.splice(0, 0, newEntries); } @@ -183,11 +221,76 @@ export class DisassemblyView extends EditorPane { 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.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 + icon: HTMLElement, + stackFrameChangedHandler?: IDisposable } class BreakpointRenderer implements ITableRenderer { @@ -195,12 +298,12 @@ class BreakpointRenderer implements ITableRenderer { + if (element.instruction.address === this._disassemblyView.currentInstructionAddress) { + templateData.icon.classList.add(this.debugStackframe); + } else { + templateData.icon.classList.remove(this.debugStackframe); + } + }); + // TODO: see getBreakpointMessageAndIcon in vs\workbench\contrib\debug\browser\breakpointEditorContribution.ts // for more types of breakpoint icons if (element.allowBreakpoint) { if (element.isBreakpointSet) { templateData.icon.classList.add(this.breakpointIcon); - } - else { + } else { templateData.icon.classList.remove(this.breakpointIcon); } @@ -260,6 +369,7 @@ class BreakpointRenderer implements ITableRenderer Date: Wed, 23 Jun 2021 16:57:26 -0700 Subject: [PATCH 20/49] Properly dispose event listeners in breakpont renderer --- .../contrib/debug/browser/disassemblyView.ts | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 39fee4dceffb9..5f2776e944777 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser'; -import { Dimension, append, $ } from 'vs/base/browser/dom'; +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'; @@ -290,7 +290,7 @@ export class DisassemblyView extends EditorPane { interface IBreakpointColumnTemplateData { container: HTMLElement, icon: HTMLElement, - stackFrameChangedHandler?: IDisposable + disposables: IDisposable[] } class BreakpointRenderer implements ITableRenderer { @@ -315,7 +315,7 @@ class BreakpointRenderer implements ITableRenderer { + templateData.disposables.push(this._disassemblyView.onDidChangeStackFrame(() => { if (element.instruction.address === this._disassemblyView.currentInstructionAddress) { templateData.icon.classList.add(this.debugStackframe); } else { templateData.icon.classList.remove(this.debugStackframe); } - }); + })); // TODO: see getBreakpointMessageAndIcon in vs\workbench\contrib\debug\browser\breakpointEditorContribution.ts // for more types of breakpoint icons @@ -343,15 +343,15 @@ class BreakpointRenderer implements ITableRenderer { + templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseover', () => { templateData.icon.classList.add(this.breakpointHintIcon); - }; + })); - templateData.container.onmouseout = () => { + templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseout', () => { templateData.icon.classList.remove(this.breakpointHintIcon); - }; + })); - templateData.container.onclick = () => { + templateData.disposables.push(addStandardDisposableListener(templateData.container, 'click', () => { if (element.isBreakpointSet) { this.debugService.removeInstructionBreakpoints(element.instruction.address).then(() => { element.isBreakpointSet = false; @@ -364,17 +364,16 @@ class BreakpointRenderer implements ITableRenderer disposable.dispose()); } + disposeTemplate(templateData: IBreakpointColumnTemplateData): void { } + } interface IInstructionColumnTemplateData { From a6865b4a4aed97ecc68e055143cd3d9e974e6a39 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Wed, 23 Jun 2021 17:13:05 -0700 Subject: [PATCH 21/49] Revert ext point changes in debugAdapterManager.ts and debugSchemas.ts --- .../debug/browser/debugAdapterManager.ts | 12 +---------- .../contrib/debug/common/debugSchemas.ts | 21 ------------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 7c319f99c8627..7f527846f8242 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -18,7 +18,7 @@ import { IDebugConfiguration, IConfig, IDebugAdapterDescriptorFactory, IDebugAda import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { launchSchema, debuggersExtPoint, breakpointsExtPoint, presentationSchema, disassemblyExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { launchSchema, debuggersExtPoint, breakpointsExtPoint, presentationSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; @@ -41,7 +41,6 @@ export class AdapterManager implements IAdapterManager { private readonly _onDidRegisterDebugger = new Emitter(); private readonly _onDidDebuggersExtPointRead = new Emitter(); private breakpointModeIdsSet = new Set(); - private disassemblyModeIdsSet = new Set(); constructor( @IEditorService private readonly editorService: IEditorService, @@ -156,15 +155,6 @@ export class AdapterManager implements IAdapterManager { added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); }); }); - - disassemblyExtPoint.setHandler((extensions, delta) => { - delta.removed.forEach(removed => { - removed.value.forEach(disassembly => this.disassemblyModeIdsSet.delete(disassembly.language)); - }); - delta.added.forEach(added => { - added.value.forEach(disassembly => this.disassemblyModeIdsSet.add(disassembly.language)); - }); - }); } registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { diff --git a/src/vs/workbench/contrib/debug/common/debugSchemas.ts b/src/vs/workbench/contrib/debug/common/debugSchemas.ts index 721ccecfb2419..0667a45a868e5 100644 --- a/src/vs/workbench/contrib/debug/common/debugSchemas.ts +++ b/src/vs/workbench/contrib/debug/common/debugSchemas.ts @@ -131,27 +131,6 @@ export interface IDisassemblyContribution { language: string; } -// disassembly extension point -export const disassemblyExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'disassembly', - jsonSchema: { - description: nls.localize('vscode.extension.contributes.disassembly', 'Contributes disassembly.'), - type: 'array', - defaultSnippets: [{ body: [{ language: '' }] }], - items: { - type: 'object', - additionalProperties: false, - defaultSnippets: [{ body: { language: '' } }], - properties: { - language: { - description: nls.localize('vscode.extension.contributes.disassembly.language', "Allow disassembly view for this language."), - type: 'string' - }, - } - } - } -}); - // debug general schema export const presentationSchema: IJSONSchema = { From b8e4d3e84ec8c07fe7b71aba9d038cd552fb02fe Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Wed, 23 Jun 2021 17:15:01 -0700 Subject: [PATCH 22/49] More to revert --- src/vs/workbench/contrib/debug/common/debugSchemas.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/common/debugSchemas.ts b/src/vs/workbench/contrib/debug/common/debugSchemas.ts index 0667a45a868e5..a32cb7dd2e2f7 100644 --- a/src/vs/workbench/contrib/debug/common/debugSchemas.ts +++ b/src/vs/workbench/contrib/debug/common/debugSchemas.ts @@ -127,10 +127,6 @@ export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registe } }); -export interface IDisassemblyContribution { - language: string; -} - // debug general schema export const presentationSchema: IJSONSchema = { From d160870520ab3d580ac023d176cc24a47d64ed9f Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 24 Jun 2021 11:14:38 -0700 Subject: [PATCH 23/49] Reset disposables array after disposing --- src/vs/workbench/contrib/debug/browser/disassemblyView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 5f2776e944777..4110c73754e91 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -370,6 +370,7 @@ class BreakpointRenderer implements ITableRenderer disposable.dispose()); + templateData.disposables = []; } disposeTemplate(templateData: IBreakpointColumnTemplateData): void { } From c2119a4740b6a2a20da18c774016180383c53d36 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 24 Jun 2021 14:38:08 -0700 Subject: [PATCH 24/49] Current instruction line highlight --- .../browser/callStackEditorContribution.ts | 4 +- .../contrib/debug/browser/disassemblyView.ts | 100 ++++++++++++------ 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index 284a4bc170794..7ead40ff72cb9 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -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. diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 4110c73754e91..1d8004d89abba 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -21,8 +21,10 @@ import { DISASSEMBLY_VIEW_ID, IDebugService, State } from 'vs/workbench/contrib/ 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 { IDisposable } from 'vs/base/common/lifecycle'; +import { 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'; interface IDisassembledInstructionEntry { allowBreakpoint: boolean; @@ -80,6 +82,8 @@ export class DisassemblyView extends EditorPane { } }; + const instructionRenderer = this._register(this._instantiationService.createInstance(InstructionRenderer, this)); + this._disassembledInstructions = this._register(this._instantiationService.createInstance(WorkbenchTable, 'DisassemblyView', parent, delegate, [ @@ -102,7 +106,7 @@ export class DisassemblyView extends EditorPane { ], [ this._instantiationService.createInstance(BreakpointRenderer, this), - this._instantiationService.createInstance(InstructionRenderer, this), + instructionRenderer, ], { identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address }, @@ -296,15 +300,18 @@ interface IBreakpointColumnTemplateData { class BreakpointRenderer implements ITableRenderer { static readonly TEMPLATE_ID = 'breakpoint'; - private breakpointIcon = 'codicon-' + icons.breakpoint.regular.id; - private breakpointHintIcon = 'codicon-' + icons.debugBreakpointHint.id; - private debugStackframe = 'codicon-' + icons.debugStackframe.id; - // private debugStackframeFocused = 'codicon-' + icons.debugStackframeFocused.id; templateId: string = BreakpointRenderer.TEMPLATE_ID; - constructor(private readonly _disassemblyView: DisassemblyView, - @IDebugService private readonly debugService: IDebugService) { + 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 { @@ -319,49 +326,46 @@ class BreakpointRenderer implements ITableRenderer { + const rerenderDebugStackframe = () => { if (element.instruction.address === this._disassemblyView.currentInstructionAddress) { - templateData.icon.classList.add(this.debugStackframe); + templateData.icon.classList.add(this._debugStackframe); } else { - templateData.icon.classList.remove(this.debugStackframe); + templateData.icon.classList.remove(this._debugStackframe); } - })); + }; + + 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) { if (element.isBreakpointSet) { - templateData.icon.classList.add(this.breakpointIcon); + templateData.icon.classList.add(this._breakpointIcon); } else { - templateData.icon.classList.remove(this.breakpointIcon); + templateData.icon.classList.remove(this._breakpointIcon); } templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseover', () => { - templateData.icon.classList.add(this.breakpointHintIcon); + templateData.icon.classList.add(this._breakpointHintIcon); })); templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseout', () => { - templateData.icon.classList.remove(this.breakpointHintIcon); + templateData.icon.classList.remove(this._breakpointHintIcon); })); templateData.disposables.push(addStandardDisposableListener(templateData.container, 'click', () => { if (element.isBreakpointSet) { - this.debugService.removeInstructionBreakpoints(element.instruction.address).then(() => { + this._debugService.removeInstructionBreakpoints(element.instruction.address).then(() => { element.isBreakpointSet = false; - templateData.icon.classList.remove(this.breakpointIcon); + templateData.icon.classList.remove(this._breakpointIcon); }); } else if (element.allowBreakpoint && !element.isBreakpointSet) { - this.debugService.addInstructionBreakpoint(element.instruction.address, 0).then(() => { + this._debugService.addInstructionBreakpoint(element.instruction.address, 0).then(() => { element.isBreakpointSet = true; - templateData.icon.classList.add(this.breakpointIcon); + templateData.icon.classList.add(this._breakpointIcon); }); } })); @@ -380,24 +384,40 @@ class BreakpointRenderer implements ITableRenderer { +class InstructionRenderer extends Disposable implements ITableRenderer { + + static readonly TEMPLATE_ID = 'instruction'; private static readonly INSTRUCTION_ADDR_MIN_LENGTH = 25; private static readonly INSTRUCTION_BYTES_MIN_LENGTH = 30; - static readonly TEMPLATE_ID = 'instruction'; - templateId: string = InstructionRenderer.TEMPLATE_ID; - constructor(private readonly disassemblyView: DisassemblyView) { + 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 }; + return { instruction, disposables: [] }; } renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void { @@ -428,12 +448,28 @@ class InstructionRenderer implements ITableRenderer { + 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 { + templateData.disposables.forEach(disposable => disposable.dispose()); + templateData.disposables = []; } disposeTemplate(templateData: IInstructionColumnTemplateData): void { } private applyFontInfo(element: HTMLElement) { - const fontInfo = this.disassemblyView.fontInfo; + const fontInfo = this._disassemblyView.fontInfo; element.style.fontFamily = fontInfo.getMassagedFontFamily(); element.style.fontWeight = fontInfo.fontWeight; element.style.fontSize = fontInfo.fontSize + 'px'; From 73fa288915414a834120ab20261daa7af28d6d65 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Thu, 24 Jun 2021 14:07:08 -0700 Subject: [PATCH 25/49] wip --- .../contrib/debug/browser/breakpointsView.ts | 85 ++++++++++++++++-- .../debug/browser/debugEditorActions.ts | 1 + .../contrib/debug/browser/debugSession.ts | 28 +++++- .../contrib/debug/browser/disassemblyView.ts | 88 ++++++++++++++----- .../workbench/contrib/debug/common/debug.ts | 9 +- .../contrib/debug/common/debugModel.ts | 2 +- .../debug/test/browser/callStack.test.ts | 4 +- .../contrib/debug/test/browser/mockDebug.ts | 8 +- 8 files changed, 185 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index b8160112d8eb4..7b62e602b6f5d 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -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'; @@ -60,7 +60,7 @@ export function getExpandedBodySize(model: IDebugModel, countLimit: number): num const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().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 +120,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 +143,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); } }); @@ -326,6 +329,9 @@ class BreakpointsDelegate implements IListVirtualDelegate { if (element instanceof DataBreakpoint) { return DataBreakpointsRenderer.ID; } + if (element instanceof InstructionBreakpoint) { + return InstructionBreakpointsRenderer.ID; + } return ''; } @@ -362,6 +368,10 @@ interface IDataBreakpointTemplateData extends IBaseBreakpointWithIconTemplateDat accessType: HTMLElement; } +interface IInstructionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData { + address: HTMLElement; +} + interface IFunctionBreakpointInputTemplateData { inputBox: InputBox; checkbox: HTMLInputElement; @@ -673,6 +683,71 @@ class DataBreakpointsRenderer implements IListRenderer { + + 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 { constructor( @@ -927,7 +1002,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; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index f783cb3f2e1f8..c717201fb70f0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -54,6 +54,7 @@ class ToggleBreakpointAction extends EditorAction2 { } async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise { + // TODO: add disassembly F9 if (editor.hasModel()) { const debugService = accessor.get(IDebugService); const modelUri = editor.getModel().uri; diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 9c6654c01e41e..8ac7e6c3b7059 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -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, IInstructionBreakpoint } 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, CONTEXT_DISASSEMBLE_VIEW_FOCUS } 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 { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class DebugSession implements IDebugSession { @@ -51,6 +52,7 @@ export class DebugSession implements IDebugSession { private fetchThreadsScheduler: RunOnceScheduler | undefined; private repl: ReplModel; private stoppedDetails: IRawStoppedDetails[] = []; + private _disassemblyViewFocus: IContextKey; private readonly _onDidChangeState = new Emitter(); private readonly _onDidEndAdapter = new Emitter(); @@ -83,7 +85,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()) { @@ -105,6 +108,8 @@ export class DebugSession implements IDebugSession { if (compoundRoot) { toDispose.push(compoundRoot.onDidSessionStop(() => this.terminate())); } + + this._disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); } getId(): string { @@ -558,6 +563,10 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'next')); } + if (this._disassemblyViewFocus.get()) { + granularity = 'instruction'; + } + await this.raw.next({ threadId, granularity }); } @@ -566,6 +575,10 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepIn')); } + if (this._disassemblyViewFocus.get()) { + granularity = 'instruction'; + } + await this.raw.stepIn({ threadId, targetId, granularity }); } @@ -574,6 +587,10 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepOut')); } + if (this._disassemblyViewFocus.get()) { + granularity = 'instruction'; + } + await this.raw.stepOut({ threadId, granularity }); } @@ -582,6 +599,10 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepBack')); } + if (this._disassemblyViewFocus.get()) { + granularity = 'instruction'; + } + await this.raw.stepBack({ threadId, granularity }); } @@ -859,7 +880,8 @@ export class DebugSession implements IDebugSession { // Second retrieves the rest of the call stack. For performance reasons #25605 const promises = this.model.fetchCallStack(thread); const focus = async () => { - if (!event.body.preserveFocusHint && thread.getCallStack().length) { + // Don't switch view when DisassemblyView is in focus. + if (!event.body.preserveFocusHint && !this._disassemblyViewFocus.get() && thread.getCallStack().length) { await this.debugService.focusStackFrame(undefined, thread); if (thread.stoppedDetails) { if (this.configurationService.getValue('debug').openDebug === 'openOnDebugBreak') { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 1d8004d89abba..6b01266b5795c 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -17,7 +17,7 @@ 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 { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { DISASSEMBLY_VIEW_ID, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_DISASSEMBLE_VIEW_FOCUS, DISASSEMBLY_VIEW_ID, IDebugService, 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'; @@ -25,6 +25,8 @@ import { 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'; interface IDisassembledInstructionEntry { allowBreakpoint: boolean; @@ -43,6 +45,7 @@ export class DisassemblyView extends EditorPane { private _disassembledInstructions: WorkbenchTable | undefined; private _onDidChangeStackFrame: Emitter; private _privousDebuggingState: State; + disassemblyViewFocus: IContextKey; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -50,6 +53,7 @@ export class DisassemblyView extends EditorPane { @IStorageService storageService: IStorageService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, @IDebugService private readonly _debugService: IDebugService ) { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); @@ -65,6 +69,7 @@ export class DisassemblyView extends EditorPane { this._disassembledInstructions = undefined; this._onDidChangeStackFrame = new Emitter(); this._privousDebuggingState = _debugService.state; + this.disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); } get fontInfo() { return this._fontInfo; } @@ -137,9 +142,10 @@ export class DisassemblyView extends EditorPane { })); this._register(this._debugService.getViewModel().onDidFocusStackFrame((stackFrame) => { - if (this._disassembledInstructions) { + if (this._disassembledInstructions && this._currentInstructionAddress !== stackFrame.stackFrame?.instructionPointerReference) { this._currentInstructionAddress = stackFrame.stackFrame?.instructionPointerReference; if (this._currentInstructionAddress) { + console.log(`DisassemblyView: ${this._currentInstructionAddress}`); const index = this.getIndexFromAddress(this._currentInstructionAddress); if (index >= 0) { // If the row is out of the viewport, reveal it @@ -162,9 +168,41 @@ export class DisassemblyView extends EditorPane { this.reloadDisassembly(); } } + + 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; + } + } + }); + + if (changed) { + this._onDidChangeStackFrame.fire(); + } } - this._onDidChangeStackFrame.fire(); })); this._register(this._debugService.onDidChangeState(e => { @@ -184,13 +222,21 @@ export class DisassemblyView extends EditorPane { } private async scrollUp_LoadDisassembledInstructions(instructionCount: number): Promise { - const address: string | undefined = this._disassembledInstructions?.row(0).instruction.address; - return this.loadDisassembledInstructions(address, -instructionCount, instructionCount - 1); + 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 { - const address: string | undefined = this._disassembledInstructions?.row(this._disassembledInstructions?.length - 1).instruction.address; - return this.loadDisassembledInstructions(address, 1, instructionCount); + 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 { @@ -200,9 +246,12 @@ export class DisassemblyView extends EditorPane { if (frame?.instructionPointerReference) { address = frame.instructionPointerReference; this._currentInstructionAddress = address; + } else { + 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) { @@ -332,21 +381,22 @@ class BreakpointRenderer implements ITableRenderer { templateData.icon.classList.add(this._breakpointHintIcon); })); @@ -356,17 +406,13 @@ class BreakpointRenderer implements ITableRenderer { + // click show hint while waiting for BP to resolve. + templateData.icon.classList.add(this._breakpointHintIcon); if (element.isBreakpointSet) { - this._debugService.removeInstructionBreakpoints(element.instruction.address).then(() => { - element.isBreakpointSet = false; - templateData.icon.classList.remove(this._breakpointIcon); - }); + this._debugService.removeInstructionBreakpoints(element.instruction.address); } else if (element.allowBreakpoint && !element.isBreakpointSet) { - this._debugService.addInstructionBreakpoint(element.instruction.address, 0).then(() => { - element.isBreakpointSet = true; - templateData.icon.classList.add(this._breakpointIcon); - }); + this._debugService.addInstructionBreakpoint(element.instruction.address, 0); } })); } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 188ff0b386edb..099b2c1f1bda5 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -82,6 +82,7 @@ export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exce export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('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('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('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") }); +export const CONTEXT_DISASSEMBLE_VIEW_FOCUS = new RawContextKey('disassembleViewFocus', false, { type: 'boolean', description: nls.localize('disassembleViewFocus', "True when the Disassembly View is in focused.") }); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; @@ -270,11 +271,11 @@ export interface IDebugSession extends ITreeElement { disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise; restartFrame(frameId: number, threadId: number): Promise; - next(threadId: number): Promise; - stepIn(threadId: number, targetId?: number): Promise; + next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise; + stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise; stepInTargets(frameId: number): Promise<{ id: number, label: string }[] | undefined>; - stepOut(threadId: number): Promise; - stepBack(threadId: number): Promise; + stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise; + stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise; continue(threadId: number): Promise; reverseContinue(threadId: number): Promise; pause(threadId: number): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 52237fa58f13c..f166cc5b468eb 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -927,7 +927,7 @@ export class InstructionBreakpoint extends BaseBreakpoint implements IInstructio override toJSON(): any { const result = super.toJSON(); - result.address = this.instructionReference; + result.instructionReference = this.instructionReference; result.offset = this.offset; return result; } diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index c1e0453234402..91e73765f6ce0 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -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); diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index acafa0731b502..688b6cbb71b9b 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -362,16 +362,16 @@ export class MockSession implements IDebugSession { restartFrame(frameId: number, threadId: number): Promise { throw new Error('Method not implemented.'); } - next(threadId: number): Promise { + next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise { throw new Error('Method not implemented.'); } - stepIn(threadId: number, targetId?: number): Promise { + stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise { throw new Error('Method not implemented.'); } - stepOut(threadId: number): Promise { + stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise { throw new Error('Method not implemented.'); } - stepBack(threadId: number): Promise { + stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise { throw new Error('Method not implemented.'); } continue(threadId: number): Promise { From a2f7ea57ffbafea2b3204027f9e921ac1f35c0ca Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Sun, 27 Jun 2021 20:03:53 -0700 Subject: [PATCH 26/49] Add InstructionBreakpoint to BP View --- .../contrib/debug/browser/breakpointsView.ts | 7 ++++--- .../contrib/debug/browser/disassemblyView.ts | 11 +++++++++-- src/vs/workbench/contrib/debug/common/debug.ts | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 7b62e602b6f5d..722070c246b1c 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -57,7 +57,7 @@ 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 | IInstructionBreakpoint; @@ -217,7 +217,8 @@ export class BreakpointsView extends ViewPane { private onListContextMenu(e: IListContextMenuEvent): 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); @@ -291,7 +292,7 @@ export class BreakpointsView extends ViewPane { private get elements(): BreakpointItem[] { const model = this.debugService.getModel(); - const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()); + const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()).concat(model.getInstructionBreakpoints()); return elements as BreakpointItem[]; } diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 6b01266b5795c..2752978e80346 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -45,7 +45,7 @@ export class DisassemblyView extends EditorPane { private _disassembledInstructions: WorkbenchTable | undefined; private _onDidChangeStackFrame: Emitter; private _privousDebuggingState: State; - disassemblyViewFocus: IContextKey; + _disassemblyViewFocus: IContextKey; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -69,7 +69,7 @@ export class DisassemblyView extends EditorPane { this._disassembledInstructions = undefined; this._onDidChangeStackFrame = new Emitter(); this._privousDebuggingState = _debugService.state; - this.disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); + this._disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); } get fontInfo() { return this._fontInfo; } @@ -215,6 +215,13 @@ export class DisassemblyView extends EditorPane { })); } + override focus(): void { + super.focus(); + let isFocus = this.hasFocus(); + console.log(`DisassemblyView: Focus(${isFocus})`); + this._disassemblyViewFocus.set(isFocus); + } + layout(dimension: Dimension): void { if (this._disassembledInstructions) { this._disassembledInstructions.layout(dimension.height); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 099b2c1f1bda5..53ddf8f568e98 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -492,6 +492,7 @@ export interface IDebugModel extends ITreeElement { getFunctionBreakpoints(): ReadonlyArray; getDataBreakpoints(): ReadonlyArray; getExceptionBreakpoints(): ReadonlyArray; + getInstructionBreakpoints(): ReadonlyArray; getWatchExpressions(): ReadonlyArray; onDidChangeBreakpoints: Event; From fc659a871468a921b9878c9086f016b8f3fd3acb Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Tue, 29 Jun 2021 12:53:39 -0700 Subject: [PATCH 27/49] Show 'open disassembly view' option only when language in currently focused document supports disassembly --- .../debug/browser/debug.contribution.ts | 3 +- .../debug/browser/debugEditorActions.ts | 4 +- .../contrib/debug/browser/disassemblyView.ts | 58 +++++++++++++++++-- .../workbench/contrib/debug/common/debug.ts | 1 + 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index b441011670dbc..7b6395fb6295d 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,7 +52,7 @@ import { FileAccess } from 'vs/base/common/network'; import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; import { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions } from 'vs/workbench/common/editor'; -import { DisassemblyView, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; +import { DisassemblyView, DisassemblyVIewContribution, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); @@ -67,6 +67,7 @@ if (isWeb) { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisassemblyVIewContribution, LifecyclePhase.Eventually); // Register Quick Access Registry.as(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({ diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index c717201fb70f0..cffb0de61a274 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -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, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED } 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'; @@ -145,7 +145,7 @@ class OpenDisassemblyViewAction extends EditorAction { 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), + 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 diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 2752978e80346..c7d0fc97b535e 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -17,16 +17,19 @@ 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 { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { CONTEXT_DISASSEMBLE_VIEW_FOCUS, DISASSEMBLY_VIEW_ID, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, DISASSEMBLY_VIEW_ID, IDebugService, 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 { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +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; @@ -426,7 +429,7 @@ class BreakpointRenderer implements ITableRenderer disposable.dispose()); + dispose(templateData.disposables); templateData.disposables = []; } @@ -515,7 +518,7 @@ class InstructionRenderer extends Disposable implements ITableRenderer disposable.dispose()); + dispose(templateData.disposables); templateData.disposables = []; } @@ -587,3 +590,50 @@ class AccessibilityProvider implements IListAccessibilityProvider | 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(); + } + +} diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 53ddf8f568e98..5227ade4c7342 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -83,6 +83,7 @@ export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('multiSessi export const CONTEXT_MULTI_SESSION_DEBUG = new RawContextKey('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('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") }); export const CONTEXT_DISASSEMBLE_VIEW_FOCUS = new RawContextKey('disassembleViewFocus', false, { type: 'boolean', description: nls.localize('disassembleViewFocus', "True when the Disassembly View is in focused.") }); +export const CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST = new RawContextKey('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'; From aa5940f715df07e0242c54258c6528ad2a9c77df Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Fri, 2 Jul 2021 10:56:22 -0700 Subject: [PATCH 28/49] Fix breakpoint view. Remove ContextKey. --- .../contrib/debug/browser/breakpointsView.ts | 5 ++++ .../contrib/debug/browser/debugService.ts | 4 ++- .../contrib/debug/browser/debugSession.ts | 25 +++---------------- .../contrib/debug/browser/disassemblyView.ts | 1 - .../contrib/debug/common/debugModel.ts | 6 ++--- 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 722070c246b1c..2488d2b982a30 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -159,6 +159,9 @@ 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) { + } if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.inputBoxData?.breakpoint) { // double click @@ -1175,6 +1178,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); } } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 339011bd24324..528ed1f7d8870 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -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'; @@ -878,6 +878,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(); } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 8ac7e6c3b7059..cb14e738344c8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -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, IInstructionBreakpoint, CONTEXT_DISASSEMBLE_VIEW_FOCUS } 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,7 +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 { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class DebugSession implements IDebugSession { @@ -52,7 +52,6 @@ export class DebugSession implements IDebugSession { private fetchThreadsScheduler: RunOnceScheduler | undefined; private repl: ReplModel; private stoppedDetails: IRawStoppedDetails[] = []; - private _disassemblyViewFocus: IContextKey; private readonly _onDidChangeState = new Emitter(); private readonly _onDidEndAdapter = new Emitter(); @@ -108,8 +107,6 @@ export class DebugSession implements IDebugSession { if (compoundRoot) { toDispose.push(compoundRoot.onDidSessionStop(() => this.terminate())); } - - this._disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); } getId(): string { @@ -563,10 +560,6 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'next')); } - if (this._disassemblyViewFocus.get()) { - granularity = 'instruction'; - } - await this.raw.next({ threadId, granularity }); } @@ -575,10 +568,6 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepIn')); } - if (this._disassemblyViewFocus.get()) { - granularity = 'instruction'; - } - await this.raw.stepIn({ threadId, targetId, granularity }); } @@ -587,10 +576,6 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepOut')); } - if (this._disassemblyViewFocus.get()) { - granularity = 'instruction'; - } - await this.raw.stepOut({ threadId, granularity }); } @@ -599,10 +584,6 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepBack')); } - if (this._disassemblyViewFocus.get()) { - granularity = 'instruction'; - } - await this.raw.stepBack({ threadId, granularity }); } @@ -881,7 +862,7 @@ export class DebugSession implements IDebugSession { const promises = this.model.fetchCallStack(thread); const focus = async () => { // Don't switch view when DisassemblyView is in focus. - if (!event.body.preserveFocusHint && !this._disassemblyViewFocus.get() && thread.getCallStack().length) { + if (!event.body.preserveFocusHint && thread.getCallStack().length) { await this.debugService.focusStackFrame(undefined, thread); if (thread.stoppedDetails) { if (this.configurationService.getValue('debug').openDebug === 'openOnDebugBreak') { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index c7d0fc97b535e..ab2a59abafedd 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -205,7 +205,6 @@ export class DisassemblyView extends EditorPane { this._onDidChangeStackFrame.fire(); } } - })); this._register(this._debugService.onDidChangeState(e => { diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index f166cc5b468eb..571bf3b4ba4b6 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -1282,9 +1282,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 = []; - 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 = []; + if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof DataBreakpoint || element instanceof InstructionBreakpoint)) { changed.push(element); } From cc5619492f2df929be09125f30491f73893775d8 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Fri, 2 Jul 2021 23:10:45 -0700 Subject: [PATCH 29/49] Add ContextKey when focus, step disassembly. --- .../contrib/debug/browser/debugCommands.ts | 38 ++++++++++++++++++- .../contrib/debug/browser/disassemblyView.ts | 19 ++++++---- .../workbench/contrib/debug/common/debug.ts | 8 ++-- .../contrib/debug/common/debugModel.ts | 22 +++++------ 4 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 9f27816b01cf8..bb1e4ed216067 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -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_DISASSEMBLE_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'; @@ -41,6 +41,9 @@ export const TERMINATE_THREAD_ID = 'workbench.action.debug.terminateThread'; export const STEP_OVER_ID = 'workbench.action.debug.stepOver'; export const STEP_INTO_ID = 'workbench.action.debug.stepInto'; export const STEP_OUT_ID = 'workbench.action.debug.stepOut'; +export const INSTRUCTION_STEP_OVER_ID = 'workbench.action.debug.instructionStepOver'; +export const INSTRUCTION_STEP_INTO_ID = 'workbench.action.debug.instructionStepInto'; +export const INSTRUCTION_STEP_OUT_ID = 'workbench.action.debug.instructionStepOut'; export const PAUSE_ID = 'workbench.action.debug.pause'; export const DISCONNECT_ID = 'workbench.action.debug.disconnect'; export const STOP_ID = 'workbench.action.debug.stop'; @@ -70,6 +73,8 @@ export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'" export const DEBUG_START_LABEL = nls.localize('startDebug', "Start Debugging"); export const DEBUG_RUN_LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging"); +const INSTRUCTION_PRIORITY = 5; + interface CallStackContext { sessionId: string; threadId: string; @@ -281,6 +286,37 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: INSTRUCTION_STEP_OVER_ID, + weight: KeybindingWeight.WorkbenchContrib + INSTRUCTION_PRIORITY, + primary: isWeb ? (KeyMod.Alt | KeyCode.F10) : KeyCode.F10, // Browsers do not allow F10 to be binded so we have to bind an alternative + when: ContextKeyExpr.and(CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction')); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: INSTRUCTION_STEP_INTO_ID, + weight: KeybindingWeight.WorkbenchContrib + INSTRUCTION_PRIORITY + 10, // Have a stronger weight to have priority over full screen when debugging + primary: (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11, // Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut + // Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times + when: ContextKeyExpr.and(CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_DEBUG_STATE.notEqualsTo('inactive')), + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn('instruction')); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: INSTRUCTION_STEP_OUT_ID, + weight: KeybindingWeight.WorkbenchContrib + INSTRUCTION_PRIORITY, + primary: KeyMod.Shift | KeyCode.F11, + when: ContextKeyExpr.and(CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut('instruction')); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: PAUSE_ID, weight: KeybindingWeight.WorkbenchContrib + 2, // take priority over focus next part while we are debugging diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index ab2a59abafedd..ac392a1e6123a 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -57,7 +57,8 @@ export class DisassemblyView extends EditorPane { @IConfigurationService configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextKeyService contextKeyService: IContextKeyService, - @IDebugService private readonly _debugService: IDebugService + @IDebugService private readonly _debugService: IDebugService, + @IEditorService private readonly editorService: IEditorService, ) { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); @@ -69,6 +70,15 @@ export class DisassemblyView extends EditorPane { } })); + this._register(editorService.onDidActiveEditorChange(() => { + if (this.editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView`) { + this._disassemblyViewFocus.set(true); + } + else { + this._disassemblyViewFocus.reset(); + } + })); + this._disassembledInstructions = undefined; this._onDidChangeStackFrame = new Emitter(); this._privousDebuggingState = _debugService.state; @@ -217,13 +227,6 @@ export class DisassemblyView extends EditorPane { })); } - override focus(): void { - super.focus(); - let isFocus = this.hasFocus(); - console.log(`DisassemblyView: Focus(${isFocus})`); - this._disassemblyViewFocus.set(isFocus); - } - layout(dimension: Dimension): void { if (this._disassembledInstructions) { this._disassembledInstructions.layout(dimension.height); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 5227ade4c7342..b57871a507199 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -343,10 +343,10 @@ export interface IThread extends ITreeElement { */ readonly stopped: boolean; - next(): Promise; - stepIn(): Promise; - stepOut(): Promise; - stepBack(): Promise; + next(granularity?: DebugProtocol.SteppingGranularity): Promise; + stepIn(granularity?: DebugProtocol.SteppingGranularity): Promise; + stepOut(granularity?: DebugProtocol.SteppingGranularity): Promise; + stepBack(granularity?: DebugProtocol.SteppingGranularity): Promise; continue(): Promise; pause(): Promise; terminate(): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 571bf3b4ba4b6..bb911233fab3e 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -519,20 +519,20 @@ export class Thread implements IThread { return Promise.resolve(undefined); } - next(): Promise { - return this.session.next(this.threadId); + next(granularity?: DebugProtocol.SteppingGranularity): Promise { + return this.session.next(this.threadId, granularity); } - stepIn(): Promise { - return this.session.stepIn(this.threadId); + stepIn(granularity?: DebugProtocol.SteppingGranularity): Promise { + return this.session.stepIn(this.threadId, undefined, granularity); } - stepOut(): Promise { - return this.session.stepOut(this.threadId); + stepOut(granularity?: DebugProtocol.SteppingGranularity): Promise { + return this.session.stepOut(this.threadId, granularity); } - stepBack(): Promise { - return this.session.stepBack(this.threadId); + stepBack(granularity?: DebugProtocol.SteppingGranularity): Promise { + return this.session.stepBack(this.threadId, granularity); } continue(): Promise { @@ -1244,9 +1244,9 @@ export class DebugModel implements IDebugModel { if (!data) { ibp.setSessionData(sessionId, undefined); } else { - const dbpData = data.get(ibp.getId()); - if (dbpData) { - ibp.setSessionData(sessionId, toBreakpointSessionData(dbpData, capabilites)); + const ibpData = data.get(ibp.getId()); + if (ibpData) { + ibp.setSessionData(sessionId, toBreakpointSessionData(ibpData, capabilites)); } } }); From e81f4cca58b6cf0be4054dbd73dde4edbcc38773 Mon Sep 17 00:00:00 2001 From: Felix Huang Date: Wed, 7 Jul 2021 11:31:15 -0700 Subject: [PATCH 30/49] Fix spelling. --- .../contrib/debug/browser/debug.contribution.ts | 6 +++--- .../contrib/debug/browser/debugEditorActions.ts | 3 +-- src/vs/workbench/contrib/debug/browser/debugService.ts | 2 +- .../workbench/contrib/debug/browser/disassemblyView.ts | 10 +++++----- src/vs/workbench/contrib/debug/common/debug.ts | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 7b6395fb6295d..5acf6e94c5e34 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,7 +52,7 @@ import { FileAccess } from 'vs/base/common/network'; import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; import { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions } from 'vs/workbench/common/editor'; -import { DisassemblyView, DisassemblyVIewContribution, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; +import { DisassemblyView, DisassemblyViewContribution, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); @@ -67,7 +67,7 @@ if (isWeb) { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisassemblyVIewContribution, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisassemblyViewContribution, LifecyclePhase.Eventually); // Register Quick Access Registry.as(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({ @@ -377,7 +377,7 @@ 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 disassmebly editor +// Register disassembly editor Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), [new SyncDescriptor(DisassemblyViewInput)] diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index cffb0de61a274..fcc173732f9d4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -154,8 +154,7 @@ class OpenDisassemblyViewAction extends EditorAction { } async run(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise { - const position = editor.getPosition(); - if (position && editor.hasModel()) { + if (editor.hasModel()) { const editorService = accessor.get(IEditorService); editorService.openEditor(DisassemblyViewInput.instance); } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 528ed1f7d8870..591bffe91acb3 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -1006,7 +1006,7 @@ export class DebugService implements IDebugService { const result: DebugProtocol.DisassembledInstruction[] = []; for (let i = 0; i < instructionCount; i++) { - result.push({ address: `${i + offset}`, instruction: `debugger is not availible.` }); + result.push({ address: `${i + offset}`, instruction: `debugger is not available.` }); } resolve(result); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index ac392a1e6123a..4dd1523ea8c55 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -47,7 +47,7 @@ export class DisassemblyView extends EditorPane { private _currentInstructionAddress: string | undefined; private _disassembledInstructions: WorkbenchTable | undefined; private _onDidChangeStackFrame: Emitter; - private _privousDebuggingState: State; + private _previousDebuggingState: State; _disassemblyViewFocus: IContextKey; constructor( @@ -81,7 +81,7 @@ export class DisassemblyView extends EditorPane { this._disassembledInstructions = undefined; this._onDidChangeStackFrame = new Emitter(); - this._privousDebuggingState = _debugService.state; + this._previousDebuggingState = _debugService.state; this._disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); } @@ -219,11 +219,11 @@ export class DisassemblyView extends EditorPane { this._register(this._debugService.onDidChangeState(e => { if ((e === State.Running || e === State.Stopped) && - (this._privousDebuggingState !== State.Running && this._privousDebuggingState !== State.Stopped)) { + (this._previousDebuggingState !== State.Running && this._previousDebuggingState !== State.Stopped)) { // Just started debugging, clear the view this._disassembledInstructions?.splice(0, this._disassembledInstructions.length); } - this._privousDebuggingState = e; + this._previousDebuggingState = e; })); } @@ -593,7 +593,7 @@ class AccessibilityProvider implements IListAccessibilityProvider('exce export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('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('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('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") }); -export const CONTEXT_DISASSEMBLE_VIEW_FOCUS = new RawContextKey('disassembleViewFocus', false, { type: 'boolean', description: nls.localize('disassembleViewFocus', "True when the Disassembly View is in focused.") }); +export const CONTEXT_DISASSEMBLE_VIEW_FOCUS = new RawContextKey('disassembleViewFocus', false, { type: 'boolean', description: nls.localize('disassembleViewFocus', "True when the Disassembly View is focused.") }); export const CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST = new RawContextKey('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'; @@ -439,7 +439,7 @@ export interface IDataBreakpoint extends IBaseBreakpoint { } export interface IInstructionBreakpoint extends IBaseBreakpoint { - // instructionReference is from debugProtocal and is address for purposes. + // instructionReference is the instruction 'address' from the debugger. readonly instructionReference: string; readonly offset?: number; } From cc1d75a92db739eedef92957c4042df4748a658d Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Wed, 7 Jul 2021 13:12:16 -0700 Subject: [PATCH 31/49] WIP --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 5acf6e94c5e34..2e688742b5a3e 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -50,7 +50,6 @@ 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 { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions } from 'vs/workbench/common/editor'; import { DisassemblyView, DisassemblyViewContribution, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; @@ -378,7 +377,7 @@ viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, cont 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 editor -Registry.as(EditorExtensions.Editors).registerEditor( +Registry.as(EditorExtensions.EditorPane).registerEditor( EditorDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), [new SyncDescriptor(DisassemblyViewInput)] ); From 4114792b630d2e516bacab2f00cb158b395d1c71 Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Wed, 7 Jul 2021 13:34:02 -0700 Subject: [PATCH 32/49] Fix build error --- .../workbench/contrib/debug/browser/debug.contribution.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 2e688742b5a3e..446b1b78a2f24 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,6 +52,7 @@ 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, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; +import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); @@ -377,8 +378,9 @@ viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, cont 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 editor -Registry.as(EditorExtensions.EditorPane).registerEditor( - EditorDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), + +Registry.as(EditorExtensions.EditorPane).registerEditorPane( + EditorPaneDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), [new SyncDescriptor(DisassemblyViewInput)] ); From b197a3430238712fb1a04b18195839368bca963f Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Fri, 9 Jul 2021 10:46:01 -0700 Subject: [PATCH 33/49] Fix rendering BP when restarting debugging. --- .../contrib/debug/browser/disassemblyView.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 4dd1523ea8c55..1a540292e6360 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -17,7 +17,7 @@ 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 { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, DISASSEMBLY_VIEW_ID, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_DISASSEMBLE_VIEW_FOCUS, 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'; @@ -48,6 +48,7 @@ export class DisassemblyView extends EditorPane { private _disassembledInstructions: WorkbenchTable | undefined; private _onDidChangeStackFrame: Emitter; private _previousDebuggingState: State; + private _instructionBpList: readonly IInstructionBreakpoint[] = []; _disassemblyViewFocus: IContextKey; constructor( @@ -158,7 +159,7 @@ export class DisassemblyView extends EditorPane { if (this._disassembledInstructions && this._currentInstructionAddress !== stackFrame.stackFrame?.instructionPointerReference) { this._currentInstructionAddress = stackFrame.stackFrame?.instructionPointerReference; if (this._currentInstructionAddress) { - console.log(`DisassemblyView: ${this._currentInstructionAddress}`); + // console.log(`DisassemblyView: ${this._currentInstructionAddress}`); const index = this.getIndexFromAddress(this._currentInstructionAddress); if (index >= 0) { // If the row is out of the viewport, reveal it @@ -211,6 +212,9 @@ export class DisassemblyView extends EditorPane { } }); + // 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(); } @@ -263,14 +267,15 @@ export class DisassemblyView extends EditorPane { } } - console.log(`DisassemblyView: loadDisassembledInstructions ${address}, ${instructionOffset}, ${instructionCount}`); + // 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++) { - newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: resultEntries[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 @@ -341,6 +346,7 @@ export class DisassemblyView extends EditorPane { 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) { From 6f38fee985b343d7322d1f290130adaccf25f5f1 Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Fri, 9 Jul 2021 13:32:15 -0700 Subject: [PATCH 34/49] Don't switch focus when DisassemblyView is in Focus. --- src/vs/workbench/contrib/debug/common/debugSource.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index bf1ac904c0aef..a167120e41649 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -73,6 +73,9 @@ export class Source { } openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { + if (editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView`) { + return Promise.resolve(undefined); + } return !this.available ? Promise.resolve(undefined) : editorService.openEditor({ resource: this.uri, description: this.origin, From a94415c48d82f19f84033efe1f522e836ff92e9b Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Fri, 9 Jul 2021 14:57:42 -0700 Subject: [PATCH 35/49] Improve stepping and double click source will jump to address. --- .../contrib/debug/browser/debugService.ts | 22 ++++++++++++------- .../contrib/debug/browser/disassemblyView.ts | 14 ++++++++++++ .../workbench/contrib/debug/common/debug.ts | 4 ++-- .../contrib/debug/common/debugModel.ts | 10 +++++++-- .../contrib/debug/common/debugSource.ts | 3 --- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 591bffe91acb3..e628fe003c20f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -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 { DisassemblyView } from 'vs/workbench/contrib/debug/browser/disassemblyView'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -816,14 +817,19 @@ export class DebugService implements IDebugService { if (stackFrame) { const editor = await stackFrame.openInEditor(this.editorService, true); if (editor) { - const control = editor.getControl(); - if (stackFrame && isCodeEditor(control) && control.hasModel()) { - const model = control.getModel(); - const lineNumber = stackFrame.range.startLineNumber; - if (lineNumber >= 1 && lineNumber <= model.getLineCount()) { - const lineContent = control.getModel().getLineContent(lineNumber); - aria.alert(nls.localize({ key: 'debuggingPaused', comment: ['First placeholder is the stack frame name, second is the line number, third placeholder is the reason why debugging is stopped, for example "breakpoint" and the last one is the file line content.'] }, - "{0}:{1}, debugging paused {2}, {3}", stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', lineContent)); + if (editor.getId() === 'workbench.debug.disassemblyView') { + const disassemblyView = editor as DisassemblyView; + disassemblyView.goToAddress(stackFrame.instructionPointerReference); + } else { + const control = editor.getControl(); + if (stackFrame && isCodeEditor(control) && control.hasModel()) { + const model = control.getModel(); + const lineNumber = stackFrame.range.startLineNumber; + if (lineNumber >= 1 && lineNumber <= model.getLineCount()) { + const lineContent = control.getModel().getLineContent(lineNumber); + aria.alert(nls.localize({ key: 'debuggingPaused', comment: ['First placeholder is the stack frame name, second is the line number, third placeholder is the reason why debugging is stopped, for example "breakpoint" and the last one is the file line content.'] }, + "{0}:{1}, debugging paused {2}, {3}", stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', lineContent)); + } } } } diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 1a540292e6360..91b9244aa2071 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -237,6 +237,20 @@ export class DisassemblyView extends EditorPane { } } + goToAddress(address?: string): void { + if (address) { + const index = this.getIndexFromAddress(address); + if (index >= 0) { + this._disassembledInstructions?.reveal(index, 0.5); + } else { + // Address is very far from what's shown in the view now. Clear the table and reload. + this.reloadDisassembly(address); + } + } else { + this.reloadDisassembly(); + } + } + private async scrollUp_LoadDisassembledInstructions(instructionCount: number): Promise { if (this._disassembledInstructions && this._disassembledInstructions.length > 0) { const address: string | undefined = this._disassembledInstructions?.row(0).instruction.address; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index be3de1396b582..9a5607abe39df 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -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'; @@ -374,7 +374,7 @@ export interface IStackFrame extends ITreeElement { forgetScopes(): void; restart(): Promise; toString(): string; - openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): Promise; + openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise; equals(other: IStackFrame): boolean; } diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index bb911233fab3e..6bc3286d43c62 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -19,7 +19,7 @@ import { 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'; @@ -386,7 +386,13 @@ 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 { + async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { + // if DisassemblyView is openned and has disassembly instruction, + // then stay with DisassemblyView + if (editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView` && this.instructionPointerReference) { + return editorService.activeEditorPane; + } + if (this.source.available) { return this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned); } diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index a167120e41649..bf1ac904c0aef 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -73,9 +73,6 @@ export class Source { } openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { - if (editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView`) { - return Promise.resolve(undefined); - } return !this.available ? Promise.resolve(undefined) : editorService.openEditor({ resource: this.uri, description: this.origin, From fa702333194e0ba64d11033fc0e6dd9e08b29424 Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Fri, 9 Jul 2021 16:30:54 -0700 Subject: [PATCH 36/49] Add breakpoint UnitTest --- .../debug/test/browser/breakpoints.test.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index d98cb1cba8ed2..51c5c268dd612 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -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++); From 636b70da227750d1905f76b46a500e0dd6c2c0ce Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Tue, 13 Jul 2021 19:22:40 -0700 Subject: [PATCH 37/49] Decide focus based on stop reason and stepping granularity --- .../debug/browser/debug.contribution.ts | 3 +- .../debug/browser/debugEditorActions.ts | 4 +-- .../contrib/debug/browser/debugSession.ts | 11 ++++++ .../contrib/debug/browser/disassemblyView.ts | 30 ---------------- .../contrib/debug/common/debugModel.ts | 12 ++++--- .../debug/common/disassemblyViewInput.ts | 36 +++++++++++++++++++ 6 files changed, 58 insertions(+), 38 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/common/disassemblyViewInput.ts diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 446b1b78a2f24..2dba9e15aa9ab 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -51,8 +51,9 @@ import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debu 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, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView'; +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(); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index fcc173732f9d4..c83482a1aa213 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -26,7 +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/browser/disassemblyView'; +import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; class ToggleBreakpointAction extends EditorAction2 { constructor() { @@ -156,7 +156,7 @@ class OpenDisassemblyViewAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise { if (editor.hasModel()) { const editorService = accessor.get(IEditorService); - editorService.openEditor(DisassemblyViewInput.instance); + editorService.openEditor(DisassemblyViewInput.instance, { pinned: true }); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index cb14e738344c8..9fee2704c044b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -555,11 +555,19 @@ export class DebugSession implements IDebugSession { await this.raw.restartFrame({ frameId }, threadId); } + 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 { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'next')); } + this.setLastSteppingGranularity(threadId, granularity); await this.raw.next({ threadId, granularity }); } @@ -568,6 +576,7 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepIn')); } + this.setLastSteppingGranularity(threadId, granularity); await this.raw.stepIn({ threadId, targetId, granularity }); } @@ -576,6 +585,7 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepOut')); } + this.setLastSteppingGranularity(threadId, granularity); await this.raw.stepOut({ threadId, granularity }); } @@ -584,6 +594,7 @@ export class DebugSession implements IDebugSession { throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'stepBack')); } + this.setLastSteppingGranularity(threadId, granularity); await this.raw.stepBack({ threadId, granularity }); } diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 91b9244aa2071..3314fc3922705 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -16,7 +16,6 @@ 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 { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { CONTEXT_DISASSEMBLE_VIEW_FOCUS, 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'; @@ -557,35 +556,6 @@ class InstructionRenderer extends Disposable implements ITableRenderer { getWidgetAriaLabel(): string { diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 6bc3286d43c62..ca30630f90e5c 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -24,6 +24,7 @@ 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,7 +322,7 @@ export class StackFrame implements IStackFrame { private scopes: Promise | undefined; constructor( - public thread: IThread, + public thread: Thread, public frameId: number, public source: Source, public name: string, @@ -387,10 +388,10 @@ export class StackFrame implements IStackFrame { } async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { - // if DisassemblyView is openned and has disassembly instruction, - // then stay with DisassemblyView - if (editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView` && this.instructionPointerReference) { - return editorService.activeEditorPane; + const threadStopReason = this.thread.stoppedDetails?.reason; + if (threadStopReason === 'instruction breakpoint' || + (threadStopReason === 'step' && this.thread.lastSteppingGranularity === 'instruction')) { + return editorService.openEditor(DisassemblyViewInput.instance, { pinned: true }); } if (this.source.available) { @@ -411,6 +412,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 = []; diff --git a/src/vs/workbench/contrib/debug/common/disassemblyViewInput.ts b/src/vs/workbench/contrib/debug/common/disassemblyViewInput.ts new file mode 100644 index 0000000000000..f66b095f68611 --- /dev/null +++ b/src/vs/workbench/contrib/debug/common/disassemblyViewInput.ts @@ -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; + } + +} From 3f8801c02680d1e765b0b2ffa09b885714615633 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 15 Jul 2021 12:51:13 -0700 Subject: [PATCH 38/49] Also check instructionPointerReference to decide which editor to show --- src/vs/workbench/contrib/debug/common/debugModel.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index ca30630f90e5c..331077120f628 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -389,8 +389,9 @@ export class StackFrame implements IStackFrame { async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { const threadStopReason = this.thread.stoppedDetails?.reason; - if (threadStopReason === 'instruction breakpoint' || - (threadStopReason === 'step' && this.thread.lastSteppingGranularity === 'instruction')) { + if (this.instructionPointerReference && + (threadStopReason === 'instruction breakpoint' || + (threadStopReason === 'step' && this.thread.lastSteppingGranularity === 'instruction'))) { return editorService.openEditor(DisassemblyViewInput.instance, { pinned: true }); } From a5d10cb65ae0eb48bd8bed6886bdbbe2db4f482c Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Thu, 15 Jul 2021 18:07:27 -0700 Subject: [PATCH 39/49] Show disassembly view when clicking on instruction breakpoint in breakpoints view --- .../contrib/debug/browser/breakpointsView.ts | 5 ++++- .../contrib/debug/browser/disassemblyView.ts | 14 +++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 2488d2b982a30..53282c0bcfa3a 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -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.$; @@ -161,7 +163,8 @@ export class BreakpointsView extends ViewPane { 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 diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 3314fc3922705..b4ada64d15daa 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -73,8 +73,7 @@ export class DisassemblyView extends EditorPane { this._register(editorService.onDidActiveEditorChange(() => { if (this.editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView`) { this._disassemblyViewFocus.set(true); - } - else { + } else { this._disassemblyViewFocus.reset(); } })); @@ -241,12 +240,13 @@ export class DisassemblyView extends EditorPane { const index = this.getIndexFromAddress(address); if (index >= 0) { this._disassembledInstructions?.reveal(index, 0.5); - } else { - // Address is very far from what's shown in the view now. Clear the table and reload. - this.reloadDisassembly(address); + return; } - } else { - this.reloadDisassembly(); + } + // 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. + if (this._debugService.state === State.Stopped) { + this.reloadDisassembly(address); } } From 6be31ee164ef25f6ebe2549082f8d30710680cf3 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Fri, 16 Jul 2021 14:47:21 -0700 Subject: [PATCH 40/49] Improve stepping --- .../contrib/debug/browser/debugCommands.ts | 62 +++++++------------ .../contrib/debug/browser/debugService.ts | 16 ++++- .../contrib/debug/browser/disassemblyView.ts | 23 ++----- .../workbench/contrib/debug/common/debug.ts | 2 +- 4 files changed, 43 insertions(+), 60 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index bb1e4ed216067..fadc463e4712e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -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, CONTEXT_DISASSEMBLE_VIEW_FOCUS } 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'; @@ -41,9 +41,6 @@ export const TERMINATE_THREAD_ID = 'workbench.action.debug.terminateThread'; export const STEP_OVER_ID = 'workbench.action.debug.stepOver'; export const STEP_INTO_ID = 'workbench.action.debug.stepInto'; export const STEP_OUT_ID = 'workbench.action.debug.stepOut'; -export const INSTRUCTION_STEP_OVER_ID = 'workbench.action.debug.instructionStepOver'; -export const INSTRUCTION_STEP_INTO_ID = 'workbench.action.debug.instructionStepInto'; -export const INSTRUCTION_STEP_OUT_ID = 'workbench.action.debug.instructionStepOut'; export const PAUSE_ID = 'workbench.action.debug.pause'; export const DISCONNECT_ID = 'workbench.action.debug.disconnect'; export const STOP_ID = 'workbench.action.debug.stop'; @@ -73,8 +70,6 @@ export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'" export const DEBUG_START_LABEL = nls.localize('startDebug', "Start Debugging"); export const DEBUG_RUN_LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging"); -const INSTRUCTION_PRIORITY = 5; - interface CallStackContext { sessionId: string; threadId: string; @@ -261,7 +256,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) => { - getThreadAndRun(accessor, context, (thread: IThread) => thread.next()); + const contextKeyService = accessor.get(IContextKeyService); + const disassemblyViewFocus = CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService); + if (disassemblyViewFocus) { + getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction')); + } else { + getThreadAndRun(accessor, context, (thread: IThread) => thread.next()); + } } }); @@ -272,7 +273,12 @@ 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) => { - getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn()); + 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()); + } } }); @@ -282,38 +288,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyMod.Shift | KeyCode.F11, when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { - getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut()); - } -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: INSTRUCTION_STEP_OVER_ID, - weight: KeybindingWeight.WorkbenchContrib + INSTRUCTION_PRIORITY, - primary: isWeb ? (KeyMod.Alt | KeyCode.F10) : KeyCode.F10, // Browsers do not allow F10 to be binded so we have to bind an alternative - when: ContextKeyExpr.and(CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), - handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { - getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction')); - } -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: INSTRUCTION_STEP_INTO_ID, - weight: KeybindingWeight.WorkbenchContrib + INSTRUCTION_PRIORITY + 10, // Have a stronger weight to have priority over full screen when debugging - primary: (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11, // Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut - // Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times - when: ContextKeyExpr.and(CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_DEBUG_STATE.notEqualsTo('inactive')), - handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { - getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn('instruction')); - } -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: INSTRUCTION_STEP_OUT_ID, - weight: KeybindingWeight.WorkbenchContrib + INSTRUCTION_PRIORITY, - primary: KeyMod.Shift | KeyCode.F11, - when: ContextKeyExpr.and(CONTEXT_DISASSEMBLE_VIEW_FOCUS, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), - handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { - getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut('instruction')); + 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()); + } } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index e628fe003c20f..f59f84e6e379a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -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'; @@ -52,6 +52,7 @@ import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/work import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { IEditorInput } from 'vs/workbench/common/editor'; import { DisassemblyView } from 'vs/workbench/contrib/debug/browser/disassemblyView'; +import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -73,6 +74,7 @@ export class DebugService implements IDebugService { private inDebugMode!: IContextKey; private debugUx!: IContextKey; private breakpointsExist!: IContextKey; + private disassemblyViewFocus!: IContextKey; private breakpointsToSendOnResourceSaved: Set; private initializing = false; private previousState: State | 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 { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index b4ada64d15daa..adc61a75fb8f1 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -16,7 +16,7 @@ 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_DISASSEMBLE_VIEW_FOCUS, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, DISASSEMBLY_VIEW_ID, IDebugService, IInstructionBreakpoint, State } from 'vs/workbench/contrib/debug/common/debug'; +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'; @@ -48,7 +48,6 @@ export class DisassemblyView extends EditorPane { private _onDidChangeStackFrame: Emitter; private _previousDebuggingState: State; private _instructionBpList: readonly IInstructionBreakpoint[] = []; - _disassemblyViewFocus: IContextKey; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -56,12 +55,15 @@ export class DisassemblyView extends EditorPane { @IStorageService storageService: IStorageService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, @IDebugService private readonly _debugService: IDebugService, - @IEditorService private readonly editorService: IEditorService, + @IEditorService editorService: IEditorService, ) { super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService); + this._disassembledInstructions = undefined; + this._onDidChangeStackFrame = new Emitter(); + this._previousDebuggingState = _debugService.state; + this._fontInfo = BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel(), getPixelRatio()); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor')) { @@ -69,19 +71,6 @@ export class DisassemblyView extends EditorPane { this._disassembledInstructions?.rerender(); } })); - - this._register(editorService.onDidActiveEditorChange(() => { - if (this.editorService.activeEditorPane?.getId() === `workbench.debug.disassemblyView`) { - this._disassemblyViewFocus.set(true); - } else { - this._disassemblyViewFocus.reset(); - } - })); - - this._disassembledInstructions = undefined; - this._onDidChangeStackFrame = new Emitter(); - this._previousDebuggingState = _debugService.state; - this._disassemblyViewFocus = CONTEXT_DISASSEMBLE_VIEW_FOCUS.bindTo(contextKeyService); } get fontInfo() { return this._fontInfo; } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 9a5607abe39df..0043e77644f70 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -82,7 +82,7 @@ export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exce export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('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('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('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") }); -export const CONTEXT_DISASSEMBLE_VIEW_FOCUS = new RawContextKey('disassembleViewFocus', false, { type: 'boolean', description: nls.localize('disassembleViewFocus', "True when the Disassembly View is focused.") }); +export const CONTEXT_DISASSEMBLY_VIEW_FOCUS = new RawContextKey('disassemblyViewFocus', false, { type: 'boolean', description: nls.localize('disassemblyViewFocus', "True when the Disassembly View is focused.") }); export const CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST = new RawContextKey('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'; From 6cc269ef4968553f3cb1b09625e0aed61cfd71c0 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Fri, 16 Jul 2021 14:51:33 -0700 Subject: [PATCH 41/49] Revert a test change --- src/vs/workbench/contrib/debug/browser/debugCommands.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index fadc463e4712e..908db924ed113 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -257,8 +257,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { const contextKeyService = accessor.get(IContextKeyService); - const disassemblyViewFocus = CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService); - if (disassemblyViewFocus) { + if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) { getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction')); } else { getThreadAndRun(accessor, context, (thread: IThread) => thread.next()); From 7d4ba0c920e0118e521d73e9d249bd4e74895467 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Mon, 19 Jul 2021 11:54:13 -0700 Subject: [PATCH 42/49] Step back --- src/vs/workbench/contrib/debug/browser/debugCommands.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 908db924ed113..2cd04022523e8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -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()); + } } }); From aaaae0c680c24056275cef9dcc9723968f20557e Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Mon, 19 Jul 2021 16:26:38 -0700 Subject: [PATCH 43/49] Disable open disassembly action when a stackframe without a instructionPointerReference is selected --- src/vs/workbench/contrib/debug/common/debugViewModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index 021362f06dece..8be2cff667f0e 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -80,7 +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(session ? !!session?.capabilities.supportsDisassembleRequest : false); + this.disassembleRequestSupported.set(!!stackFrame?.instructionPointerReference && !!session?.capabilities.supportsDisassembleRequest); const attach = !!session && isSessionAttach(session); this.focusedSessionIsAttach.set(attach); }); From 420140fe7da2041a268c18e87d9df6eec6b4a30d Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Tue, 20 Jul 2021 18:35:52 -0700 Subject: [PATCH 44/49] Improve instruction breakpoint tooltip --- .../contrib/debug/browser/breakpointsView.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 53282c0bcfa3a..1237aa3e992fd 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -1067,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[] = []; From 526e1774ed84003b9a0bd050446bfd62ddf8f84a Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Tue, 20 Jul 2021 20:39:14 -0700 Subject: [PATCH 45/49] Better go to address --- .../contrib/debug/browser/debugService.ts | 2 +- .../contrib/debug/browser/disassemblyView.ts | 92 +++++++++---------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index f59f84e6e379a..28a81a7f848ec 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -831,7 +831,7 @@ export class DebugService implements IDebugService { if (stackFrame) { const editor = await stackFrame.openInEditor(this.editorService, true); if (editor) { - if (editor.getId() === 'workbench.debug.disassemblyView') { + if (editor.input === DisassemblyViewInput.instance) { const disassemblyView = editor as DisassemblyView; disassemblyView.goToAddress(stackFrame.instructionPointerReference); } else { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index adc61a75fb8f1..1b670e43da31c 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -43,7 +43,6 @@ export class DisassemblyView extends EditorPane { // Used in instruction renderer private _fontInfo: BareFontInfo; - private _currentInstructionAddress: string | undefined; private _disassembledInstructions: WorkbenchTable | undefined; private _onDidChangeStackFrame: Emitter; private _previousDebuggingState: State; @@ -75,7 +74,9 @@ export class DisassemblyView extends EditorPane { get fontInfo() { return this._fontInfo; } - get currentInstructionAddress() { return this._currentInstructionAddress; } + get currentInstructionAddress() { + return this._debugService.getViewModel().focusedStackFrame?.instructionPointerReference; + } get onDidChangeStackFrame() { return this._onDidChangeStackFrame.event; } @@ -143,33 +144,8 @@ export class DisassemblyView extends EditorPane { })); this._register(this._debugService.getViewModel().onDidFocusStackFrame((stackFrame) => { - if (this._disassembledInstructions && this._currentInstructionAddress !== stackFrame.stackFrame?.instructionPointerReference) { - this._currentInstructionAddress = stackFrame.stackFrame?.instructionPointerReference; - if (this._currentInstructionAddress) { - // console.log(`DisassemblyView: ${this._currentInstructionAddress}`); - const index = this.getIndexFromAddress(this._currentInstructionAddress); - 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.ceil((this._disassembledInstructions.scrollTop + this._disassembledInstructions.renderHeight) / this.fontInfo.lineHeight); - if (index > topElement && index < bottomElement) { - // Inside the viewport, don't do anything here - } else if (index < topElement && index > topElement - 5) { - // Not too far from top, review it at the top - this._disassembledInstructions.reveal(index, 0); - } else if (index > bottomElement && index < bottomElement + 5) { - // Not too far from bottom, review it at the bottom - this._disassembledInstructions.reveal(index, 1); - } else { - // Far from the current viewport, reveal it - this._disassembledInstructions.reveal(index, 0.5); - } - } else { - // Stack frame is very far from what's shown in the view now. Clear the table and reload. - this.reloadDisassembly(); - } - } - + if (this._disassembledInstructions) { + this.goToAddress(); this._onDidChangeStackFrame.fire(); } })); @@ -224,18 +200,43 @@ export class DisassemblyView extends EditorPane { } } + /** + * Go to the address provided. If no address is provided, reveal the address of the currently focused stack frame. + */ goToAddress(address?: string): void { - if (address) { - const index = this.getIndexFromAddress(address); - if (index >= 0) { - this._disassembledInstructions?.reveal(index, 0.5); + 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.ceil((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); } - } - // 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. - if (this._debugService.state === State.Stopped) { - this.reloadDisassembly(address); + } 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); } } @@ -258,20 +259,17 @@ export class DisassemblyView extends EditorPane { } private async loadDisassembledInstructions(address: string | undefined, instructionOffset: number, instructionCount: number): Promise { - // if address is null, then use current stackframe. + // if address is null, then use current stack frame. if (!address) { - const frame = this._debugService.getViewModel().focusedStackFrame; - if (frame?.instructionPointerReference) { - address = frame.instructionPointerReference; - this._currentInstructionAddress = address; - } else { - return false; - } + 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); + const resultEntries = await session?.disassemble(address, 0, instructionOffset, instructionCount); if (session && resultEntries && this._disassembledInstructions) { const newEntries: IDisassembledInstructionEntry[] = []; From 93c8ad6ecbaff78c66a721a0fc86ded391613295 Mon Sep 17 00:00:00 2001 From: Xinyu Sui Date: Wed, 21 Jul 2021 13:20:02 -0700 Subject: [PATCH 46/49] Improve stepping UI --- src/vs/workbench/contrib/debug/browser/debugService.ts | 4 +--- src/vs/workbench/contrib/debug/browser/disassemblyView.ts | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 28a81a7f848ec..bb1ddb12b4ae6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -51,7 +51,6 @@ 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 { DisassemblyView } from 'vs/workbench/contrib/debug/browser/disassemblyView'; import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; export class DebugService implements IDebugService { @@ -832,8 +831,7 @@ export class DebugService implements IDebugService { const editor = await stackFrame.openInEditor(this.editorService, true); if (editor) { if (editor.input === DisassemblyViewInput.instance) { - const disassemblyView = editor as DisassemblyView; - disassemblyView.goToAddress(stackFrame.instructionPointerReference); + // Go to address is invoked via setFocus } else { const control = editor.getControl(); if (stackFrame && isCodeEditor(control) && control.hasModel()) { diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 1b670e43da31c..8a505a107ddc0 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -219,14 +219,14 @@ export class DisassemblyView extends EditorPane { 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.ceil((this._disassembledInstructions.scrollTop + this._disassembledInstructions.renderHeight) / 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) { + } 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) { + } else if (index >= bottomElement && index < bottomElement + 5) { // Not too far from bottom, review it at the bottom return this._disassembledInstructions.reveal(index, 1); } else { From a0e2977eadd97c91744d9a807a27139883cc9a17 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 22 Jul 2021 09:57:27 +0200 Subject: [PATCH 47/49] call sendInstructionBreakpoints for persisted breakpoints --- src/vs/workbench/contrib/debug/browser/debugService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index bb1ddb12b4ae6..d4f9d6964013f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -1007,6 +1007,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); } From 7fb6a2ef1cb9f5693280287d55dac4be5aa7ceeb Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 22 Jul 2021 09:57:57 +0200 Subject: [PATCH 48/49] check correct capability supportsInstructionBreakpoints --- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index a3533aee1ed28..267c2e977dec9 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -498,7 +498,7 @@ export class RawDebugSession implements IDisposable { } async setInstructionBreakpoints(args: DebugProtocol.SetInstructionBreakpointsArguments): Promise { - if (this.capabilities.supportsDisassembleRequest) { + if (this.capabilities.supportsInstructionBreakpoints) { return await this.send('setInstructionBreakpoints', args); } From c4920642ce13b5663e669d273ad0031e8a55ed2a Mon Sep 17 00:00:00 2001 From: "Yue (Felix) Huang" Date: Thu, 22 Jul 2021 01:24:09 -0700 Subject: [PATCH 49/49] Remove getdisassemble as it is not used from DebugService --- .../debug/browser/debug.contribution.ts | 2 +- .../contrib/debug/browser/debugService.ts | 20 ------------------- .../workbench/contrib/debug/common/debug.ts | 3 +-- .../contrib/debug/test/browser/mockDebug.ts | 4 ---- 4 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 2dba9e15aa9ab..8452fe6600753 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -378,7 +378,7 @@ 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 editor +// Register disassembly view Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index d4f9d6964013f..5e3c5cad6ab9c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -1012,26 +1012,6 @@ export class DebugService implements IDebugService { await this.sendExceptionBreakpoints(session); } - async getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise { - const session = this.getViewModel().focusedSession; - if (session) { - const frame = this.getViewModel().focusedStackFrame; - if (frame?.instructionPointerReference) { - return session.disassemble(frame?.instructionPointerReference, offset, instructionOffset, instructionCount); - } - } - - return new Promise((resolve, reject) => { - const result: DebugProtocol.DisassembledInstruction[] = []; - - for (let i = 0; i < instructionCount; i++) { - result.push({ address: `${i + offset}`, instruction: `debugger is not available.` }); - } - - resolve(result); - }); - } - private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { const breakpointsToSend = this.model.getBreakpoints({ uri: modelUri, enabledOnly: true }); await sendToOneOrAllSessions(this.model, session, s => s.sendBreakpoints(modelUri, breakpointsToSend, sourceModified)); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 0043e77644f70..db5b7b8b6cb0c 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -906,6 +906,7 @@ export interface IDebugService { /** * 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; @@ -965,8 +966,6 @@ export interface IDebugService { */ sourceIsNotAvailable(uri: uri): void; - getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise; - /** * Gets the current debug model. */ diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 688b6cbb71b9b..c507e2caf5256 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -157,10 +157,6 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } - getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise { - throw new Error('not implemented'); - } - logToRepl(session: IDebugSession, value: string): void { } sourceIsNotAvailable(uri: uri): void { }