Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

Memory Inspector: UI and UX Updates and New Features #119

Merged

Conversation

colin-grant-work
Copy link
Contributor

@colin-grant-work colin-grant-work commented Mar 6, 2021

What it does

This PR introduces substantial changes to the UI of the memory viewer widget, building on Simon Marchi's work on colorizing variables. It adds useful functionality for inspecting memory during debugging including: dynamic updating of memory, multiple memory views, view comparison (diffing), register viewing.


Key features:

Multiple Views

We have added the ability to open multiple memory views. This allows users to compare different regions of memory or to examine the same memory region of memory at different points in time.

multi-view.mp4

Diff View

In addition to the standard memory viewer, we have added a diffing view that allows users to compare the content of different memory views in-line.

before-after.mp4

Dynamic Updating and Locking

Memory views will now update as users step through code, and changes to the visible memory region will be highlighted. If users would like to opt out of automatic updates - for example to preserve a memory state for comparison to a later state - they can 'lock' the view by clicking a lock icon.

edit-title-and-lock.mp4
highlighting-changes-ascii.mp4

Entry points

On the master branch, in order to examine a particular memory region, users must manually open the memory view and configure its view parameters when stopped at a breakpoint. This PR adds context-menu commands to open a memory view or register view at the location of a variable visible in the Debug Variables widget while also allowing manually address specification.

right-click-entry.mp4
manual-entry-point.mp4

Register View

The Debug Adapter Protocol supports the reporting of register contents as a variable scope, although the CDT-GDB adapter doesn't currently provide registers. We have added a memory view dedicated to showing register contents, since the layout is quite different from the standard memory view.


Setup

Prerequisites: You should have GDB set up on your system and have a working installation of NodeJS >= v.12 and < v.13.

  1. Clone the repo and checkout this PR (git fetch origin pull/119/head:memory-inspector && git checkout memory-inspector)
  2. Build the application (npx yarn)
  3. If you intend to use electron, you will need to run npx yarn rebuild:electron.
  4. Start the application (npx yarn start:browser or npx yarn start:electron)
  5. Open the example workspace (File > Open Workspace... > select <repository>/examples/cpp-debug-workspace)
  6. Run the compilation task: (Terminal > Run Task... > select compile example)
  7. Open a.cpp and add a break point somewhere in main.
  8. Open the debugger using the image icon.
  9. Launch a debug session using configuration test-cpp - it should already be selected in the dropdown. image
  10. Follow some of the steps below to explore the functionality of the

How to test

  • Getting started:
    • Open the examples/cpp-debug-workspace workspace and run the command Task: Run and run the compilation task.
    • Set a breakpoint in a.cpp
    • open the debug widget and run the debug configuration from the launch.json
  • Accessing the Memory View (2 ways):
    • Via the Debug Widget
      • Open the variables > locals pane in the Debug Widget
      • Right click on a variable name (or struct member)
      • Click 'Show variable in memory view'
      • You should see a memory widget whose address field is set to &<what you clicked> and it should be populated with colorful memory.
    • From the Memory View
      • Click on the Memory View icon or run the command Toggle Memory View
      • You should get a blank widget.
      • You can enter an address as a number or &<some variable> to get memory at that location.
  • Freezing Memory (Before you do anything else):
    • Click the lock icon at the top of the widget.
  • Starting up a second widget:
    • Get a new widget with the same display by following the same steps as above (to get a clean widget, click the hex icon in the Memory Widget Tabbar)
    • You should see a diff compare icon appear in the tabbar for the main widget.
      • If you click it, a small widget at the bottom of the Memory Widget pane should appear that gives you options to select your widgets for comparison. Just ignore it… for now 😊
    • Then, in the second widget:
      • Change the display settings
        • Click the Settings cog wheel at the top
        • Manipulate the various fields that appear.
        • The content of memory you see shouldn't change, but you can change its layout
      • Change the Title
        • Hover over the large label for the widget.
        • You should see a pencil icon appear
        • Click anywhere over the title or pencil.
        • The title should transform into an input box.
        • Input something new and memorable.
        • Hitting enter should save, escape should cancel the edit.
        • If you save, the tab for the widget should also update.
    • Move the widget around/change the widget location
      • Move the widget to a different area of the application by dragging the tab
  • Dynamic features:
    • Responding to Value Setting
      • In the Debug Widget, find a variable that is visible in the memory widget.
      • Right click on that variable and select 'set value'. Set it to a different value than it currently has.
      • The memory widget should update to reflect the value you set, and you should see a highlighted memory field indicating a change.
    • Responding to changes during steps
      • Find some code that exercises a variable that is visible in the memory view.
      • Step through that code.
      • When changes are made relative to what is shown in the memory view, you should see a highlight.
    • Hovering
      • In a standard widget, you should be able to hover over a memory cell and see various data:
        • Hex, binary, octal, decimal, and Unicode value should appear for every cell.
        • If the cell changed during a step, you should see the previous value.
        • If you've typed a new value into the cell, you should see the current value of the byte.
    • Diffing
      • Good scenario:
        • Now that the first widget you opened up and locked and the second widget are different, click on the Go button in the comparison area.
        • You can diff any arbitrary memory regions by changing the offset from the start of the widget.
        • A widget should appear that shows the memory region you've been examining, with familiar diff highlights.
        • You have the same display formatting options as in a normal widget.
        • You can view ASCII, and it should be diffed.
      • Bad scenarios
        • If you have no memory loaded in one of the widgets selected for diffing, you should get a toast saying you need to have memory in both widgets.
        • If you select two widgets with non-overlapping memory areas, you should get a toast saying that you can't diff non-overlapping memory
      • Other Diff Select Widget Features
        • It should be impossible to try to diff the same widget against itself.
          • If you open the first select input and choose the same value as in the second select, the second select should change to some other value.
          • If you open the second select input, you shouldn't see the value of the first input as an option.
        • It should update with changes to widget title.
          • Change the title of one of the widgets you see selected.
          • The selection should update
  • Cleanup
    • Put your memory and diff widgets wherever you want in the shell.
    • Restart the application.
    • They should all be gone.

Additional features to observe:

  • Memory Pagination : When you are viewing memory, you have the option to "Load X more bytes" at the top/bottom of the table. Clicking on this text will perform the action, you can also use the number-select dropdown to load more/less memory. The input fields for "offset" and "length" should respond accordingly.
  • Theming: light/dark/high contrast
  • Keyboard accessibility: are all inputs tabbable, do arrow keys/enter/escape work as expected
  • Dynamic icons : widget icon will toggle between "0x" and "lock" when widget state changes
  • Static/unpopulated views : If you open the memory view without any data loaded, are the empty views aesthetically pleasing?
  • Resizing : does the widget respond (mostly) well to reasonable window resize changes?

To think about:

  • The current memory provider works with the cdt-gdb adapter, but most of the functionality is available via standard requests of the debug adapter protocol, so it might be desirable to implement a plain DAP memory provider, perhaps with a service to match sessions to the memory provider best-suited to handle them.
  • Only the memory provider needs to know about how the debug adapter works - it's the only part of the material in src/browser that knows anything about the CDT-GDB adapter. The remaining frontend code is debugger-agnostic, as long as it can get memory from that provider. That means that the CPP part of the extension name is limited to the registering the GDB debug types on the backend. What does that mean for the nature of the repository?

Review checklist

Reminder for reviewers

@colin-grant-work colin-grant-work changed the title Feature/UI updates UI and UX Updates Mar 11, 2021
@colin-grant-work colin-grant-work changed the title UI and UX Updates Memory Inspector: UI and UX Updates and New Features Mar 15, 2021
@colin-grant-work colin-grant-work force-pushed the feature/ui-updates branch 3 times, most recently from 75d42ca to 54288bf Compare March 17, 2021 21:23
address: number,
}
import { injectable, inject } from 'inversify';
import { hexStrToUnsignedLong } from '../../common/util';

export const MemoryProvider = Symbol('MemoryProvider');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@thegecko, this is the interface between the debugger backend and the frontend. The current implementation knows it's interacting with GDB because the CDT-GDB adapter doesn't implement memory reading and writing as prescribed by the debug adapter protocol, but it should be relatively straightforward to swap it out.

It might be desirable to implement something like a MemoryProviderService that gathers MemoryProvider implementations and matches a given debug session with the memory provider designed for it. Let us know if that sounds useful, and we'll be happy to add it.

@JonasHelming
Copy link
Contributor

This looks great! Are there plans to merge this PR? This would make it so much easier to adopt the great work you did!

@JonasHelming
Copy link
Contributor

@MatthewKhouzam

@colin-grant-work
Copy link
Contributor Author

colin-grant-work commented Jul 26, 2021

@JonasHelming, we will plan on merging next week or the following. The one outstanding desideratum is that the memory reading be reimplemented to use the debug adapter protocol's prescribed method, rather than a custom request. That functionality has been implemented for the CDT-GDB adapter here, and once the changes have been confirmed to work for this extension, we will rewrite the code in this PR to use the new method.

Comment on lines 91 to 102
// This functionality is not presently available from the CDT-GDB adapter
// async writeMemory(address: string, content: string): Promise<void> {
// const { currentSession } = this.debugSessionManager;
// if (!currentSession) {
// throw new Error('No active debug session.');
// }

// await currentSession.sendCustomRequest('cdt-gdb-adapter/MemoryWrite', { address, content });
// }
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 write request is now in the DAP, so it should be possible to get it into the adapter.

@JonasHelming
Copy link
Contributor

@colin-grant-work when do you expect to merge? I don not intend to push, it is just for planning our adoption use case.

@colin-grant-work
Copy link
Contributor Author

colin-grant-work commented Aug 10, 2021

@JonasHelming, at the moment, I am waiting on a new release of CDT-GDB to convert the calls to read memory to DAP-compatible requests. It seems the release hasn't yet made it all the way to the VSCode plugin side, which is what this extension consumes. If you'd like to see it merged earlier, I could write a DAP-compatible memory provider, but leave it unbound for now until a DAP-compatible memory request is available from CDT-GDB.

@marcdumais-work
Copy link
Contributor

It seems the release hasn't yet made it all the way to the VSCode plugin side, which is what this extension consumes.

@colin-grant-work I wanted to mention that this is our own fork of this repo, that @paul-marechal arranged and periodically makes new releases-for. A new version there will not happen on its own, once the new version of the DA is published. Please let Paul know when the start are aligned.

@marcdumais-work
Copy link
Contributor

@paul-marechal Considering that you have since become a committer on the CDT project, maybe we can think about making releases upstream, instead of on our fork. I see there are still no release tags on the upstream repo, so I guess no official releases yet?

Alternatively, as I recall, the way we currently consume the DA from the cdt-gdb-vscode extension is quite twisted - maybe it would be better to work on making the DA more directly consumable, and then that extension is not longer relevant to us?

@thegecko
Copy link
Member

Alternatively, as I recall, the way we currently consume the DA from the cdt-gdb-vscode extension is quite twisted - maybe it would be better to work on making the DA more directly consumable, and then that extension is not longer relevant to us?

https://open-vsx.org/extension/eclipse-cdt/cdt-gdb-vscode ??

@marcdumais-work
Copy link
Contributor

https://open-vsx.org/extension/eclipse-cdt/cdt-gdb-vscode ??

Good catch - the extension is published on open-vsx. I made that happen with this PR: EclipseFdn/publish-extensions#285 . I am not sure, but I think the bot may publish a new version automatically, if a new release tag is added to the repo.

@colin-grant-work
Copy link
Contributor Author

At the moment it actually downloads from the GitHub releases:

"adapters": {
"cdt-gdb-vscode": "https://github.com/theia-ide/cdt-gdb-vscode/releases/download/v0.0.90-theia.2/cdt-gdb-vscode-0.0.90-theia.2.tgz"
}

Copy link
Contributor

@federicobozzini federicobozzini left a comment

Choose a reason for hiding this comment

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

@colin-grant-work I've added one small suggestion before this is merged. Can you please take a look at it?


dispose(): void {
this.toDispose.dispose();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like the super call is missing here and it's not possible to properly dispose of this widget. Can we add this?

Suggested change
}
this.super.dispose();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I've added a call to super.dispose() to the method.

@JonasHelming
Copy link
Contributor

Will this be merged soon? I do not want to push, but I need to adapt some project planning based on this, so it would be useful for me to have an ETA. Thank you in advance! :-)

@colin-grant-work
Copy link
Contributor Author

@JonasHelming, as of yesterday, a new release of CDT-GDB-VSCode is available on Open VSX, so I can now make the changes to use the DAP and then merge this PR. Apologies for the delays!

@colin-grant-work
Copy link
Contributor Author

colin-grant-work commented Aug 23, 2021

@JonasHelming, I have made the necessary adjustments to use the DAP - just need to bring it up to date with current Theia and it will be good to go. I plan to take care of that tomorrow morning.

@JonasHelming
Copy link
Contributor

sounds great, thank you very much for the update!

colin-grant-work and others added 4 commits August 24, 2021 12:42
Signed-off-by: Colin Grant <colin.grant@ericsson.com>
Signed-off-by: Kenneth Marut <kenneth.marut@ericsson.com>

Co-authored-by: Colin Grant <colin.grant@ericsson.com>
Co-authored-by: Kenneth Marut <kenneth.marut@ericsson.com>
In theory, we could have problems reading addresses as JS numbers, since
JS numbers don't have full 64 bits precision.  This patch makes use of
the 'long' library, which stores 64-bit numbers as two 32-bit halves.

Signed-off-by: Vincent Fugnitto <vincent.fugnitto@ericsson.com>
Signed-off-by: Simon Marchi <simon.marchi@ericsson.com>
Signed-off-by: Paul Maréchal <paul.marechal@ericsson.com>
This patch makes it so we hightlight the local variables of the current
frame in the memory view.  The idea is simple, we get the size and
address of all locals of the current frame through GDB (sizeof(foo) and
&foo).  If the bytes backing these variables are in the scope of the
memory view, we highlight them with a different color for each variable.

Signed-off-by: Simon Marchi <simon.marchi@ericsson.com>
Signed-off-by: Paul Maréchal <paul.marechal@ericsson.com>
Signed-off-by: Vincent Fugnitto <vincent.fugnitto@ericsson.com>
Signed-off-by: Colin Grant <colin.grant@ericsson.com>
Signed-off-by: Kenneth Marut <kenneth.marut@ericsson.com>
Signed-off-by: Tomas Sisohore <tomas.sisohore@ericsson.com>

Co-authored-by: Colin Grant <colin.grant@ericsson.com>
Co-authored-by: Kenneth Marut <kenneth.marut@ericsson.com>
Co-authored-by: Tomas Sisohore <tomas.sisohore@ericsson.com>
@colin-grant-work colin-grant-work changed the base branch from repo/theia-wrapper to master August 24, 2021 18:43
@colin-grant-work
Copy link
Contributor Author

@JonasHelming, @federicobozzini, @thegecko, @kenneth-marut-work, @paul-marechal, I have pushed up some significant changes to this PR.

  • I have changed the method by which the CDT-GDB-VSCode plugin is consumed: now it is downloaded as a plugin, and the backend code that registered its functionality using Theia API's has been removed.
  • I have changed the MemoryProvider code to use DAP methods.
  • I have tried to trim the dependencies listed and dev-packages included from the Theia repo to the minimum necessary to build and run the application and the various NPM scripts defined in the package.json files.
  • I've committed the yarn.lock, though it can be omitted if people prefer not to have it - I didn't realize it hadn't been committed previously.

If you're willing, please give things a run through to ensure that everything is working to your satisfaction, and if it is, I'll merge this ASAP.

Signed-off-by: Colin Grant <colin.grant@ericsson.com>
Copy link
Contributor

@federicobozzini federicobozzini left a comment

Choose a reason for hiding this comment

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

@colin-grant-work I've added a couple of comments again.

@colin-grant-work colin-grant-work force-pushed the feature/ui-updates branch 2 times, most recently from 51c8b50 to 0234329 Compare August 25, 2021 16:39
@colin-grant-work
Copy link
Contributor Author

@federicobozzini, I've pushed up a base-64 patch on this PR, but I propose that we merge it without those changes, since GDB is currently the main (only?) adapter that we here have an interest in, and it's sending its results in hex. I'll make a separate PR for the base 64 changes, and then there's less urgency on the long pipeline from CDT-GDB--> CDT-GDB-VSCode --> here. Does that sound reasonable?

cc: @JonasHelming, @thegecko

Signed-off-by: Colin Grant <colin.grant@ericsson.com>
@federicobozzini
Copy link
Contributor

federicobozzini commented Aug 26, 2021

@federicobozzini, I've pushed up a base-64 patch on this PR, but I propose that we merge it without those changes, since GDB is currently the main (only?) adapter that we here have an interest in, and it's sending its results in hex. I'll make a separate PR for the base 64 changes, and then there's less urgency on the long pipeline from CDT-GDB--> CDT-GDB-VSCode --> here. Does that sound reasonable?

cc: @JonasHelming, @thegecko

It sounds like a sensible choice to me. I'm OK with it.

Also feel free to ignore the other code review if the last commit is not going to be included.

@colin-grant-work
Copy link
Contributor Author

@federicobozzini, at @paul-marechal's urging, I decided to implement a different solution to the problem along the lines of this idea from the PR description:

The current memory provider works with the cdt-gdb adapter, but most of the functionality is available via standard requests of the debug adapter protocol, so it might be desirable to implement a plain DAP memory provider, perhaps with a service to match sessions to the memory provider best-suited to handle them.

I have left the DAP-compliant, base-64-expecting MemoryProvider as the default (and made the changes you suggested, swapping -0x1 for 0x-1 - oops), but also created a service that allows adopters to implement distinct MemoryProviders for different debug session types, if desired. Now the old (hex-expecting) MemoryProvider implementation is labeled CDTGDBMemoryProvider and will handle the requests for debug sessions of type gdb, and the default will handle anything else. Adopters can also implement a memory provider for any debug types that might require custom handling downstream.

@colin-grant-work
Copy link
Contributor Author

Unless there are any objections, I'll merge this on Monday.

@colin-grant-work
Copy link
Contributor Author

@paul-marechal, would you mind approving this when you have a chance? Thanks!

Copy link
Member

@paul-marechal paul-marechal left a comment

Choose a reason for hiding this comment

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

I actually can't test it locally as I don't have the right environment.

Looking at the code it looks good.

Looking at the animated captures, the new widgets looks much better than what we have.

@JonasHelming @thegecko @federicobozzini we plan on deprecating @theia/cpp-debug and rename this package something like @theia/debug-memory-inspector. The reasoning is that cpp-debug used to be about providing C/C++ debugging capabilities, but this is not the case anymore. We'll merge this PR for now, but keep in mind that we need a new package for it with a new more appropriate name, feel free to give suggestions.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants