Skip to content

Commit 8aa356b

Browse files
Christian Weichelspoenemann
Christian Weichel
authored andcommitted
Automated debug config setup
1 parent ea5f528 commit 8aa356b

File tree

9 files changed

+325
-66
lines changed

9 files changed

+325
-66
lines changed

arduino-debugger-extension/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"dependencies": {
1010
"@theia/core": "next",
1111
"@theia/debug": "next",
12-
12+
"arduino-ide-extension": "0.0.2",
1313
"cdt-gdb-adapter": "^0.0.14-next.4783033.0",
1414
"vscode-debugadapter": "^1.26.0",
1515
"vscode-debugprotocol": "^1.26.0"
@@ -34,7 +34,8 @@
3434
],
3535
"theiaExtensions": [
3636
{
37-
"backend": "lib/node/backend-module"
37+
"backend": "lib/node/backend-module",
38+
"frontend": "lib/browser/frontend-module"
3839
}
3940
]
4041
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
2+
import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser';
3+
import { injectable, inject } from 'inversify';
4+
import URI from '@theia/core/lib/common/uri';
5+
import { BoardsServiceClientImpl } from 'arduino-ide-extension/lib/browser/boards/boards-service-client-impl';
6+
import { BoardsService, ToolLocations } from 'arduino-ide-extension/lib/common/protocol/boards-service';
7+
import { WorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
8+
9+
@injectable()
10+
export class ArduinoVariableResolver implements VariableContribution {
11+
12+
@inject(BoardsServiceClientImpl)
13+
protected readonly boardsServiceClient: BoardsServiceClientImpl;
14+
15+
@inject(BoardsService)
16+
protected readonly boardsService: BoardsService;
17+
18+
@inject(WorkspaceVariableContribution)
19+
protected readonly workspaceVars: WorkspaceVariableContribution;
20+
21+
registerVariables(variables: VariableRegistry): void {
22+
variables.registerVariable(<Variable>{
23+
name: `boardTools`,
24+
description: "Provides paths and access to board specific tooling",
25+
resolve: this.resolveBoardTools.bind(this),
26+
});
27+
variables.registerVariable(<Variable>{
28+
name: "board",
29+
description: "Provides details about the currently selected board",
30+
resolve: this.resolveBoard.bind(this),
31+
});
32+
33+
variables.registerVariable({
34+
name: "sketchBinary",
35+
description: "Path to the sketch's binary file",
36+
resolve: this.resolveSketchBinary.bind(this)
37+
});
38+
}
39+
40+
// TODO: this function is a total hack. Instead of botching around with URI's it should ask something on the backend
41+
// that properly udnerstands the filesystem.
42+
protected async resolveSketchBinary(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
43+
let sketchPath = argument || this.workspaceVars.getResourceUri()!.path.toString();
44+
return sketchPath.substring(0, sketchPath.length - 3) + "arduino.samd.arduino_zero_edbg.elf";
45+
}
46+
47+
protected async resolveBoard(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
48+
const { boardsConfig } = this.boardsServiceClient;
49+
if (!boardsConfig || !boardsConfig.selectedBoard) {
50+
throw new Error('No boards selected. Please select a board.');
51+
}
52+
53+
if (!argument || argument === "fqbn") {
54+
return boardsConfig.selectedBoard.fqbn!;
55+
}
56+
if (argument === "name") {
57+
return boardsConfig.selectedBoard.name;
58+
}
59+
60+
const details = await this.boardsService.detail({id: boardsConfig.selectedBoard.fqbn!});
61+
if (!details.item) {
62+
throw new Error("Cannot get board details");
63+
}
64+
if (argument === "openocd-debug-file") {
65+
return details.item.locations!.debugScript;
66+
}
67+
68+
return boardsConfig.selectedBoard.fqbn!;
69+
}
70+
71+
protected async resolveBoardTools(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
72+
const { boardsConfig } = this.boardsServiceClient;
73+
if (!boardsConfig || !boardsConfig.selectedBoard) {
74+
throw new Error('No boards selected. Please select a board.');
75+
}
76+
const details = await this.boardsService.detail({id: boardsConfig.selectedBoard.fqbn!});
77+
if (!details.item) {
78+
throw new Error("Cannot get board details")
79+
}
80+
81+
let toolLocations: { [name: string]: ToolLocations } = {};
82+
details.item.requiredTools.forEach(t => {
83+
toolLocations[t.name] = t.locations!;
84+
})
85+
86+
switch(argument) {
87+
case "openocd":
88+
return toolLocations["openocd"].main;
89+
case "openocd-scripts":
90+
return toolLocations["openocd"].scripts;
91+
case "objdump":
92+
return toolLocations["arm-none-eabi-gcc"].objdump;
93+
case "gdb":
94+
return toolLocations["arm-none-eabi-gcc"].gdb;
95+
}
96+
97+
return boardsConfig.selectedBoard.name;
98+
}
99+
100+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ContainerModule } from 'inversify';
2+
import { VariableContribution } from '@theia/variable-resolver/lib/browser';
3+
import { ArduinoVariableResolver } from './arduino-variable-resolver';
4+
5+
export default new ContainerModule((bind, unbind, isBound, rebind) => {
6+
bind(ArduinoVariableResolver).toSelf().inSingletonScope();
7+
bind(VariableContribution).toService(ArduinoVariableResolver);
8+
});
Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
import { injectable } from 'inversify';
1+
import { injectable, inject } from 'inversify';
22
import { DebugAdapterContribution, DebugAdapterExecutable, DebugAdapterSessionFactory } from '@theia/debug/lib/common/debug-model';
33
import { DebugConfiguration } from "@theia/debug/lib/common/debug-configuration";
44
import { MaybePromise } from "@theia/core/lib/common/types";
55
import { IJSONSchema, IJSONSchemaSnippet } from "@theia/core/lib/common/json-schema";
66
import * as path from 'path';
7+
import { BoardsService } from 'arduino-ide-extension/lib/common/protocol/boards-service';
8+
import { CoreService } from 'arduino-ide-extension/lib/common/protocol/core-service';
9+
import { FileSystem } from '@theia/filesystem/lib/common';
710

811
@injectable()
912
export class ArduinoDebugAdapterContribution implements DebugAdapterContribution {
13+
@inject(BoardsService)
14+
protected readonly boardsService: BoardsService;
15+
16+
@inject(CoreService)
17+
protected readonly coreService: CoreService;
18+
19+
@inject(FileSystem)
20+
protected readonly fileSystem: FileSystem;
21+
1022
type = "arduino";
1123

1224
label = "Arduino";
@@ -22,56 +34,22 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
2234
"program"
2335
],
2436
"properties": {
25-
"program": {
26-
"type": "string",
27-
"description": "Path to the program to be launched",
28-
"default": "${workspaceFolder}/${command:askProgramPath}"
29-
},
3037
"sketch": {
3138
"type": "string",
32-
"description": "Path to the sketch folder",
33-
"default": "${workspaceFolder}"
34-
},
35-
"fbqn": {
36-
"type": "string",
37-
"description": "Fully qualified board name of the debugging target",
38-
"default": "unknown"
39+
"description": "path to the sketch root ino file",
40+
"default": "${file}",
3941
},
4042
"runToMain": {
4143
"description": "If enabled the debugger will run until the start of the main function.",
4244
"type": "boolean",
4345
"default": false
4446
},
45-
"gdb": {
46-
"type": "string",
47-
"description": "Path to gdb",
48-
"default": "arm-none-eabi-gdb"
49-
},
50-
"gdbArguments": {
51-
"description": "Additional arguments to pass to GDB command line",
52-
"type": "array",
53-
"default": []
54-
},
55-
"gdbServer": {
47+
"fqbn": {
5648
"type": "string",
57-
"description": "Path to gdb server",
58-
"default": "pyocd"
59-
},
60-
"gdbServerArguments": {
61-
"description": "Additional arguments to pass to GDB server",
62-
"type": "array",
63-
"default": []
64-
},
65-
"objdump": {
66-
"type": "string",
67-
"description": "Path to objdump executable",
68-
"default": "arm-none-eabi-objdump"
69-
},
70-
"initCommands": {
71-
"description": "Extra gdb commands to run after initialisation",
72-
"type": "array",
73-
"default": []
49+
"description": "Fully-qualified board name to debug on",
50+
"default": ""
7451
},
52+
7553
"verbose": {
7654
"type": "boolean",
7755
"description": "Produce verbose log output",
@@ -105,11 +83,71 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
10583
}
10684

10785
provideDebugConfigurations?(workspaceFolderUri?: string): MaybePromise<DebugConfiguration[]> {
108-
return [];
86+
return [
87+
<DebugConfiguration>{
88+
name: this.label,
89+
type: this.type,
90+
request: "launch",
91+
92+
sketch: "${file}",
93+
94+
verbose: true,
95+
runToMain: true,
96+
},
97+
<DebugConfiguration>{
98+
name: this.label + " (explicit)",
99+
type: this.type,
100+
request: "launch",
101+
102+
program: "${sketchBinary}",
103+
objdump: "${boardTools:objdump}",
104+
gdb: "${boardTools:gdb}",
105+
gdbServer: "${boardTools:openocd}",
106+
gdbServerArguments: ["-s", "${boardTools:openocd-scripts}", "--file", "${board:openocd-debug-file}"],
107+
108+
verbose: true,
109+
runToMain: true,
110+
}
111+
];
109112
}
110113

111-
resolveDebugConfiguration?(config: DebugConfiguration, workspaceFolderUri?: string): MaybePromise<DebugConfiguration> {
112-
return config;
114+
async resolveDebugConfiguration?(config: DebugConfiguration, workspaceFolderUri?: string): Promise<DebugConfiguration> {
115+
// if program is present we expect to have an explicit config here
116+
if (!!config.program) {
117+
return config;
118+
}
119+
120+
let sketchBinary = "${sketchBinary}"
121+
if (config.sketch !== "${file}") {
122+
sketchBinary = "${sketchBinary:" + config.sketch + "}";
123+
}
124+
const res: ActualDebugConfig = {
125+
...config,
126+
127+
objdump: "${boardTools:objdump}",
128+
gdb: "${boardTools:gdb}",
129+
gdbServer: "${boardTools:openocd}",
130+
gdbServerArguments: ["-s", "${boardTools:openocd-scripts}", "--file", "${board:openocd-debug-file}"],
131+
program: sketchBinary
132+
}
133+
return res;
113134
}
114135

136+
}
137+
138+
interface ActualDebugConfig extends DebugConfiguration {
139+
// path to the program to be launched
140+
program: string
141+
// path to gdb
142+
gdb: string
143+
// additional arguments to pass to GDB command line
144+
gdbArguments?: string[]
145+
// path to the gdb server
146+
gdbServer: string
147+
// additional arguments to pass to GDB server
148+
gdbServerArguments: string[]
149+
// path to objdump executable
150+
objdump: string
151+
// extra gdb commands to run after initialisation
152+
initCommands?: string[]
115153
}

arduino-debugger-extension/src/node/debug-adapter/cmsis-debug-session.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import { normalize } from 'path';
2727
import { DebugProtocol } from 'vscode-debugprotocol';
2828
import { Logger, logger, InitializedEvent, OutputEvent, Scope, TerminatedEvent } from 'vscode-debugadapter';
29-
import { GDBDebugSession, RequestArguments, FrameVariableReference, FrameReference, ObjectVariableReference } from 'cdt-gdb-adapter/dist/GDBDebugSession';
29+
import { GDBDebugSession, RequestArguments, FrameVariableReference, FrameReference } from 'cdt-gdb-adapter/dist/GDBDebugSession';
3030
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
3131
import { CmsisBackend } from './cmsis-backend';
3232
// import { PyocdServer } from './pyocd-server';
@@ -123,15 +123,15 @@ export class CmsisDebugSession extends GDBDebugSession {
123123
type: 'frame',
124124
frameHandle: args.frameId,
125125
};
126-
const pins: ObjectVariableReference = {
127-
type: "object",
128-
varobjName: "__pins",
129-
frameHandle: args.frameId,
130-
}
126+
// const pins: ObjectVariableReference = {
127+
// type: "object",
128+
// varobjName: "__pins",
129+
// frameHandle: 42000,
130+
// }
131131

132132
response.body = {
133133
scopes: [
134-
new Scope('Pins', this.variableHandles.create(pins), false),
134+
// new Scope('Pins', this.variableHandles.create(pins), false),
135135
new Scope('Local', this.variableHandles.create(frame), false),
136136
new Scope('Global', GLOBAL_HANDLE_ID, false),
137137
new Scope('Static', STATIC_HANDLES_START + parseInt(args.frameId as any, 10), false)
@@ -162,6 +162,8 @@ export class CmsisDebugSession extends GDBDebugSession {
162162
} else if (ref && ref.type === 'frame') {
163163
// List variables for current frame
164164
response.body.variables = await this.handleVariableRequestFrame(ref);
165+
} else if (ref && ref.varobjName === '__pins') {
166+
response.body.variables = await this.handlePinStatusRequest();
165167
} else if (ref && ref.type === 'object') {
166168
// List data under any variable
167169
response.body.variables = await this.handleVariableRequestObject(ref);
@@ -300,6 +302,17 @@ export class CmsisDebugSession extends GDBDebugSession {
300302
return variables;
301303
}
302304

305+
private async handlePinStatusRequest(): Promise<DebugProtocol.Variable[]> {
306+
const variables: DebugProtocol.Variable[] = [];
307+
variables.push({
308+
name: "D2",
309+
type: "gpio",
310+
value: "0x00",
311+
variablesReference: 0
312+
})
313+
return variables;
314+
}
315+
303316
private async getStaticVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
304317
const frame = this.frameHandles.get(frameHandle);
305318
const result = await mi.sendStackInfoFrame(this.gdb, frame.threadId, frame.frameId);

arduino-debugger-extension/src/node/debug-adapter/openocd-server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PortScanner } from './port-scanner';
33
import { CmsisRequestArguments } from './cmsis-debug-session';
44
import * as fs from 'fs-extra';
55
import * as path from 'path';
6+
import * as os from 'os';
67

78
const LAUNCH_REGEX = /GDB server started/;
89
const ERROR_REGEX = /:ERROR:gdbserver:/;
@@ -20,7 +21,7 @@ export class OpenocdServer extends AbstractServer {
2021
}
2122
sessionConfigFile += `echo "GDB server started"${"\n"}`
2223

23-
const tmpdir = await fs.mkdtemp("arduino-debugger");
24+
const tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), "arduino-debugger"));
2425
const sessionCfgPath = path.join(tmpdir, "gdb.cfg");
2526
await fs.writeFile(sessionCfgPath, sessionConfigFile);
2627

0 commit comments

Comments
 (0)