Skip to content

Commit a72533b

Browse files
committed
Added basic support for global and static variables
1 parent acf7b6a commit a72533b

File tree

6 files changed

+223
-33
lines changed

6 files changed

+223
-33
lines changed

arduino-debugger-extension/src/browser/arduino-debug-frontend-application-contribution.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { injectable, inject } from 'inversify';
22
import { MenuModelRegistry, Path, MessageService, Command, CommandRegistry } from '@theia/core';
3-
import { KeybindingRegistry, Widget } from '@theia/core/lib/browser';
3+
import { KeybindingRegistry } from '@theia/core/lib/browser';
44
import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
55
import { DebugFrontendApplicationContribution, DebugCommands } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
66
import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options";
77
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
8-
import { EditorMode } from "arduino-ide-extension/lib/browser/editor-mode";
9-
import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager';
10-
import { SketchesService } from 'arduino-ide-extension/lib/common/protocol/sketches-service';
118
import { FileSystem } from '@theia/filesystem/lib/common';
129
import URI from '@theia/core/lib/common/uri';
1310
import { EditorManager } from '@theia/editor/lib/browser';
11+
import { EditorMode } from "arduino-ide-extension/lib/browser/editor-mode";
12+
import { SketchesService } from 'arduino-ide-extension/lib/common/protocol/sketches-service';
1413
import { ArduinoToolbar } from 'arduino-ide-extension/lib/browser/toolbar/arduino-toolbar';
14+
import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager';
1515

1616
export namespace ArduinoDebugCommands {
1717
export const START_DEBUG: Command = {
@@ -109,9 +109,7 @@ export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendAp
109109
}
110110

111111
registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
112-
if (this.editorMode.proMode) {
113-
super.registerToolbarItems(toolbar);
114-
}
112+
super.registerToolbarItems(toolbar);
115113
toolbar.registerItem({
116114
id: ArduinoDebugCommands.START_DEBUG.id,
117115
command: ArduinoDebugCommands.START_DEBUG.id,
@@ -125,7 +123,7 @@ export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendAp
125123
registry.registerCommand(ArduinoDebugCommands.START_DEBUG, {
126124
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
127125
isEnabled: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
128-
execute: async (widget: Widget, target: EventTarget) => {
126+
execute: () => {
129127
registry.executeCommand(DebugCommands.START.id);
130128
}
131129
});

arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
7070
fqbn: '${fqbn}',
7171
uploadPort: '${port}',
7272
initCommands: [
73-
`-break-insert -t --function ${startFunction}`
73+
`-break-insert -t --function ${startFunction}`,
74+
'-interpreter-exec console "monitor reset halt"'
7475
]
7576
}
7677
if (!res.sketch) {
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import * as mi from 'cdt-gdb-adapter/dist/mi';
21
import { DebugProtocol } from 'vscode-debugprotocol';
3-
import { GDBDebugSession, LaunchRequestArguments } from 'cdt-gdb-adapter/dist/GDBDebugSession';
2+
import { GDBDebugSession, FrameVariableReference } from 'cdt-gdb-adapter/dist/GDBDebugSession';
43
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
4+
import * as mi from 'cdt-gdb-adapter/dist/mi';
55
import { ArduinoGDBBackend } from './arduino-gdb-backend';
6+
import { ArduinoVariableHandler } from './arduino-variable-handler';
7+
import { Scope } from 'vscode-debugadapter';
68

79
export interface ArduinoLaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
810
arduinoCli?: string;
@@ -11,26 +13,32 @@ export interface ArduinoLaunchRequestArguments extends DebugProtocol.LaunchReque
1113
uploadPort?: string;
1214
}
1315

16+
const GLOBAL_HANDLE_ID = 0xFE;
17+
const STATIC_HANDLES_START = 0x010000;
18+
const STATIC_HANDLES_FINISH = 0x01FFFF;
19+
1420
export class ArduinoDebugSession extends GDBDebugSession {
1521

16-
protected get arduinoBackend(): ArduinoGDBBackend {
22+
private _variableHandler: ArduinoVariableHandler;
23+
24+
get arduinoBackend(): ArduinoGDBBackend {
1725
return this.gdb as ArduinoGDBBackend;
1826
}
1927

20-
protected createBackend(): GDBBackend {
21-
return new ArduinoGDBBackend();
28+
protected get variableHandler() {
29+
if (this._variableHandler) {
30+
return this._variableHandler;
31+
}
32+
if (!this.gdb) {
33+
throw new Error("GDB backend is not ready.");
34+
}
35+
const handler = new ArduinoVariableHandler(this, this.frameHandles, this.variableHandles);
36+
this._variableHandler = handler;
37+
return handler;
2238
}
2339

24-
protected launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): Promise<void> {
25-
const additionalCommands = [
26-
'-interpreter-exec console "monitor reset halt"'
27-
];
28-
if (!args.initCommands) {
29-
args.initCommands = additionalCommands;
30-
} else {
31-
args.initCommands.push(...additionalCommands);
32-
}
33-
return super.launchRequest(response, args);
40+
protected createBackend(): GDBBackend {
41+
return new ArduinoGDBBackend();
3442
}
3543

3644
protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise<void> {
@@ -50,16 +58,75 @@ export class ArduinoDebugSession extends GDBDebugSession {
5058
this.gdb.pause();
5159
await waitPromise;
5260
}
53-
try {
54-
await this.arduinoBackend.sendTargetDetach();
55-
} catch (e) {
56-
// Need to catch here as the command result being returned will never exist as it's detached
57-
}
5861
await this.gdb.sendGDBExit();
5962
this.sendResponse(response);
6063
} catch (err) {
6164
this.sendErrorResponse(response, 1, err.message);
6265
}
6366
}
6467

68+
protected async stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): Promise<void> {
69+
try {
70+
71+
return super.stackTraceRequest(response, args);
72+
} catch (err) {
73+
this.sendErrorResponse(response, 1, err.message);
74+
}
75+
}
76+
77+
protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
78+
try {
79+
const frame: FrameVariableReference = {
80+
type: 'frame',
81+
frameHandle: args.frameId,
82+
};
83+
// const pins: ObjectVariableReference = {
84+
// type: "object",
85+
// varobjName: "__pins",
86+
// frameHandle: 42000,
87+
// }
88+
89+
response.body = {
90+
scopes: [
91+
// new Scope('Pins', this.variableHandles.create(pins), false),
92+
new Scope('Local', this.variableHandles.create(frame), false),
93+
new Scope('Global', GLOBAL_HANDLE_ID, false),
94+
new Scope('Static', STATIC_HANDLES_START + parseInt(args.frameId as any, 10), false)
95+
],
96+
};
97+
98+
this.sendResponse(response);
99+
} catch (err) {
100+
this.sendErrorResponse(response, 1, err.message);
101+
}
102+
}
103+
104+
protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise<void> {
105+
try {
106+
response.body = {
107+
variables: [] as DebugProtocol.Variable[]
108+
};
109+
const ref = this.variableHandles.get(args.variablesReference);
110+
if (args.variablesReference === GLOBAL_HANDLE_ID) {
111+
// Use hardcoded global handle to load and store global variables
112+
response.body.variables = await this.variableHandler.getGlobalVariables();
113+
} else if (args.variablesReference >= STATIC_HANDLES_START && args.variablesReference <= STATIC_HANDLES_FINISH) {
114+
// Use STATIC_HANDLES_START to shift the framehandles back
115+
const frameHandle = args.variablesReference - STATIC_HANDLES_START;
116+
response.body.variables = await this.variableHandler.getStaticVariables(frameHandle);
117+
} else if (ref && ref.type === 'frame') {
118+
// List variables for current frame
119+
response.body.variables = await this.handleVariableRequestFrame(ref);
120+
} else if (ref && ref.varobjName === '__pins') {
121+
response.body.variables = await this.variableHandler.handlePinStatusRequest();
122+
} else if (ref && ref.type === 'object') {
123+
// List data under any variable
124+
response.body.variables = await this.handleVariableRequestObject(ref);
125+
}
126+
this.sendResponse(response);
127+
} catch (err) {
128+
this.sendErrorResponse(response, 1, err.message);
129+
}
130+
}
131+
65132
}

arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as path from 'path';
22
import * as fs from 'arduino-ide-extension/lib/node/fs-extra'
33
import { spawn } from 'child_process';
44
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
5+
import { MIFrameInfo } from 'cdt-gdb-adapter/dist/mi';
56
import { ArduinoLaunchRequestArguments } from './arduino-debug-session';
67

78
export class ArduinoGDBBackend extends GDBBackend {
@@ -33,9 +34,17 @@ export class ArduinoGDBBackend extends GDBBackend {
3334
return Promise.resolve();
3435
}
3536

36-
pause(): boolean {
37-
this.sendCommand('-exec-interrupt');
38-
return true;
37+
sendExecInterrupt(threadId?: number) {
38+
let command = '-exec-interrupt';
39+
if (threadId) {
40+
command += ` --thread ${threadId}`;
41+
}
42+
return this.sendCommand(command);
43+
}
44+
45+
sendStackInfoFrame(gdb: GDBBackend, threadId: number, frameId: number): Promise<{ frame: MIFrameInfo }> {
46+
const command = `-stack-info-frame --thread ${threadId} --frame ${frameId}`;
47+
return gdb.sendCommand(command);
3948
}
4049

4150
sendTargetDetach(): Promise<void> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import * as path from 'path';
2+
import { DebugProtocol } from "vscode-debugprotocol";
3+
import { Handles } from 'vscode-debugadapter/lib/handles';
4+
import { FrameReference, VariableReference } from "cdt-gdb-adapter/dist/GDBDebugSession";
5+
import { VarManager } from 'cdt-gdb-adapter/dist/varManager';
6+
import * as mi from 'cdt-gdb-adapter/dist/mi';
7+
import { ArduinoDebugSession } from "./arduino-debug-session";
8+
import { ArduinoGDBBackend } from './arduino-gdb-backend';
9+
10+
export class ArduinoVariableHandler {
11+
12+
protected readonly gdb: ArduinoGDBBackend;
13+
protected readonly varMgr: VarManager;
14+
15+
protected globalHandle: number;
16+
17+
constructor(protected readonly session: ArduinoDebugSession,
18+
protected frameHandles: Handles<FrameReference>,
19+
protected variableHandles: Handles<VariableReference>) {
20+
this.gdb = session.arduinoBackend;
21+
this.varMgr = new VarManager(this.gdb);
22+
}
23+
24+
createGlobalHandle() {
25+
this.globalHandle = this.frameHandles.create({
26+
threadId: -1,
27+
frameId: -1
28+
});
29+
}
30+
31+
/** TODO */
32+
async getGlobalVariables(): Promise<DebugProtocol.Variable[]> {
33+
throw new Error('Global variables are not supported yet.');
34+
const frame = this.frameHandles.get(this.globalHandle);
35+
const symbolInfo: any[] = [] // this.symbolTable.getGlobalVariables();
36+
const variables: DebugProtocol.Variable[] = [];
37+
38+
for (const symbol of symbolInfo) {
39+
const name = `global_var_${symbol.name}`;
40+
const variable = await this.getVariables(frame, name, symbol.name, -1);
41+
variables.push(variable);
42+
}
43+
44+
return variables;
45+
}
46+
47+
private async getVariables(frame: FrameReference, name: string, expression: string, depth: number): Promise<DebugProtocol.Variable> {
48+
let global = this.varMgr.getVar(frame.frameId, frame.threadId, depth, name);
49+
50+
if (global) {
51+
// Update value if it is already loaded
52+
const vup = await mi.sendVarUpdate(this.gdb, { name });
53+
const update = vup.changelist[0];
54+
if (update && update.in_scope === 'true' && update.name === global.varname) {
55+
global.value = update.value;
56+
}
57+
} else {
58+
// create var in GDB and store it in the varMgr
59+
const varCreateResponse = await mi.sendVarCreate(this.gdb, {
60+
name,
61+
frame: 'current',
62+
expression,
63+
});
64+
65+
global = this.varMgr.addVar(frame.frameId, frame.threadId, depth, name, true, false, varCreateResponse);
66+
}
67+
68+
return {
69+
name: expression,
70+
value: (global.value === void 0) ? '<unknown>' : global.value,
71+
type: global.type,
72+
variablesReference: parseInt(global.numchild, 10) > 0
73+
? this.variableHandles.create({
74+
frameHandle: this.globalHandle,
75+
type: 'object',
76+
varobjName: global.varname,
77+
})
78+
: 0,
79+
};
80+
}
81+
82+
/** TODO */
83+
async getStaticVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
84+
throw new Error('Static variables are not supported yet.');
85+
const frame = this.frameHandles.get(frameHandle);
86+
const result = await this.gdb.sendStackInfoFrame(this.gdb, frame.threadId, frame.frameId);
87+
const file = path.normalize(result.frame.file || '');
88+
const symbolInfo: any[] = [] // this.symbolTable.getStaticVariables(file);
89+
const variables: DebugProtocol.Variable[] = [];
90+
91+
// Fetch stack depth to obtain frameId/threadId/depth tuple
92+
const stackDepth = await mi.sendStackInfoDepth(this.gdb, { maxDepth: 100 });
93+
const depth = parseInt(stackDepth.depth, 10);
94+
95+
for (const symbol of symbolInfo) {
96+
const name = `${file}_static_var_${symbol.name}`;
97+
const variable = await this.getVariables(frame, name, symbol.name, depth);
98+
variables.push(variable);
99+
}
100+
101+
return variables;
102+
}
103+
104+
async handlePinStatusRequest(): Promise<DebugProtocol.Variable[]> {
105+
const variables: DebugProtocol.Variable[] = [];
106+
variables.push({
107+
name: "D2",
108+
type: "gpio",
109+
value: "0x00",
110+
variablesReference: 0
111+
})
112+
return variables;
113+
}
114+
115+
}

arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
461461
});
462462
registry.registerMenuAction(ArduinoMenus.SKETCH, {
463463
commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id,
464-
label: 'Optimize for Debug',
464+
label: 'Optimize for Debugging',
465465
order: '2'
466466
});
467467
registry.registerMenuAction(ArduinoMenus.SKETCH, {

0 commit comments

Comments
 (0)