Skip to content

Commit 3d6d2ce

Browse files
Christian Weichelspoenemann
Christian Weichel
authored andcommitted
Ran first debugging session
1 parent e189a8c commit 3d6d2ce

22 files changed

+1428
-9
lines changed

.vscode/arduino.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"board": "arduino:samd:arduino_zero_edbg",
3+
"port": "/dev/cu.usbmodem141402"
4+
}

.vscode/c_cpp_properties.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"configurations": [
3+
{
4+
"name": "Mac",
5+
"includePath": [
6+
"/Users/csweichel/Library/Arduino15/packages/arduino/tools/**",
7+
"/Users/csweichel/Library/Arduino15/packages/arduino/hardware/samd/1.8.4/**"
8+
],
9+
"forcedInclude": [
10+
"/Users/csweichel/Library/Arduino15/packages/arduino/hardware/samd/1.8.4/cores/arduino/Arduino.h"
11+
]
12+
}
13+
]
14+
}

.vscode/launch.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
"name": "Attach by Process ID",
1111
"processId": "${command:PickProcess}"
1212
},
13+
{
14+
"type": "node",
15+
"request": "launch",
16+
"name": "Launch Electron Packager",
17+
"program": "${workspaceRoot}/electron/packager/index.js",
18+
"cwd": "${workspaceFolder}/electron/packager"
19+
},
1320
{
1421
"type": "node",
1522
"request": "launch",
@@ -99,13 +106,6 @@
99106
"smartStep": true,
100107
"internalConsoleOptions": "openOnSessionStart",
101108
"outputCapture": "std"
102-
},
103-
{
104-
"type": "node",
105-
"request": "launch",
106-
"name": "Packager",
107-
"program": "${workspaceRoot}/electron/packager/index.js",
108-
"cwd": "${workspaceFolder}/electron/packager"
109109
}
110110
]
111111
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "arduino-debugger-extension",
3+
"version": "0.0.2",
4+
"description": "An extension for debugging Arduino programs",
5+
"license": "MIT",
6+
"engines": {
7+
"node": ">=10.10.0"
8+
},
9+
"dependencies": {
10+
"@theia/core": "next",
11+
"@theia/debug": "next",
12+
13+
"cdt-gdb-adapter": "^0.0.14-next.4783033.0",
14+
"vscode-debugadapter": "^1.26.0",
15+
"vscode-debugprotocol": "^1.26.0"
16+
},
17+
"scripts": {
18+
"prepare": "yarn run clean && yarn run build",
19+
"clean": "rimraf lib",
20+
"lint": "tslint -c ./tslint.json --project ./tsconfig.json",
21+
"build": "tsc && yarn lint",
22+
"watch": "tsc -w"
23+
},
24+
"devDependencies": {
25+
"rimraf": "^2.6.1",
26+
"tslint": "^5.5.0",
27+
"typescript": "3.5.1"
28+
},
29+
"files": [
30+
"lib",
31+
"src",
32+
"build",
33+
"data"
34+
],
35+
"theiaExtensions": [
36+
{
37+
"backend": "lib/node/backend-module"
38+
}
39+
]
40+
}

arduino-debugger-extension/src/common/index.ts

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
gdb_port 50000
2+
telnet_port 44444
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
source [find interface/cmsis-dap.cfg]
2+
3+
# chip name
4+
set CHIPNAME at91samd21g18
5+
set ENDIAN little
6+
7+
# choose a port here
8+
set telnet_port 0
9+
10+
source [find target/at91samdXX.cfg]
11+
12+
echo "GDB server started"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { injectable } from 'inversify';
2+
import { DebugAdapterContribution, DebugAdapterExecutable, DebugAdapterSessionFactory } from '@theia/debug/lib/common/debug-model';
3+
import { DebugConfiguration } from "@theia/debug/lib/common/debug-configuration";
4+
import { MaybePromise } from "@theia/core/lib/common/types";
5+
import { IJSONSchema, IJSONSchemaSnippet } from "@theia/core/lib/common/json-schema";
6+
import * as path from 'path';
7+
8+
@injectable()
9+
export class ArduinoDebugAdapterContribution implements DebugAdapterContribution {
10+
type = "arduino";
11+
12+
label = "Arduino";
13+
14+
languages = ["c", "cpp", "ino"];
15+
16+
debugAdapterSessionFactory?: DebugAdapterSessionFactory;
17+
18+
getSchemaAttributes?(): MaybePromise<IJSONSchema[]> {
19+
return [
20+
{
21+
"required": [
22+
"program"
23+
],
24+
"properties": {
25+
"program": {
26+
"type": "string",
27+
"description": "Path to the program to be launched",
28+
"default": "${workspaceFolder}/${command:askProgramPath}"
29+
},
30+
"arguments": {
31+
"type": "string",
32+
"description": "Arguments for the program"
33+
},
34+
"runToMain": {
35+
"description": "If enabled the debugger will run until the start of the main function.",
36+
"type": "boolean",
37+
"default": false
38+
},
39+
"gdb": {
40+
"type": "string",
41+
"description": "Path to gdb",
42+
"default": "arm-none-eabi-gdb"
43+
},
44+
"gdbArguments": {
45+
"description": "Additional arguments to pass to GDB command line",
46+
"type": "array",
47+
"default": []
48+
},
49+
"gdbServer": {
50+
"type": "string",
51+
"description": "Path to gdb server",
52+
"default": "pyocd"
53+
},
54+
"gdbServerArguments": {
55+
"description": "Additional arguments to pass to GDB server",
56+
"type": "array",
57+
"default": []
58+
},
59+
"objdump": {
60+
"type": "string",
61+
"description": "Path to objdump executable",
62+
"default": "arm-none-eabi-objdump"
63+
},
64+
"initCommands": {
65+
"description": "Extra gdb commands to run after initialisation",
66+
"type": "array",
67+
"default": []
68+
},
69+
"verbose": {
70+
"type": "boolean",
71+
"description": "Produce verbose log output",
72+
"default": "false"
73+
},
74+
"debugDebugAdapter": {
75+
"type": "boolean",
76+
"description": "Start the debug adapter in debug mode (with --inspect-brk)",
77+
"default": "false"
78+
}
79+
}
80+
}
81+
]
82+
}
83+
84+
getConfigurationSnippets?(): MaybePromise<IJSONSchemaSnippet[]> {
85+
return []
86+
}
87+
88+
provideDebugAdapterExecutable?(config: DebugConfiguration): MaybePromise<DebugAdapterExecutable> {
89+
let args: string[] = [];
90+
if (!!config.debugDebugAdapter) {
91+
args.push('--inspect-brk')
92+
}
93+
args = args.concat([path.join(__dirname, 'debug-adapter', 'index.js')]);
94+
95+
return {
96+
command: "node",
97+
args: args,
98+
}
99+
}
100+
101+
provideDebugConfigurations?(workspaceFolderUri?: string): MaybePromise<DebugConfiguration[]> {
102+
return [];
103+
}
104+
105+
resolveDebugConfiguration?(config: DebugConfiguration, workspaceFolderUri?: string): MaybePromise<DebugConfiguration> {
106+
return config;
107+
}
108+
109+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ContainerModule } from 'inversify';
2+
import { DebugAdapterContribution } from '@theia/debug/lib/common/debug-model';
3+
import { ArduinoDebugAdapterContribution } from './arduino-debug-adapter-contribution';
4+
5+
export default new ContainerModule((bind, unbind, isBound, rebind) => {
6+
bind(DebugAdapterContribution).to(ArduinoDebugAdapterContribution).inSingletonScope();
7+
});
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* CMSIS Debug Adapter
3+
* Copyright (c) 2017-2019 Marcel Ball
4+
* Copyright (c) 2019 Arm Limited
5+
*
6+
* The MIT License (MIT)
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in all
16+
* copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*/
26+
27+
import { EOL } from 'os';
28+
import { spawn, ChildProcess } from 'child_process';
29+
import { EventEmitter } from 'events';
30+
import { dirname } from 'path';
31+
import { CmsisRequestArguments } from './cmsis-debug-session';
32+
33+
const TIMEOUT = 1000 * 10; // 10 seconds
34+
35+
export abstract class AbstractServer extends EventEmitter {
36+
37+
protected process?: ChildProcess;
38+
protected outBuffer: string = '';
39+
protected errBuffer: string = '';
40+
protected launchResolve?: () => void;
41+
protected launchReject?: (error: any) => void;
42+
protected timer?: NodeJS.Timer;
43+
44+
public spawn(args: CmsisRequestArguments): Promise<void> {
45+
return new Promise(async (resolve, reject) => {
46+
this.launchResolve = resolve;
47+
this.launchReject = reject;
48+
49+
try {
50+
this.timer = setTimeout(() => this.onSpawnError(new Error('Timeout waiting for gdb server to start')), TIMEOUT);
51+
52+
const command = args.gdbServer || 'gdb-server';
53+
const serverArguments = await this.resolveServerArguments(args.gdbServerArguments);
54+
this.process = spawn(command, serverArguments, {
55+
cwd: dirname(command),
56+
});
57+
58+
if (!this.process) {
59+
throw new Error('Unable to spawn gdb server');
60+
}
61+
62+
this.process.on('exit', this.onExit.bind(this));
63+
this.process.on('error', this.onSpawnError.bind(this));
64+
65+
if (this.process.stdout) {
66+
this.process.stdout.on('data', this.onStdout.bind(this));
67+
}
68+
if (this.process.stderr) {
69+
this.process.stderr.on('data', this.onStderr.bind(this));
70+
}
71+
} catch (error) {
72+
this.onSpawnError(error);
73+
}
74+
});
75+
}
76+
77+
public kill() {
78+
if (this.process) {
79+
this.process.kill('SIGINT');
80+
}
81+
}
82+
83+
protected async resolveServerArguments(serverArguments?: string[]): Promise<string[]> {
84+
return serverArguments || [];
85+
}
86+
87+
protected onExit(code: number, signal: string) {
88+
this.emit('exit', code, signal);
89+
90+
// Code can be undefined, null or 0 and we want to ignore those values
91+
if (!!code) {
92+
this.emit('error', `GDB server stopped unexpectedly with exit code ${code}`);
93+
}
94+
}
95+
96+
protected onSpawnError(error: Error) {
97+
if (this.launchReject) {
98+
this.clearTimer();
99+
this.launchReject(error);
100+
this.clearPromises();
101+
}
102+
}
103+
104+
protected onStdout(chunk: string | Buffer) {
105+
this.onData(chunk, this.outBuffer, 'stdout');
106+
}
107+
108+
protected onStderr(chunk: string | Buffer) {
109+
this.onData(chunk, this.errBuffer, 'stderr');
110+
}
111+
112+
protected onData(chunk: string | Buffer, buffer: string, event: string) {
113+
buffer += typeof chunk === 'string' ? chunk
114+
: chunk.toString('utf8');
115+
116+
const end = buffer.lastIndexOf('\n');
117+
if (end !== -1) {
118+
const data = buffer.substring(0, end);
119+
this.emit(event, data);
120+
this.handleData(data);
121+
buffer = buffer.substring(end + 1);
122+
}
123+
}
124+
125+
protected handleData(data: string) {
126+
if (this.launchResolve && this.serverStarted(data)) {
127+
this.clearTimer();
128+
this.launchResolve();
129+
this.clearPromises();
130+
}
131+
132+
if (this.serverError(data)) {
133+
this.emit('error', data.split(EOL)[0]);
134+
}
135+
}
136+
137+
protected clearTimer() {
138+
if (this.timer) {
139+
clearTimeout(this.timer);
140+
this.timer = undefined;
141+
}
142+
}
143+
144+
protected clearPromises() {
145+
this.launchResolve = undefined;
146+
this.launchReject = undefined;
147+
}
148+
149+
protected abstract serverStarted(data: string): boolean;
150+
protected abstract serverError(data: string): boolean;
151+
}

0 commit comments

Comments
 (0)