-
Notifications
You must be signed in to change notification settings - Fork 86
Add support for registers, disassembly, and memory #212
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -457,6 +457,10 @@ | |
| "Debugger attached to an existing process.", | ||
| "A project launcher component has launched a new process in a suspended state and then asked the debugger to attach." | ||
| ] | ||
| }, | ||
| "pointerLength": { | ||
| "type": "integer", | ||
| "description": "The size of a pointer or address for this process, in bits. This value may be used by clients when formatting addresses for display." | ||
| } | ||
| }, | ||
| "required": [ "name" ] | ||
|
|
@@ -627,6 +631,10 @@ | |
| "supportsRunInTerminalRequest": { | ||
| "type": "boolean", | ||
| "description": "Client supports the runInTerminal request." | ||
| }, | ||
| "supportsMemoryReferences": { | ||
| "type": "boolean", | ||
| "description": "Client supports memory references." | ||
| } | ||
| }, | ||
| "required": [ "adapterID" ] | ||
|
|
@@ -2009,6 +2017,10 @@ | |
| "indexedVariables": { | ||
| "type": "number", | ||
| "description": "The number of indexed child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks." | ||
| }, | ||
| "memoryReference": { | ||
| "type": "string", | ||
| "description": "Memory reference to an adapter-determined location appropriate for this result. For pointer types, this is generally a reference to the memory address contained in the pointer." | ||
| } | ||
| }, | ||
| "required": [ "result", "variablesReference" ] | ||
|
|
@@ -2326,6 +2338,133 @@ | |
| }] | ||
| }, | ||
|
|
||
| "ReadMemoryRequest": { | ||
| "allOf": [ { "$ref": "#/definitions/Request" }, { | ||
| "type": "object", | ||
| "description": "Reads bytes from memory at the provided location.", | ||
| "properties": { | ||
| "command": { | ||
| "type": "string", | ||
| "enum": [ "readMemory" ] | ||
| }, | ||
| "arguments": { | ||
| "$ref": "#/definitions/ReadMemoryArguments" | ||
| } | ||
| }, | ||
| "required": [ "command", "arguments" ] | ||
| }] | ||
| }, | ||
| "ReadMemoryArguments": { | ||
| "type": "object", | ||
| "description": "Arguments for 'readMemory' request.", | ||
| "properties": { | ||
| "memoryReference": { | ||
| "type": "string", | ||
| "description": "Memory reference to the base location from which data should be read." | ||
| }, | ||
| "offset": { | ||
| "type": "integer", | ||
| "description": "Optional offset (in bytes) to be applied to the reference location before reading data. Can be negative." | ||
| }, | ||
| "count": { | ||
| "type": "integer", | ||
| "description": "Number of bytes to read at the specified location and offset." | ||
| } | ||
| }, | ||
| "required": [ "memoryReference", "count" ] | ||
| }, | ||
| "ReadMemoryResponse": { | ||
| "allOf": [ { "$ref": "#/definitions/Response" }, { | ||
| "type": "object", | ||
| "description": "Response to 'readMemory' request.", | ||
| "properties": { | ||
| "body": { | ||
| "type": "object", | ||
| "properties": { | ||
| "address": { | ||
| "type": "string", | ||
| "description": "The address of the first byte of data returned. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise." | ||
| }, | ||
| "unreadableBytes": { | ||
| "type": "integer", | ||
| "description": "The number of unreadable bytes encountered after the last successfully read byte. This can be used to determine the number of bytes that must be skipped before a subsequent 'readMemory' request will succeed." | ||
| }, | ||
| "data": { | ||
| "type": "string", | ||
| "description": "The bytes read from memory, encoded using base64." | ||
| } | ||
| }, | ||
| "required": [ "address" ] | ||
| } | ||
| } | ||
| }] | ||
| }, | ||
|
|
||
| "DisassembleRequest": { | ||
| "allOf": [ { "$ref": "#/definitions/Request" }, { | ||
| "type": "object", | ||
| "description": "Disassembles code stored at the provided location.", | ||
| "properties": { | ||
| "command": { | ||
| "type": "string", | ||
| "enum": [ "disassemble" ] | ||
| }, | ||
| "arguments": { | ||
| "$ref": "#/definitions/DisassembleArguments" | ||
| } | ||
| }, | ||
| "required": [ "command", "arguments" ] | ||
| }] | ||
| }, | ||
| "DisassembleArguments": { | ||
| "type": "object", | ||
| "description": "Arguments for 'disassemble' request.", | ||
| "properties": { | ||
| "memoryReference": { | ||
| "type": "string", | ||
| "description": "Memory reference to the base location containing the instructions to disassemble." | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think an additional requirement is for the address to be properly aligned to start of actual instruction. Or else, you get garbage as output? Again, I am thinking of variable instruction lengths. Anything relative to the PC or symbols in the object file or other things on the stack is generally okay. It should be the DA clients responsibility to make the proper address request or does the DA figure it out?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. In the VS case, disassembly will generally be started from a location known to correspond with an instruction - the user can right-click a stack frame and select "go to disassembly", in which case we'll use the Nothing stops the user from bringing up the diassembly UI and entering a random address, of course, in which case the disassembly will likely be nonsense. |
||
| }, | ||
| "offset": { | ||
| "type": "integer", | ||
| "description": "Optional offset (in bytes) to be applied to the reference location before disassembling. Can be negative." | ||
| }, | ||
| "instructionOffset": { | ||
| "type": "integer", | ||
| "description": "Optional offset (in instructions) to be applied after the byte offset (if any) before disassembling. Can be negative." | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is a negative
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once the adapter turns the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, I should check out how you did that. You said 'heuristics' as oppose to `algorithm'. I can see going back to a previous symbol and then going forward, but I bet you did something way smarter.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two techniques that I am aware of for reverse disassembly: As you suggested, look for symbols -or- heuristically by repeating the following. You can see the MIEngine's basic implementation of this stuff by looking at SeekBack. Heuristic for backwards disassembly without symbols:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup @gregg-miskelly that was what I thinking what we would have to do :-) I pretty much typed up your heuristic and said to myself, oh you guys must have something much much better. I thought I would look stupider typing it out. Some processors have instruction align (like 2-byte) requirements as well.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup. This problem is certainly a lot easier on architectures that have such restrictions. |
||
| }, | ||
| "instructionCount": { | ||
| "type": "integer", | ||
| "description": "Number of instructions to disassemble starting at the specified location and offset. An adapter must return exactly this number of instructions - any unavailable instructions should be replaced with an implementation-defined 'invalid instruction' value." | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually, a disassembly request with a symbol/section reference always succeeds. Normally, we make that request with what we find on the stack trace. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some times there can be data embedded along with instructions. Usually at the beginning or the end of a function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've uploaded a file to show how data can be interspersed with instructions...I just took a random file I had, disassembled it and pasted a few functions. https://github.com/haneefdm/cortex-debug-samples/blob/master/demo/tmp.s
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "lazy scrolling" UI knows that it needs to request N lines of disassembly to fill in after scrolling, so it issues a request saying "starting at < The only string field on a Here's an example. I compiled the following code: int main()
{
start:
std::cout << "Hello World!\n";
goto start;
}In the VS disassembler, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrewcrawley Yes nice, so the instruction can be data as well -- My assumption (that you confirmed) is that if the I do think negative offsets are super useful for implementing lazy query/rendering. But how does the DA client make such a request -- a valid one. Can I just request a 1000 instructions before the I am thinking If available, I would love to look at your vsdbg implementation. Then everything might make sense. Finally, isn't this a holiday for you?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A The reason for having both a byte and instruction offset is because the VS disassembly API allows consumers to say "go to this address (represented by vsdbg is not open source, unfortunately, but I believe MIEngine uses a similar heuristic - take a look at Yes, today is a holiday, but I'm happy to have a distraction from cleaning out my garage : ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrewcrawley Thank you, sir, for your patience. I was not aware of what vsdbg was currently doing. |
||
| }, | ||
| "resolveSymbols": { | ||
| "type": "boolean", | ||
| "description": "If true, the adapter should attempt to resolve memory addresses and other values to symbolic names." | ||
| } | ||
| }, | ||
| "required": [ "memoryReference", "instructionCount" ] | ||
| }, | ||
| "DisassembleResponse": { | ||
| "allOf": [ { "$ref": "#/definitions/Response" }, { | ||
| "type": "object", | ||
| "description": "Response to 'disassemble' request.", | ||
| "properties": { | ||
| "body": { | ||
| "type": "object", | ||
| "properties": { | ||
| "instructions": { | ||
| "type": "array", | ||
| "items": { | ||
| "$ref": "#/definitions/DisassembledInstruction" | ||
| }, | ||
| "description": "The list of disassembled instructions." | ||
| } | ||
| }, | ||
| "required": [ "instructions" ] | ||
| } | ||
| } | ||
| }] | ||
| }, | ||
|
|
||
| "Capabilities": { | ||
| "type": "object", | ||
| "title": "Types", | ||
|
|
@@ -2447,6 +2586,14 @@ | |
| "supportsDataBreakpoints": { | ||
| "type": "boolean", | ||
| "description": "The debug adapter supports data breakpoints." | ||
| }, | ||
| "supportsReadMemoryRequest": { | ||
| "type": "boolean", | ||
| "description": "The debug adapter supports the 'readMemory' request." | ||
| }, | ||
| "supportsDisassembleRequest": { | ||
| "type": "boolean", | ||
| "description": "The debug adapter supports the 'disassemble' request." | ||
| } | ||
| } | ||
| }, | ||
|
|
@@ -2704,6 +2851,10 @@ | |
| "type": "string", | ||
| "enum": [ "normal", "label", "subtle" ], | ||
| "description": "An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way." | ||
| }, | ||
| "instructionPointerReference": { | ||
| "type": "string", | ||
| "description": "Memory reference for the current instruction pointer in this frame." | ||
| } | ||
| }, | ||
| "required": [ "id", "name", "line", "column" ] | ||
|
|
@@ -2752,6 +2903,16 @@ | |
| "endColumn": { | ||
| "type": "integer", | ||
| "description": "Optional end column of the range covered by this scope." | ||
| }, | ||
| "kind": { | ||
| "type": "string", | ||
| "description": "Optional hint describing the contents of this scope.", | ||
| "_enum": [ "arguments", "locals", "registers" ], | ||
weinand marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "enumDescriptions": [ | ||
| "Scope contains method arguments.", | ||
| "Scope contains local variables.", | ||
| "Scope contains registers. Only a single scope of this kind should be returned." | ||
| ] | ||
| } | ||
| }, | ||
| "required": [ "name", "variablesReference", "expensive" ] | ||
|
|
@@ -2792,6 +2953,10 @@ | |
| "indexedVariables": { | ||
| "type": "integer", | ||
| "description": "The number of indexed child variables.\nThe client can use this optional information to present the children in a paged UI and fetch them in chunks." | ||
| }, | ||
| "memoryReference": { | ||
| "type": "string", | ||
| "description": "Memory reference for the variable if the variable represents executable code, such as a function pointer." | ||
| } | ||
| }, | ||
| "required": [ "name", "value", "variablesReference" ] | ||
|
|
@@ -3005,6 +3170,10 @@ | |
| "endColumn": { | ||
| "type": "integer", | ||
| "description": "An optional end column of the range covered by the goto target." | ||
| }, | ||
| "instructionPointerReference": { | ||
| "type": "string", | ||
| "description": "Memory reference for the instruction pointer value represented by this target." | ||
| } | ||
| }, | ||
| "required": [ "id", "label", "line" ] | ||
|
|
@@ -3190,6 +3359,50 @@ | |
| "description": "Details of the exception contained by this exception, if any." | ||
| } | ||
| } | ||
| }, | ||
|
|
||
| "DisassembledInstruction": { | ||
| "type": "object", | ||
| "description": "Represents a single disassembled instruction.", | ||
| "properties": { | ||
| "address": { | ||
| "type": "string", | ||
| "description": "The address of the instruction. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise." | ||
| }, | ||
| "instructionBytes": { | ||
| "type": "string", | ||
| "description": "Raw bytes representing the instruction and its operands, in an implementation-defined format." | ||
| }, | ||
| "instruction": { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clarification: So I am not just focused on ARM. Just an example I have at the moment. I can find similar things with MS/Intel stuff.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, if an adapter wanted to return something like |
||
| "type": "string", | ||
| "description": "Text representing the instruction and its operands, in an implementation-defined format." | ||
| }, | ||
| "symbol": { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More of a question. Does it strictly have to be an actual symbol or can it be something like
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the intent here is to display things like goto labels. So you probably wouldn't want to return something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I was wrong about symbols. No such thing as Upto the client to dicide how to display all of this, but it is the DA job to consolidate the first two lines above into one
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Correct |
||
| "type": "string", | ||
| "description": "Name of the symbol that correponds with the location of this instruction, if any." | ||
| }, | ||
| "location": { | ||
| "$ref": "#/definitions/Source", | ||
| "description": "Source location that coresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction." | ||
| }, | ||
| "line": { | ||
| "type": "integer", | ||
| "description": "The line within the source location that corresponds to this instruction, if any." | ||
| }, | ||
| "column": { | ||
| "type": "integer", | ||
| "description": "The column within the line that corresponds to this instruction, if any." | ||
| }, | ||
| "endLine": { | ||
| "type": "integer", | ||
| "description": "The end line of the range that corresponds to this instruction, if any." | ||
| }, | ||
| "endColumn": { | ||
| "type": "integer", | ||
| "description": "The end column of the range that corresponds to this instruction, if any." | ||
| } | ||
| }, | ||
| "required": [ "address", "instruction" ] | ||
| } | ||
|
|
||
| } | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.