Skip to content

Commit

Permalink
Merge #27
Browse files Browse the repository at this point in the history
27: Various fixes and updates. Requires updated `probe-rs-debugger` r=Yatekii a=noppej

- Adapt to `defmt` 0.3 'Rzcobs' encoding to fix #26.
- Support the new `defmt` 0.3 `DEFMT_LOG` environment variable.
- Catch target-side `log::error!()` and display as client-side message.

** Depends on the changes in [probe-rs-debugger PR #895](probe-rs/probe-rs#895
```
cargo install --git https://github.com/probe-rs/probe-rs probe-rs-debugger --branch noppej/issue894
```

Co-authored-by: JackN <noppej@hotmail.com>
  • Loading branch information
bors[bot] and noppej authored Feb 3, 2022
2 parents f646608 + 60de50a commit b751ff2
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 36 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "probe-rs-debugger",
"displayName": "Debugger for probe-rs",
"version": "0.3.3",
"version": "0.3.4",
"publisher": "probe-rs",
"description": "probe-rs Debug Adapter for VS Code.",
"author": {
Expand All @@ -13,7 +13,7 @@
"probe-rs embedded debug"
],
"engines": {
"vscode": "^1.62.2"
"vscode": "^1.64.0"
},
"icon": "images/probe-rs-debugger.png",
"categories": [
Expand Down Expand Up @@ -43,7 +43,7 @@
"watch-web": "webpack --watch --devtool nosources-source-map --config ./build/web-extension.webpack.config.js",
"package-web": "webpack --mode production --config ./build/web-extension.webpack.config.js"
},
"enableApiProposal": false,
"enabledApiProposals": [],
"dependencies": {
"@vscode/debugadapter": "^1.51.0",
"await-notify": "1.0.1",
Expand All @@ -53,10 +53,10 @@
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.0",
"@types/node": "^17.0.13",
"@types/vscode": "^1.61.0",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@types/vscode": "^1.64.0",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.1",
"@vscode/debugadapter-testsupport": "^1.51.0",
"@vscode/debugprotocol": "^1.53.0",
"eslint": "^8.8.0",
"glob": "^7.2.0",
"mocha": "^9.2.0",
Expand Down
74 changes: 57 additions & 17 deletions src/debugProtocol.json
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,15 @@
"properties": {
"category": {
"type": "string",
"description": "The output category. If not specified, 'console' is assumed.",
"_enum": [ "console", "stdout", "stderr", "telemetry" ]
"description": "The output category. If not specified or if the category is not understand by the client, 'console' is assumed.",
"_enum": [ "console", "important", "stdout", "stderr", "telemetry" ],
"enumDescriptions": [
"Show the output in the client's default message UI, e.g. a 'debug console'. This category should only be used for informational output from the debugger (as opposed to the debuggee).",
"A hint for the client to show the ouput in the client's UI for important and highly visible information, e.g. as a popup notification. This category should only be used for important messages from the debugger (as opposed to the debuggee). Since this category value is a hint, clients might ignore the hint and assume the 'console' category.",
"Show the output as normal program output from the debuggee.",
"Show the output as error program output from the debuggee.",
"Send the output to telemetry instead of showing it to the user."
]
},
"output": {
"type": "string",
Expand Down Expand Up @@ -1378,7 +1385,7 @@
},
"name": {
"type": "string",
"description": "The name of the Variable's child to obtain data breakpoint information for.\nIf variablesReference isnt provided, this can be an expression."
"description": "The name of the Variable's child to obtain data breakpoint information for.\nIf variablesReference isn't provided, this can be an expression."
}
},
"required": [ "name" ]
Expand Down Expand Up @@ -1531,7 +1538,7 @@
"ContinueRequest": {
"allOf": [ { "$ref": "#/definitions/Request" }, {
"type": "object",
"description": "The request starts the debuggee to run again.",
"description": "The request resumes execution of all threads. If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true resumes only the specified thread. If not all threads were resumed, the 'allThreadsContinued' attribute of the response must be set to false.",
"properties": {
"command": {
"type": "string",
Expand All @@ -1550,7 +1557,11 @@
"properties": {
"threadId": {
"type": "integer",
"description": "Continue execution for the specified thread (if possible).\nIf the backend cannot continue on a single thread but will continue on all threads, it should set the 'allThreadsContinued' attribute in the response to true."
"description": "Specifies the active thread. If the debug adapter supports single thread execution (see 'supportsSingleThreadExecutionRequests') and the optional argument 'singleThread' is true, only the thread with this ID is resumed."
},
"singleThread": {
"type": "boolean",
"description": "If this optional flag is true, execution is resumed only for the thread with given 'threadId'."
}
},
"required": [ "threadId" ]
Expand All @@ -1565,7 +1576,7 @@
"properties": {
"allThreadsContinued": {
"type": "boolean",
"description": "If true, the 'continue' request has ignored the specified thread and continued all threads instead.\nIf this attribute is missing a value of 'true' is assumed for backward compatibility."
"description": "The value true (or a missing property) signals to the client that all threads have been resumed. The value false must be returned if not all threads were resumed."
}
}
}
Expand All @@ -1577,7 +1588,7 @@
"NextRequest": {
"allOf": [ { "$ref": "#/definitions/Request" }, {
"type": "object",
"description": "The request starts the debuggee to run again for one step.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.",
"description": "The request executes one step (in the given granularity) for the specified thread and allows all other threads to run freely by resuming them.\nIf the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.",
"properties": {
"command": {
"type": "string",
Expand All @@ -1596,7 +1607,11 @@
"properties": {
"threadId": {
"type": "integer",
"description": "Execute 'next' for this thread."
"description": "Specifies the thread for which to resume execution for one step (of the given granularity)."
},
"singleThread": {
"type": "boolean",
"description": "If this optional flag is true, all other suspended threads are not resumed."
},
"granularity": {
"$ref": "#/definitions/SteppingGranularity",
Expand All @@ -1615,7 +1630,7 @@
"StepInRequest": {
"allOf": [ { "$ref": "#/definitions/Request" }, {
"type": "object",
"description": "The request starts the debuggee to step into a function/method if possible.\nIf it cannot step into a target, 'stepIn' behaves like 'next'.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.\nIf there are multiple function/method calls (or other targets) on the source line,\nthe optional argument 'targetId' can be used to control into which target the 'stepIn' should occur.\nThe list of possible targets for a given source line can be retrieved via the 'stepInTargets' request.",
"description": "The request resumes the given thread to step into a function/method and allows all other threads to run freely by resuming them.\nIf the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.\nIf the request cannot step into a target, 'stepIn' behaves like the 'next' request.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.\nIf there are multiple function/method calls (or other targets) on the source line,\nthe optional argument 'targetId' can be used to control into which target the 'stepIn' should occur.\nThe list of possible targets for a given source line can be retrieved via the 'stepInTargets' request.",
"properties": {
"command": {
"type": "string",
Expand All @@ -1634,7 +1649,11 @@
"properties": {
"threadId": {
"type": "integer",
"description": "Execute 'stepIn' for this thread."
"description": "Specifies the thread for which to resume execution for one step-into (of the given granularity)."
},
"singleThread": {
"type": "boolean",
"description": "If this optional flag is true, all other suspended threads are not resumed."
},
"targetId": {
"type": "integer",
Expand All @@ -1657,7 +1676,7 @@
"StepOutRequest": {
"allOf": [ { "$ref": "#/definitions/Request" }, {
"type": "object",
"description": "The request starts the debuggee to run again for one step.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.",
"description": "The request resumes the given thread to step out (return) from a function/method and allows all other threads to run freely by resuming them.\nIf the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.",
"properties": {
"command": {
"type": "string",
Expand All @@ -1676,7 +1695,11 @@
"properties": {
"threadId": {
"type": "integer",
"description": "Execute 'stepOut' for this thread."
"description": "Specifies the thread for which to resume execution for one step-out (of the given granularity)."
},
"singleThread": {
"type": "boolean",
"description": "If this optional flag is true, all other suspended threads are not resumed."
},
"granularity": {
"$ref": "#/definitions/SteppingGranularity",
Expand All @@ -1695,7 +1718,7 @@
"StepBackRequest": {
"allOf": [ { "$ref": "#/definitions/Request" }, {
"type": "object",
"description": "The request starts the debuggee to run one step backwards.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.\nClients should only call this request if the capability 'supportsStepBack' is true.",
"description": "The request executes one backward step (in the given granularity) for the specified thread and allows all other threads to run backward freely by resuming them.\nIf the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.\nClients should only call this request if the capability 'supportsStepBack' is true.",
"properties": {
"command": {
"type": "string",
Expand All @@ -1714,7 +1737,11 @@
"properties": {
"threadId": {
"type": "integer",
"description": "Execute 'stepBack' for this thread."
"description": "Specifies the thread for which to resume execution for one step backwards (of the given granularity)."
},
"singleThread": {
"type": "boolean",
"description": "If this optional flag is true, all other suspended threads are not resumed."
},
"granularity": {
"$ref": "#/definitions/SteppingGranularity",
Expand All @@ -1733,7 +1760,7 @@
"ReverseContinueRequest": {
"allOf": [ { "$ref": "#/definitions/Request" }, {
"type": "object",
"description": "The request starts the debuggee to run backward.\nClients should only call this request if the capability 'supportsStepBack' is true.",
"description": "The request resumes backward execution of all threads. If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true resumes only the specified thread. If not all threads were resumed, the 'allThreadsContinued' attribute of the response must be set to false.\nClients should only call this request if the capability 'supportsStepBack' is true.",
"properties": {
"command": {
"type": "string",
Expand All @@ -1752,8 +1779,13 @@
"properties": {
"threadId": {
"type": "integer",
"description": "Execute 'reverseContinue' for this thread."
"description": "Specifies the active thread. If the debug adapter supports single thread execution (see 'supportsSingleThreadExecutionRequests') and the optional argument 'singleThread' is true, only the thread with this ID is resumed."
},
"singleThread": {
"type": "boolean",
"description": "If this optional flag is true, backward execution is resumed only for the thread with given 'threadId'."
}

},
"required": [ "threadId" ]
},
Expand Down Expand Up @@ -3109,6 +3141,10 @@
"supportsExceptionFilterOptions": {
"type": "boolean",
"description": "The debug adapter supports 'filterOptions' as an argument on the 'setExceptionBreakpoints' request."
},
"supportsSingleThreadExecutionRequests": {
"type": "boolean",
"description": "The debug adapter supports the 'singleThread' property on the execution requests ('continue', 'next', 'stepIn', 'stepOut', 'reverseContinue', 'stepBack')."
}
}
},
Expand Down Expand Up @@ -3459,7 +3495,7 @@
},
"value": {
"type": "string",
"description": "The variable's value. This can be a multi-line text, e.g. for a function the body of a function."
"description": "The variable's value.\nThis can be a multi-line text, e.g. for a function the body of a function.\nFor structured variables (which do not have a simple value), it is recommended to provide a one line representation of the structured object. This helps to identify the structured object in the collapsed state when its children are not yet visible.\nAn empty string can be used if no value should be shown in the UI."
},
"type": {
"type": "string",
Expand Down Expand Up @@ -3794,6 +3830,10 @@
"type": "string",
"description": "A string that should be used when comparing this item with other items. When `falsy` the label is used."
},
"detail": {
"type": "string",
"description": "A human-readable string with additional information about this item, like type or symbol information."
},
"type": {
"$ref": "#/definitions/CompletionItemType",
"description": "The item's type. Typically the client uses this information to render the item in the UI with an icon."
Expand Down
36 changes: 23 additions & 13 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as vscode from 'vscode';
import { DebugAdapterTracker, DebugAdapterTrackerFactory, } from 'vscode';

// This is just the default. It will be updated after the configuration has been resolved.
var probeRsLogLevel = 'Info';
var probeRsLogLevel = 'Error';

export async function activate(context: vscode.ExtensionContext) {

Expand Down Expand Up @@ -47,10 +47,22 @@ function handleExit(code: number | null, signal: string | null) {
}
}

// Messages to be sent to the debug session's console. Anything sent before or after an active debug session is silently ignored by VSCode. Ditto for any messages that doesn't start with 'ERROR', or 'INFO' , or 'WARN', ... unless the log level is DEBUG. Then everything is logged.
function logToConsole(consoleMesssage: string) {
// Messages to be sent to the debug session's console.
// Anything sent before or after an active debug session is silently ignored by VSCode.
// Any local (generated directly by this extension) messages MUST start with 'ERROR', or 'INFO' , 'WARN', `DEBUG`, or `TRACE` to match the RUST log behaviour.
// Any local messages that start with `CONSOLE` will ALWAYS be logged.
// Any messages that come from the `probe-rs-debugger` STDERR will always be logged, and will already conform with the RUST LOG setting.
function logToConsole(consoleMesssage: string, fromDebugger: boolean = false) {
console.log(consoleMesssage); // During VSCode extension development, this will also log to the local debug console
if (consoleMesssage.includes('CONSOLE')) {
if (fromDebugger) {
// Any messages that come directly from the debugger, are assumed to be RUST_LOG messages and should be logged to the console.
vscode.debug.activeDebugConsole.appendLine(consoleMesssage);
// The one exception is RUST_LOG messages of the `error` variant. These deserve to be shown as an error message in the UI also.
// This filter might capture more than expected, but since RUST_LOG messages can take many formats, it seems that this is the safest/most inclusive.
if (consoleMesssage.includes("ERROR")) {
vscode.window.showErrorMessage("`probe-rs-debugger`: " + consoleMesssage);
}
} else if (consoleMesssage.includes('CONSOLE')) {
vscode.debug.activeDebugConsole.appendLine(consoleMesssage);
} else {
switch (probeRsLogLevel) {
Expand Down Expand Up @@ -178,7 +190,10 @@ class ProbeRSDebugAdapterServerDescriptorFactory implements vscode.DebugAdapterD
// - The decision was made during investigation of an [issue](https://github.com/probe-rs/probe-rs/issues/703) ... basically, after the probe-rs API was fixed, the code would work well for TCP connections (`DebugAdapterServer`), but would not work for STDIO connections (`DebugAdapterServer`). After some searches I found other extension developers that also found the TCP based connections to be more stable.
// - Since then, we have taken advantage of the access to stderr that `DebugAdapterServer` offers to route `RUST_LOG` output from the debugger to the user's VSCode Debug Console. This is a very useful capability, and cannot easily be implemented in `DebugAdapterExecutable`, because it does not allow access to `stderr` [See ongoing issue in VScode repo](https://github.com/microsoft/vscode/issues/108145).
async createDebugAdapterDescriptor(session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined): Promise<vscode.DebugAdapterDescriptor | null | undefined> {
probeRsLogLevel = session.configuration.consoleLogLevel;
if (session.configuration.hasOwnProperty('consoleLogLevel')) {
probeRsLogLevel = session.configuration.consoleLogLevel.toLowerCase();
};


// Initiate either the 'attach' or 'launch' request.
logToConsole("INFO: Session: " + JSON.stringify(session, null, 2));
Expand Down Expand Up @@ -219,15 +234,10 @@ class ProbeRSDebugAdapterServerDescriptorFactory implements vscode.DebugAdapterD
args.push("--port");
args.push(debugServer[1]);

var logEnv = 'error'; //This is the default
if (session.configuration.hasOwnProperty('consoleLogLevel')) {
logEnv = session.configuration.consoleLogLevel.toLowerCase();
};

var options = {
cwd: session.configuration.cwd,
// eslint-disable-next-line @typescript-eslint/naming-convention
env: { ...process.env, 'RUST_LOG': logEnv, },
env: { ...process.env, 'RUST_LOG': probeRsLogLevel, 'DEFMT_LOG': probeRsLogLevel },
windowsHide: true,
};

Expand Down Expand Up @@ -267,9 +277,9 @@ class ProbeRSDebugAdapterServerDescriptorFactory implements vscode.DebugAdapterD
});
launchedDebugAdapter.stderr?.on('data', (data: string) => {
if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) {
logToConsole("ERROR: " + data);
logToConsole(data, true);
} else {
vscode.window.showErrorMessage("`probe-rs-debugger` error: " + data);
vscode.window.showErrorMessage("`probe-rs-debugger`: " + data);
}
});
launchedDebugAdapter.on('close', (code: number | null, signal: string | null) => {
Expand Down

0 comments on commit b751ff2

Please sign in to comment.