Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disassembly view #125737

Merged
merged 51 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
45bdf6b
disassembly view: initial commit
xisui-MSFT Jun 2, 2021
aada2f4
Rendering table, start to work on scrolling
xisui-MSFT Jun 3, 2021
a83ef3a
First Compile
yuehuang010 Jun 4, 2021
0e7e274
Infinite scrolling
xisui-MSFT Jun 7, 2021
f4015e1
Breakpoints work, but can still be improved
xisui-MSFT Jun 10, 2021
01dd48e
Improve breakpoints UI
xisui-MSFT Jun 11, 2021
fc3e9cd
Add instruction to viewer
yuehuang010 Jun 10, 2021
b55f201
Add debug to view.
yuehuang010 Jun 11, 2021
16c268e
Fix scrolling
yuehuang010 Jun 12, 2021
8273972
Re-enable disassemble.
yuehuang010 Jun 13, 2021
17ffdc7
Some fixes and resolving some comments
xisui-MSFT Jun 15, 2021
a9cf999
Style changes
xisui-MSFT Jun 15, 2021
d2dc742
Switch to using Address + Count for offset.
yuehuang010 Jun 15, 2021
4b681e1
Fix scroll up.
yuehuang010 Jun 16, 2021
ec80e33
Fix BP/pos icon from appearing while scrolling.
yuehuang010 Jun 16, 2021
707ae1a
Add set and remove InstructionBreakpoint.
yuehuang010 Jun 21, 2021
5914a40
Added accessiblity provider
xisui-MSFT Jun 22, 2021
451797c
Properly set CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED
xisui-MSFT Jun 23, 2021
cdc3714
Stack frame icon
xisui-MSFT Jun 23, 2021
fb797f6
Properly dispose event listeners in breakpont renderer
xisui-MSFT Jun 23, 2021
a6865b4
Revert ext point changes in debugAdapterManager.ts and debugSchemas.ts
xisui-MSFT Jun 24, 2021
b8e4d3e
More to revert
xisui-MSFT Jun 24, 2021
d160870
Reset disposables array after disposing
xisui-MSFT Jun 24, 2021
c2119a4
Current instruction line highlight
xisui-MSFT Jun 24, 2021
73fa288
wip
yuehuang010 Jun 24, 2021
a2f7ea5
Add InstructionBreakpoint to BP View
yuehuang010 Jun 28, 2021
fc659a8
Show 'open disassembly view' option only when language in currently f…
xisui-MSFT Jun 29, 2021
aa5940f
Fix breakpoint view. Remove ContextKey.
yuehuang010 Jul 2, 2021
cc56194
Add ContextKey when focus, step disassembly.
yuehuang010 Jul 3, 2021
e81f4cc
Fix spelling.
yuehuang010 Jul 7, 2021
cc1d75a
WIP
yuehuang010 Jul 7, 2021
4114792
Fix build error
yuehuang010 Jul 7, 2021
b197a34
Fix rendering BP when restarting debugging.
yuehuang010 Jul 9, 2021
6f38fee
Don't switch focus when DisassemblyView is in Focus.
yuehuang010 Jul 9, 2021
a94415c
Improve stepping and double click source will jump to address.
yuehuang010 Jul 9, 2021
fa70233
Add breakpoint UnitTest
yuehuang010 Jul 9, 2021
636b70d
Decide focus based on stop reason and stepping granularity
xisui-MSFT Jul 14, 2021
3f8801c
Also check instructionPointerReference to decide which editor to show
xisui-MSFT Jul 15, 2021
a5d10cb
Show disassembly view when clicking on instruction breakpoint in brea…
xisui-MSFT Jul 16, 2021
6be31ee
Improve stepping
xisui-MSFT Jul 16, 2021
6cc269e
Revert a test change
xisui-MSFT Jul 16, 2021
600cdae
Merge branch 'main' into dev/xisui/disassembly
xisui-MSFT Jul 16, 2021
7d4ba0c
Step back
xisui-MSFT Jul 19, 2021
aaaae0c
Disable open disassembly action when a stackframe without a instructi…
xisui-MSFT Jul 19, 2021
9dd774e
Merge branch 'main' into dev/xisui/disassembly
xisui-MSFT Jul 19, 2021
420140f
Improve instruction breakpoint tooltip
xisui-MSFT Jul 21, 2021
526e177
Better go to address
xisui-MSFT Jul 21, 2021
93c8ad6
Improve stepping UI
xisui-MSFT Jul 21, 2021
a0e2977
call sendInstructionBreakpoints for persisted breakpoints
weinand Jul 22, 2021
7fb6a2e
check correct capability supportsInstructionBreakpoints
weinand Jul 22, 2021
c492064
Remove getdisassemble as it is not used from DebugService
yuehuang010 Jul 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/vs/workbench/api/browser/mainThreadDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -337,7 +337,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return undefined;
}

private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint>)): Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto> {
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>)): Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also have the IInstructionBreakpoint as the return type.
Also make sure the conversion is properly done please.

Copy link
Contributor

@weinand weinand Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@isidorn supporting instruction breakpoints in the debug extension API requires more work (which is not part of this PR)

Please create a feature request "Support instruction breakpoints in debug API" for this.

return bps.map(bp => {
if ('name' in bp) {
const fbp = <IFunctionBreakpoint>bp;
Expand Down
126 changes: 118 additions & 8 deletions src/vs/workbench/contrib/debug/browser/breakpointsView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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.$;

Expand All @@ -57,10 +59,10 @@ function createCheckbox(): HTMLInputElement {

const MAX_VISIBLE_BREAKPOINTS = 9;
export function getExpandedBodySize(model: IDebugModel, countLimit: number): number {
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length;
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length + model.getInstructionBreakpoints().length;
return Math.min(countLimit, length) * 22;
}
type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint;
type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint | IInstructionBreakpoint;

interface InputBoxData {
breakpoint: IFunctionBreakpoint | IExceptionBreakpoint;
Expand Down Expand Up @@ -120,7 +122,8 @@ export class BreakpointsView extends ViewPane {
new ExceptionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService),
this.instantiationService.createInstance(FunctionBreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType),
this.instantiationService.createInstance(DataBreakpointsRenderer),
new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService, this.labelService)
new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService, this.labelService),
this.instantiationService.createInstance(InstructionBreakpointsRenderer),
], {
identityProvider: { getId: (element: IEnablement) => element.getId() },
multipleSelectionSupport: false,
Expand All @@ -142,6 +145,8 @@ export class BreakpointsView extends ViewPane {
await this.debugService.removeFunctionBreakpoints(element.getId());
} else if (element instanceof DataBreakpoint) {
await this.debugService.removeDataBreakpoints(element.getId());
} else if (element instanceof InstructionBreakpoint) {
await this.debugService.removeInstructionBreakpoints(element.instructionReference);
}
});

Expand All @@ -157,6 +162,10 @@ export class BreakpointsView extends ViewPane {
if (e.element instanceof Breakpoint) {
openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService);
}
if (e.element instanceof InstructionBreakpoint) {
const disassemblyView = await this.editorService.openEditor(DisassemblyViewInput.instance);
(disassemblyView as DisassemblyView).goToAddress(e.element.instructionReference);
}
if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.inputBoxData?.breakpoint) {
// double click
this.renderInputBox({ breakpoint: e.element, type: 'name' });
Expand Down Expand Up @@ -214,7 +223,8 @@ export class BreakpointsView extends ViewPane {
private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
const element = e.element;
const type = element instanceof Breakpoint ? 'breakpoint' : element instanceof ExceptionBreakpoint ? 'exceptionBreakpoint' :
element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' : undefined;
element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' :
element instanceof InstructionBreakpoint ? 'instructionBreakpoint' : undefined;
this.breakpointItemType.set(type);
const session = this.debugService.getViewModel().focusedSession;
const conditionSupported = element instanceof ExceptionBreakpoint ? element.supportsCondition : (!session || !!session.capabilities.supportsConditionalBreakpoints);
Expand Down Expand Up @@ -288,7 +298,7 @@ export class BreakpointsView extends ViewPane {

private get elements(): BreakpointItem[] {
const model = this.debugService.getModel();
const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints());
const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()).concat(model.getInstructionBreakpoints());

return elements as BreakpointItem[];
}
Expand Down Expand Up @@ -326,6 +336,9 @@ class BreakpointsDelegate implements IListVirtualDelegate<BreakpointItem> {
if (element instanceof DataBreakpoint) {
return DataBreakpointsRenderer.ID;
}
if (element instanceof InstructionBreakpoint) {
return InstructionBreakpointsRenderer.ID;
}

return '';
}
Expand Down Expand Up @@ -362,6 +375,10 @@ interface IDataBreakpointTemplateData extends IBaseBreakpointWithIconTemplateDat
accessType: HTMLElement;
}

interface IInstructionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
address: HTMLElement;
}

interface IFunctionBreakpointInputTemplateData {
inputBox: InputBox;
checkbox: HTMLInputElement;
Expand Down Expand Up @@ -673,6 +690,71 @@ class DataBreakpointsRenderer implements IListRenderer<DataBreakpoint, IDataBrea
}
}

class InstructionBreakpointsRenderer implements IListRenderer<IInstructionBreakpoint, IInstructionBreakpointTemplateData> {

constructor(
@IDebugService private readonly debugService: IDebugService,
@ILabelService private readonly labelService: ILabelService
) {
// noop
}

static readonly ID = 'instructionBreakpoints';

get templateId() {
return InstructionBreakpointsRenderer.ID;
}

renderTemplate(container: HTMLElement): IInstructionBreakpointTemplateData {
Copy link
Contributor

@weinand weinand Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@isidorn we need a new icon for instruction breakpoints.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iidorn -> @isidorn ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jogo- thanks for pointing that out

const data: IInstructionBreakpointTemplateData = Object.create(null);
data.breakpoint = dom.append(container, $('.breakpoint'));

data.icon = $('.icon');
data.checkbox = createCheckbox();
data.toDispose = [];
data.elementDisposable = [];
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
}));

dom.append(data.breakpoint, data.icon);
dom.append(data.breakpoint, data.checkbox);

data.name = dom.append(data.breakpoint, $('span.name'));

data.address = dom.append(data.breakpoint, $('span.file-path'));
data.actionBar = new ActionBar(data.breakpoint);
data.toDispose.push(data.actionBar);

return data;
}

renderElement(breakpoint: IInstructionBreakpoint, index: number, data: IInstructionBreakpointTemplateData): void {
data.context = breakpoint;
data.breakpoint.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());

data.name.textContent = breakpoint.instructionReference;
data.checkbox.checked = breakpoint.enabled;

const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService);
data.icon.className = ThemeIcon.asClassName(icon);
data.breakpoint.title = breakpoint.message || message || '';

const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
if (debugActive && !breakpoint.verified) {
data.breakpoint.classList.add('disabled');
}
}

disposeElement(_element: IInstructionBreakpoint, _index: number, templateData: IInstructionBreakpointTemplateData): void {
dispose(templateData.elementDisposable);
}

disposeTemplate(templateData: IInstructionBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
}

class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IFunctionBreakpointInputTemplateData> {

constructor(
Expand Down Expand Up @@ -927,7 +1009,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
}

export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, icon: ThemeIcon } {
export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: BreakpointItem, labelService?: ILabelService): { message?: string, icon: ThemeIcon } {
const debugActive = state === State.Running || state === State.Stopped;

const breakpointIcon = breakpoint instanceof DataBreakpoint ? icons.dataBreakpoint : breakpoint instanceof FunctionBreakpoint ? icons.functionBreakpoint : breakpoint.logMessage ? icons.logBreakpoint : icons.breakpoint;
Expand Down Expand Up @@ -985,6 +1067,32 @@ export function getBreakpointMessageAndIcon(state: State, breakpointsActivated:
};
}

if (breakpoint instanceof InstructionBreakpoint) {
if (!breakpoint.supported) {
return {
icon: breakpointIcon.unverified,
message: localize('instructionBreakpointUnsupported', "Instruction breakpoints not supported by this debug type"),
};
}
const messages: string[] = [];
if (breakpoint.message) {
messages.push(breakpoint.message);
} else if (breakpoint.instructionReference) {
messages.push(localize('instructionBreakpointAtAddress', "Instruction breakpoint at address {0}", breakpoint.instructionReference));
} else {
messages.push(localize('instructionBreakpoint', "Instruction breakpoint"));
}

if (breakpoint.hitCondition) {
messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
}

return {
icon: breakpointIcon.regular,
message: appendMessage(messages.join('\n'))
};
}

if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) {
const messages: string[] = [];

Expand Down Expand Up @@ -1099,6 +1207,8 @@ registerAction2(class extends Action2 {
await debugService.removeFunctionBreakpoints(breakpoint.getId());
} else if (breakpoint instanceof DataBreakpoint) {
await debugService.removeDataBreakpoints(breakpoint.getId());
} else if (breakpoint instanceof InstructionBreakpoint) {
await debugService.removeInstructionBreakpoints(breakpoint.instructionReference);
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading