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

Disassembly view #125737

merged 51 commits into from
Jul 22, 2021

Conversation

xisui-MSFT
Copy link
Contributor

This PR fixes #124163

@isidorn isidorn assigned isidorn and unassigned eamodio Jun 8, 2021
@isidorn isidorn added the debug Debug viewlet, configurations, breakpoints, adapter issues label Jun 8, 2021
@isidorn isidorn added this to the June 2021 milestone Jun 8, 2021
src/vs/workbench/contrib/debug/test/browser/mockDebug.ts Outdated Show resolved Hide resolved
}

// disassembly extension point
export const disassemblyExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IDisassemblyContribution[]>({
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need a new contribution point for this? I think not, and all of this can be achieved using the capability supportsDisassembleRequest.
So I suggest to remove this.

fyi @weinand

Copy link
Contributor

Choose a reason for hiding this comment

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

@xisui-MSFT what's the purpose of this contribution point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that supportsDisassembleRequest is set when the debugger supports disassemble request? However, we don't want the "show disassembly"/"go to disassembly" option to exist in files other than those of certain languages.

Ultimately, our goal is to achieve similar functions as in VS. I.e., when right clicking a certain line in source (may not be %rip), go to disassembly corresponds to that line.

Copy link
Contributor

Choose a reason for hiding this comment

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

@xisui-MSFT Thanks, now I understand the problem you are addressing with the contribution point.

I think for now it is OK to introduce an (internal) contribution point.
But since a general solution will involve public extension API, we'll have to consider more scenarios in different debuggers before we can create a "proposed API" (that will have to go through the regular VS Code API process).

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternative idea: let's not introduce a new contribution point but instead use the languages contribution point which debug extension already use to say they are interested in some langauges. Mock debug example.

So the logic would be, if there is an active debug session which supportsDisassembleRequest then show the action for all editor language types that are specified under the languages contribution of that debug extension.

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED shuold be set here
based on the focused session

For the new ext point as we discussed above this is not needed for step one, simply use the debuggers.languages contribution point for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm fine with either way... My point is that looks like we can't use IDebugService in the precondition of the action, but seems like IDebugService is the only way to access this ext point.

Copy link
Contributor

Choose a reason for hiding this comment

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

@xisui-MSFT define the precondition in common/debug.ts as all other preconditiions are defined. And they you will be able to access it from the action. And the debugViewModel will actually "implement" the precondition based on focused session.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess you meant to make it a context key just like CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED? Although I can change debugAdapterManager to be able to access the ext point, I'm not sure how to get the language id of the current editor in it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using workbench contribution as we discussed offline.

@@ -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';
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good 👍

private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50;

private _fontInfo: BareFontInfo;
private _disassembledInstructions: WorkbenchTable<IDisassembledInstructionEntry> | null;
Copy link
Contributor

Choose a reason for hiding this comment

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

My code style is to use undefined not null. So unless you have a strong reason to use null I suggest to go with undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I don't have a preference on this and I can change it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Great, thanks

],
{
identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address },
horizontalScrolling: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Make sure to also have an accessibilityProvider

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will add a TODO to make sure I don't forget this. I wanted to add it after adding some proper rendering.

Copy link
Contributor

Choose a reason for hiding this comment

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

Great thanks. As for inspiration you can checkout some other accessibilityProviders in debug lang. Try to have concise but descriptive messages with the least important piece of info at the end.


export class DisassemblyView extends EditorPane {

private static readonly NUM_INSTRUCTIONS_TO_LOAD = 50;
Copy link
Contributor

Choose a reason for hiding this comment

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

50 is a very low number here. The scroll bar will considerably jump as soon as the user scrolls a bit.
I suggest to use a much larger number like a 1000 and make sure to have a lazily model that is evaluated only once the user scrolls all the way up to it (the element gets rendered)

Copy link
Contributor Author

@xisui-MSFT xisui-MSFT Jun 8, 2021

Choose a reason for hiding this comment

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

In fact we don't want this number to be too large. The code may run on remote devices than don't have a very large communication bandwidth. I agree we can re-evaluate this value later though.

Copy link
Contributor

Choose a reason for hiding this comment

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

The table scrolling will be very weird and the UI wont be smooth. It wont be a nice user experience.
If remote is the only concern I suggest to maybe even change this number depending if we are in a remote scenario.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the UI looks fine though. Either way it's only a one line change, and later we can setup a meeting to go through the end to end experience, in which we can fix all of these minor issues.

}
)) as WorkbenchTable<IDisassembledInstructionEntry>;

this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2, '0x00005000');
Copy link
Contributor

Choose a reason for hiding this comment

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

I find it weird that you called the constant NUM_INSTRUCTIONS_TO_LOAD but then you load twice as much.
I think the constant should be larger and then you would either use it or it divided by 2, if that makes sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I consider it as number of instructions to load each time. Since the current instruction is revealed in the middle of the viewport, the first load actually loads "twice": "first time" before the current instruction, and "second time" include and after the current instruction.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okey. I was just sharing that I found it a bit confusing :)


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;
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's comment out this onDidScroll code so we try to find the issue with the reveal that you mentioned.
So if you comment this out and you just do

this.loadDisassembledInstructions(0, DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD * 2, '0x00005000');
this._disassembledInstructions.reveal(DisassemblyView.NUM_INSTRUCTIONS_TO_LOAD, 0.5);

You still get an issue? Nothing gets rendered?
How about if you comment out the reveal code do things get rendered then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I comment out those two lines, nothing is rendered.
If I comment out the reveal line only, the lines are rendered, and item 0 is at the top of the view.

Screen shot during debugging:

Immediately after creating the table:
image

Second time reveal is invoked (in scroll event listener):
image

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, thanks for providing more details.
To clarify:

  1. You comment out your onDidScroll code and nothing gets rendered because you call reveal
  2. You believe this is scrollTop is 0. But that is expected, the table is in the inital position and the top element is shown.

I just tried this out on my machine and it works just fine. I have simple relaxed the rule of the Go to Disassembly command to be always shown and the table gets nicely rendered for me (with no other changes to your source code)

Notice how the scroll bar is properly 70% through on the initial show. So I do not understand what the issue is on your side

Screenshot 2021-06-09 at 13 21 04

Copy link
Contributor

Choose a reason for hiding this comment

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

If you still see the issue on your side I suggest that you simply make a change to the PR such that I can also reproduce the problem on my machine if possible and then I can investigate with Joao the owner of the table widget. However so far all looks good to me.

Copy link
Contributor Author

@xisui-MSFT xisui-MSFT Jun 9, 2021

Choose a reason for hiding this comment

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

Sorry for the confusion. ScrollTop doesn't have problems. The problem is that List.view.renderHeight is 0.

Not sure why it's not reproing on your side though. Did you start debugging VS Code from VS Code, or are you using a packaged build?

Copy link
Contributor

Choose a reason for hiding this comment

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

I was running vscode out of source, not packaged.
I will check with Joao if he has ideas why it is broken on your machine and not on mine.
Maybe one of your colleagues could also try, just so we see if he also sees the problem on his or her machine?

Copy link
Contributor

@isidorn isidorn Jun 10, 2021

Choose a reason for hiding this comment

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

Joao had a good idea:
"renderHeight = 0 usually means the client did not call layout()"

So make sure to call the layout method.
Once the table is layouted you should call reveal on it
Here's how the runtime extensions editor is doing it:

https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts#L458

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well that function was copied from your code... Felix said he couldn't repro the issue either. So it's probably something wrong with my machine... Let's worry about this later if any of us see this again.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, just make sure to call layout please.
Also my code is 5 years old and has not been updated since. It needs a lot of improvements, it is just an example.

@isidorn
Copy link
Contributor

isidorn commented Jun 8, 2021

@xisui-MSFT great work 👏 I left comments across various files, I hope you find them helpful.
As for the reveal issue I have commented in the code with some ideas how we can potentially investigate the issue in more details. Let me know once you tackle my comments and if you have some new findings regarding the reveal funcitonality.

fyi @lramos15 since this might be a good fit to merge one day with the hex editor. Both of them have a sort of an infinite virtualised editor view

Comment on lines 155 to 430
if (element.allowBreakpoint) {
const breakpointIcon = 'codicon-' + icons.breakpoint.regular.id;
const breakpointHintIcon = 'codicon-' + icons.debugBreakpointHint.id;

if (element.isBreakpointSet) {
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;
templateData.icon.classList.remove(breakpointIcon);
} else if (element.allowBreakpoint) {
element.isBreakpointSet = true;
templateData.icon.classList.add(breakpointIcon);
}
};
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@isidorn I think there should be a better way to render breakpoints, as the breakpoint HTMLElement in main editor seems to allow duplicate items in classList. Do you know if I missed anything?

Copy link
Contributor

Choose a reason for hiding this comment

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

Breakpoint in the main editor is rendered using editor glyph margins.
Unfortunately we do not have those for our custom editor, so we have to do something like this.

Just make sure to add all those listeners to a disposable list!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are not disposable so I can't register them in the disposable list. But I set them to null in the dispose function, and I think this should be fine?

Copy link
Contributor

Choose a reason for hiding this comment

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

@isidorn
Copy link
Contributor

isidorn commented Jun 11, 2021

fyi next week and the following Monday I will be on vacation 🌴

return { instruction };
}

renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void {
Copy link
Contributor Author

@xisui-MSFT xisui-MSFT Jun 16, 2021

Choose a reason for hiding this comment

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

@isidorn Looks like there may be problems rendering the rows if the content is very long. Even when allow horizontal scrolling, the initial width of the table seems to limit the length being rendered in a row.

Also, I didn't realize this issue before, but since the first column is breakpoints column, we don't want it disappear when scrolling horizontally. So is there a way to keep it fixed from horizontal scrolling?

Copy link
Contributor

Choose a reason for hiding this comment

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

Table should allow infinite width, you can just scroll to reach it. Can you please clarify what is the problem? You can not reach the end of the row? Are you sure you are properly calling layout?

I think it is currently not possible to have rows which are always visible in the table. I am double checking with Joao, but we can think about this as step 2 as an enhancement to the table.
Alternative approach would be to use the list instead of the table and then the whole row rendering is up to you so you can use css tricks to make sure that the breakpoint is always visible

Copy link
Contributor

@isidorn isidorn Jun 22, 2021

Choose a reason for hiding this comment

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

I just got back from Joao, and horizontal scrolling is not supported by the table #117049

I was not aware of this, otherwise I would have brought it up sooner. So either we add horizontal scrolling some time in the future. Or we should consider to use the list instead and leave the rendering and positioning of fields to the implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for checking! We think a table should be fine for the first iteration. Anyway, I tried list this afternoon but wasn't able to figure out how to fix an element from scrolling. Looks like the most commonly used CSS style for this is position: fixed, but this doesn't work in a list. Any suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

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

@xisui-MSFT this would need some css magic. Your position:fixed sounds like a good idea. Though I am not sure if this will work horizontally.
The list should not prevent you from controlling your css layout. So it should be possible, if you ran out of ideas let me know and I can investigate online...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually I have already run out of ideas... I tried quite a few combinations of css styles but none of them work. Also the behavior seems to be different than when I tried to test with a simple webpage since position:fixed does work there.

Although looks like position:sticky may also be a good candidate, it doesn't work either. Feels like the concept of viewport with respect to entries in the list is different from that of the entire page...

Would be great if you could take a look. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

I checked with our css wizard and he said to try to go with position:sticky as you mentioned. He linked me this article, maybe it helps https://elad.medium.com/css-position-sticky-how-it-really-works-54cd01dc2d46

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only thing that wasn't so obvious that mentioned in this article is it requires a container to work. However, a list entry is already a container, so unfortunately it doesn't really help. Also I try to add another container under the list entry, and still no luck...

@yuehuang010
Copy link
Contributor

Hi @isidorn, at the moment, we have an early version of Disassembly View which support displaying disassembly and scrolling.
Breakpoints and Stepping are not working or partially implemented. Should we look into committing this PR as is or wait for more functionality to come online.
Thanks
Felix

@isidorn
Copy link
Contributor

isidorn commented Jun 22, 2021

@yuehuang010 thanks a lot for the updates on this PR.
There has been great progress but I would prefer that we merge this PR once we polish this a bit more.

Ideally once we merge this in it would be in a good state such that we can together write a test plan item for this feature and we can test this thoroughly in our VS Code endgame and file issues so you can fix and improve the experience even more. That way we can get this to production quality.
If you are worried about diverging from main too much, I suggest that you simple merge with latest from main from time to time.

Let me know what you think about this plan and thanks for your great work.

@isidorn
Copy link
Contributor

isidorn commented Jun 25, 2021

Chrome is doing a memory inspector, would be great if we try it out to learn about their approach
https://developer.chrome.com/blog/memory-inspector/

@xisui-MSFT
Copy link
Contributor Author

@isidorn Thanks for sharing this. I looked at their approach, and seems like it's quite different from ours. One major difference is that *.wasm is a file that has a fixed size. And actually not being able to use a file with a fixed size is almost the only reason we decided that we shouldn't use the existing text editors in VS Code.

Feature wise, for assembly view only, I think we have achieved most features they have currently (while UI may need a lot of tweaks, and we can discuss them on Monday), and we are pursuing more features, such as debug tooltip.

For memory view and diagnostics view, I believe they are on our list but I'm not sure about the current progress.

@@ -362,6 +366,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 disassmebly editor
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: disassmebly -> disassembly

const result: DebugProtocol.DisassembledInstruction[] = [];

for (let i = 0; i < instructionCount; i++) {
result.push({ address: `${i + offset}`, instruction: `debugger is not availible.` });
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: availible -> available

@@ -430,6 +437,12 @@ export interface IDataBreakpoint extends IBaseBreakpoint {
readonly accessType: DebugProtocol.DataBreakpointAccessType;
}

export interface IInstructionBreakpoint extends IBaseBreakpoint {
// instructionReference is from debugProtocal and is address for purposes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: debugProtocal -> debugProtocol
"is address for purposes." ???

private _currentInstructionAddress: string | undefined;
private _disassembledInstructions: WorkbenchTable<IDisassembledInstructionEntry> | undefined;
private _onDidChangeStackFrame: Emitter<void>;
private _privousDebuggingState: State;
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: _privousDebuggingState -> _previousDebuggingState


}

export class DisassemblyVIewContribution implements IWorkbenchContribution {
Copy link
Contributor

Choose a reason for hiding this comment

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

Letter case: DisassemblyVIewContribution -> DisassemblyViewContribution

@@ -80,6 +81,9 @@ export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey<boolean>
export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey<boolean>('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") });
export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey<boolean>('multiSessionRepl', false, { type: 'boolean', description: nls.localize('multiSessionRepl', "True when there is more than 1 debug console.") });
export const CONTEXT_MULTI_SESSION_DEBUG = new RawContextKey<boolean>('multiSessionDebug', false, { type: 'boolean', description: nls.localize('multiSessionDebug', "True when there is more than 1 active debug session.") });
export const CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED = new RawContextKey<boolean>('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") });
export const CONTEXT_DISASSEMBLE_VIEW_FOCUS = new RawContextKey<boolean>('disassembleViewFocus', false, { type: 'boolean', description: nls.localize('disassembleViewFocus', "True when the Disassembly View is in focused.") });
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: True when the Disassembly View is in focused. -> True when the Disassembly View is focused.

@xisui-MSFT xisui-MSFT marked this pull request as ready for review July 7, 2021 19:25
Copy link
Contributor

@weinand weinand left a comment

Choose a reason for hiding this comment

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

I did a final review of all changes.
Most issues are minor and only affect comments or naming.

But it would be great if you could at least address the following two bigger issues:

  • the missing call to sendInstructionBreakpoints in sendAllBreakpointsbecause without that, the Disassembly view will show instruction breakpoints from the last debug session that are not registered with the new session (and are never hit).
  • check supportsInstructionBreakpoints capability before calling setInstructionBreakpoints. Currently you are checking the supportsDisassembleRequest capability (which is wrong).

After fixing these two issue we can merge the PR.

const result: DebugProtocol.DisassembledInstruction[] = [];

for (let i = 0; i < instructionCount; i++) {
result.push({ address: `${i + offset}`, instruction: `debugger is not available.` });
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.

Filling the disassembly view with fake "error" instructions is fishy...
I suggest to throw an error with a correct error message, (e.g. "no active debug session" and "no disassembly available"), and catch that error at the calling site of getDisassemble() and show a proper error UI in the Disassembly view.

For the initial release this is OK, but make sure not to forget to fix this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed. The disassembly view should now handle all the good and bad data from debugger.

@@ -978,6 +1013,26 @@ export class DebugService implements IDebugService {
await this.sendExceptionBreakpoints(session);
}

async getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise<any> {
Copy link
Contributor

Choose a reason for hiding this comment

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

"getDisassemble" sounds wrong because "disassemble" is a verb.
I suggest "getDisassembledInstructions"

Copy link
Contributor

Choose a reason for hiding this comment

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

Please use correct return type Promise<DebugProtocol.DisassembledInstruction[]>

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed function as it was unused and it is already under debugSession.


/**
* Removes all instruction breakpoints. If address is passed only removes the instruction breakpoint with the passed address.
* Notifies debug adapter of breakpoint changes.
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.

I assume that the matching is actually based on the "string" value and not on the real "address". So "0x0F" and "15" are the same address but different strings.
Please clarify the comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

amended comment.

@@ -940,6 +965,8 @@ export interface IDebugService {
*/
sourceIsNotAvailable(uri: uri): void;

getDisassemble(offset: number, instructionOffset: number, instructionCount: number): Promise<any>;
Copy link
Contributor

Choose a reason for hiding this comment

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

bad name "getDisassemble" and incorrect return type (see my other comment on the implementation of getDisassemble)

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed "getDisassemble" from DebugService as it should be called from debugSession instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

It was unused.

return DisassemblyViewInput.ID;
}

static _instance: DisassemblyViewInput;
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.

I wonder why only a single Disassembly view is supported.
In a multi-threaded debugging scenario multiplexing multiple threads into a single viewer will result in an "interesting" user experience...

But for the initial release this is OK.

Copy link
Contributor

Choose a reason for hiding this comment

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

ATM, it is to maintain parity with VS's disassembly view. We can have a discussion about this feature for the next iteration.

@@ -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

@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 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

@@ -373,6 +378,13 @@ viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('brea
viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);

// Register disassembly editor
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.

better call it "view" for now:

// Register disassembly view

src/vs/workbench/contrib/debug/browser/rawDebugSession.ts Outdated Show resolved Hide resolved
@xisui-MSFT
Copy link
Contributor Author

@xisui-MSFT thanks, the scrolling behavior is much better now:

2021-07-21_09-38-31 (1)

When stepping down, the line with the "current instruction" still disappears partially out of view before it is revealed.
When stepping up this does not happen but I think it would be still better if scrolling would start one line earlier so that there would be "one line of context" on both sides of the "current instruction".

@weinand Sorry there was a bug in my changes yesterday, and so the behavior is actually not what I expected. I have fixed it and the behavior now is:

  1. if the target line is within the viewport, don't reveal
  2. if the target line is partially within the viewport, or is out of the viewport but close to it, reveal it either at the bottom or top of the viewport, depending on stepping over or back
  3. if the target line is far from the viewport, reveal it at the middle of the viewport.

This should avoid or mitigate most weird jumps. But then I found this behavior is different from the existing behavior in vs code. Which one do you prefer?

BTW, thanks for the mock debugger, it's very helpful!

@weinand
Copy link
Contributor

weinand commented Jul 21, 2021

@xisui-MSFT great to hear that Mock Debug is helpful for you too ;-)

BTW, I've added the sendInstructionBreakpoints to sendAllBreakpoints and that seems to work fine for me.

@weinand
Copy link
Contributor

weinand commented Jul 21, 2021

@xisui-MSFT since I'll be offline for the next 12 hours, I suggest that you merge the PR after addressing the two issues mentioned in my review. If possible please check whether the PR breaks CI.

Let's hope that we'll get a good build with the assembly view feature tomorrow morning.

@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

@xisui-MSFT @yuehuang010 since there was no reaction on your part, I have fixed the two issues myself.

Do you still want to have this PR merged?

@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

Today's demo:

2021-07-22_10-31-24 (1)

I'm happy about this and I think we should merge.

@xisui-MSFT
Copy link
Contributor Author

@weinand I think Felix is working on resolving your comments. @yuehuang010 Any updates?

@weinand weinand mentioned this pull request Jul 22, 2021
2 tasks
@weinand weinand added on-testplan feature-request Request for new features or functionality labels Jul 22, 2021
@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

Since I've fixed the only two important issues, and the other issues are minor and require no immediate code change, I think we are fine.

Should I merge?

@yuehuang010
Copy link
Contributor

Ship it!

@weinand weinand merged commit 4f5f5de into microsoft:main Jul 22, 2021
@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

Merged.
@yuehuang010 @xisui-MSFT Thanks a lot!

@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

@yuehuang010 @xisui-MSFT I've already created a Test item which is due next Monday EOD.
I suggest that we give tester the choice to test the Disassembly view either with the C++ extension or with Mock Debug.
If you agree, then please add to the item.

I will now start to create issues for known problems and link them with the test item so that testers don't have to waste time by creating new (duplicate) issues for them...

@yuehuang010
Copy link
Contributor

How soon could people start using the insider?

@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

As soon as possible or available.
Those that run VS Code out of source can use it immediately, others will have to wait until tomorrow.
But official testing will start next Tuesday.

@weinand
Copy link
Contributor

weinand commented Jul 22, 2021

BTW, we can still fix issues until Monday evening.
Tuesday and Wednesday is testing, and Wednesday and Thursday is fixing.
On Wednesday we can decide whether we want to ship/enable the feature in July.

@vadimcn
Copy link
Contributor

vadimcn commented Jul 24, 2021

I've tried this out in today's insiders and noticed that when scrolling down, the Disassembly View sends request with memoryReference = <address of the last instruction> and instructionOffset = 1 to fetch the next block of instructions.
My reading of the DAP spec is that the address field contains user-viewable address representation, which is not necessarily the same as what adapter uses to represent memoryReference's. IMO, the way the spec is currently written, DisassembleRequest is supposed to use the original memoryReference as the base.

@yuehuang010
Copy link
Contributor

yuehuang010 commented Jul 24, 2021 via email

@weinand
Copy link
Contributor

weinand commented Aug 4, 2021

Yes, @vadimcn 's observation is correct.

@github-actions github-actions bot locked and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality on-testplan
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Roadmap: Disassembly View Feature
9 participants