mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-17 22:29:27 +00:00
Automated debug config setup
This commit is contained in:
committed by
Miro Spönemann
parent
ea5f528ef0
commit
8aa356bd6e
@@ -0,0 +1,100 @@
|
||||
|
||||
import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { BoardsServiceClientImpl } from 'arduino-ide-extension/lib/browser/boards/boards-service-client-impl';
|
||||
import { BoardsService, ToolLocations } from 'arduino-ide-extension/lib/common/protocol/boards-service';
|
||||
import { WorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoVariableResolver implements VariableContribution {
|
||||
|
||||
@inject(BoardsServiceClientImpl)
|
||||
protected readonly boardsServiceClient: BoardsServiceClientImpl;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(WorkspaceVariableContribution)
|
||||
protected readonly workspaceVars: WorkspaceVariableContribution;
|
||||
|
||||
registerVariables(variables: VariableRegistry): void {
|
||||
variables.registerVariable(<Variable>{
|
||||
name: `boardTools`,
|
||||
description: "Provides paths and access to board specific tooling",
|
||||
resolve: this.resolveBoardTools.bind(this),
|
||||
});
|
||||
variables.registerVariable(<Variable>{
|
||||
name: "board",
|
||||
description: "Provides details about the currently selected board",
|
||||
resolve: this.resolveBoard.bind(this),
|
||||
});
|
||||
|
||||
variables.registerVariable({
|
||||
name: "sketchBinary",
|
||||
description: "Path to the sketch's binary file",
|
||||
resolve: this.resolveSketchBinary.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: this function is a total hack. Instead of botching around with URI's it should ask something on the backend
|
||||
// that properly udnerstands the filesystem.
|
||||
protected async resolveSketchBinary(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
|
||||
let sketchPath = argument || this.workspaceVars.getResourceUri()!.path.toString();
|
||||
return sketchPath.substring(0, sketchPath.length - 3) + "arduino.samd.arduino_zero_edbg.elf";
|
||||
}
|
||||
|
||||
protected async resolveBoard(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
||||
throw new Error('No boards selected. Please select a board.');
|
||||
}
|
||||
|
||||
if (!argument || argument === "fqbn") {
|
||||
return boardsConfig.selectedBoard.fqbn!;
|
||||
}
|
||||
if (argument === "name") {
|
||||
return boardsConfig.selectedBoard.name;
|
||||
}
|
||||
|
||||
const details = await this.boardsService.detail({id: boardsConfig.selectedBoard.fqbn!});
|
||||
if (!details.item) {
|
||||
throw new Error("Cannot get board details");
|
||||
}
|
||||
if (argument === "openocd-debug-file") {
|
||||
return details.item.locations!.debugScript;
|
||||
}
|
||||
|
||||
return boardsConfig.selectedBoard.fqbn!;
|
||||
}
|
||||
|
||||
protected async resolveBoardTools(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
||||
throw new Error('No boards selected. Please select a board.');
|
||||
}
|
||||
const details = await this.boardsService.detail({id: boardsConfig.selectedBoard.fqbn!});
|
||||
if (!details.item) {
|
||||
throw new Error("Cannot get board details")
|
||||
}
|
||||
|
||||
let toolLocations: { [name: string]: ToolLocations } = {};
|
||||
details.item.requiredTools.forEach(t => {
|
||||
toolLocations[t.name] = t.locations!;
|
||||
})
|
||||
|
||||
switch(argument) {
|
||||
case "openocd":
|
||||
return toolLocations["openocd"].main;
|
||||
case "openocd-scripts":
|
||||
return toolLocations["openocd"].scripts;
|
||||
case "objdump":
|
||||
return toolLocations["arm-none-eabi-gcc"].objdump;
|
||||
case "gdb":
|
||||
return toolLocations["arm-none-eabi-gcc"].gdb;
|
||||
}
|
||||
|
||||
return boardsConfig.selectedBoard.name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ContainerModule } from 'inversify';
|
||||
import { VariableContribution } from '@theia/variable-resolver/lib/browser';
|
||||
import { ArduinoVariableResolver } from './arduino-variable-resolver';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ArduinoVariableResolver).toSelf().inSingletonScope();
|
||||
bind(VariableContribution).toService(ArduinoVariableResolver);
|
||||
});
|
||||
@@ -1,12 +1,24 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { DebugAdapterContribution, DebugAdapterExecutable, DebugAdapterSessionFactory } from '@theia/debug/lib/common/debug-model';
|
||||
import { DebugConfiguration } from "@theia/debug/lib/common/debug-configuration";
|
||||
import { MaybePromise } from "@theia/core/lib/common/types";
|
||||
import { IJSONSchema, IJSONSchemaSnippet } from "@theia/core/lib/common/json-schema";
|
||||
import * as path from 'path';
|
||||
import { BoardsService } from 'arduino-ide-extension/lib/common/protocol/boards-service';
|
||||
import { CoreService } from 'arduino-ide-extension/lib/common/protocol/core-service';
|
||||
import { FileSystem } from '@theia/filesystem/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoDebugAdapterContribution implements DebugAdapterContribution {
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(FileSystem)
|
||||
protected readonly fileSystem: FileSystem;
|
||||
|
||||
type = "arduino";
|
||||
|
||||
label = "Arduino";
|
||||
@@ -22,56 +34,22 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
|
||||
"program"
|
||||
],
|
||||
"properties": {
|
||||
"program": {
|
||||
"type": "string",
|
||||
"description": "Path to the program to be launched",
|
||||
"default": "${workspaceFolder}/${command:askProgramPath}"
|
||||
},
|
||||
"sketch": {
|
||||
"type": "string",
|
||||
"description": "Path to the sketch folder",
|
||||
"default": "${workspaceFolder}"
|
||||
},
|
||||
"fbqn": {
|
||||
"type": "string",
|
||||
"description": "Fully qualified board name of the debugging target",
|
||||
"default": "unknown"
|
||||
"description": "path to the sketch root ino file",
|
||||
"default": "${file}",
|
||||
},
|
||||
"runToMain": {
|
||||
"description": "If enabled the debugger will run until the start of the main function.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"gdb": {
|
||||
"fqbn": {
|
||||
"type": "string",
|
||||
"description": "Path to gdb",
|
||||
"default": "arm-none-eabi-gdb"
|
||||
},
|
||||
"gdbArguments": {
|
||||
"description": "Additional arguments to pass to GDB command line",
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"gdbServer": {
|
||||
"type": "string",
|
||||
"description": "Path to gdb server",
|
||||
"default": "pyocd"
|
||||
},
|
||||
"gdbServerArguments": {
|
||||
"description": "Additional arguments to pass to GDB server",
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"objdump": {
|
||||
"type": "string",
|
||||
"description": "Path to objdump executable",
|
||||
"default": "arm-none-eabi-objdump"
|
||||
},
|
||||
"initCommands": {
|
||||
"description": "Extra gdb commands to run after initialisation",
|
||||
"type": "array",
|
||||
"default": []
|
||||
"description": "Fully-qualified board name to debug on",
|
||||
"default": ""
|
||||
},
|
||||
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
"description": "Produce verbose log output",
|
||||
@@ -105,11 +83,71 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
|
||||
}
|
||||
|
||||
provideDebugConfigurations?(workspaceFolderUri?: string): MaybePromise<DebugConfiguration[]> {
|
||||
return [];
|
||||
return [
|
||||
<DebugConfiguration>{
|
||||
name: this.label,
|
||||
type: this.type,
|
||||
request: "launch",
|
||||
|
||||
sketch: "${file}",
|
||||
|
||||
verbose: true,
|
||||
runToMain: true,
|
||||
},
|
||||
<DebugConfiguration>{
|
||||
name: this.label + " (explicit)",
|
||||
type: this.type,
|
||||
request: "launch",
|
||||
|
||||
program: "${sketchBinary}",
|
||||
objdump: "${boardTools:objdump}",
|
||||
gdb: "${boardTools:gdb}",
|
||||
gdbServer: "${boardTools:openocd}",
|
||||
gdbServerArguments: ["-s", "${boardTools:openocd-scripts}", "--file", "${board:openocd-debug-file}"],
|
||||
|
||||
verbose: true,
|
||||
runToMain: true,
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
resolveDebugConfiguration?(config: DebugConfiguration, workspaceFolderUri?: string): MaybePromise<DebugConfiguration> {
|
||||
return config;
|
||||
async resolveDebugConfiguration?(config: DebugConfiguration, workspaceFolderUri?: string): Promise<DebugConfiguration> {
|
||||
// if program is present we expect to have an explicit config here
|
||||
if (!!config.program) {
|
||||
return config;
|
||||
}
|
||||
|
||||
let sketchBinary = "${sketchBinary}"
|
||||
if (config.sketch !== "${file}") {
|
||||
sketchBinary = "${sketchBinary:" + config.sketch + "}";
|
||||
}
|
||||
const res: ActualDebugConfig = {
|
||||
...config,
|
||||
|
||||
objdump: "${boardTools:objdump}",
|
||||
gdb: "${boardTools:gdb}",
|
||||
gdbServer: "${boardTools:openocd}",
|
||||
gdbServerArguments: ["-s", "${boardTools:openocd-scripts}", "--file", "${board:openocd-debug-file}"],
|
||||
program: sketchBinary
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ActualDebugConfig extends DebugConfiguration {
|
||||
// path to the program to be launched
|
||||
program: string
|
||||
// path to gdb
|
||||
gdb: string
|
||||
// additional arguments to pass to GDB command line
|
||||
gdbArguments?: string[]
|
||||
// path to the gdb server
|
||||
gdbServer: string
|
||||
// additional arguments to pass to GDB server
|
||||
gdbServerArguments: string[]
|
||||
// path to objdump executable
|
||||
objdump: string
|
||||
// extra gdb commands to run after initialisation
|
||||
initCommands?: string[]
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
import { normalize } from 'path';
|
||||
import { DebugProtocol } from 'vscode-debugprotocol';
|
||||
import { Logger, logger, InitializedEvent, OutputEvent, Scope, TerminatedEvent } from 'vscode-debugadapter';
|
||||
import { GDBDebugSession, RequestArguments, FrameVariableReference, FrameReference, ObjectVariableReference } from 'cdt-gdb-adapter/dist/GDBDebugSession';
|
||||
import { GDBDebugSession, RequestArguments, FrameVariableReference, FrameReference } from 'cdt-gdb-adapter/dist/GDBDebugSession';
|
||||
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
|
||||
import { CmsisBackend } from './cmsis-backend';
|
||||
// import { PyocdServer } from './pyocd-server';
|
||||
@@ -123,15 +123,15 @@ export class CmsisDebugSession extends GDBDebugSession {
|
||||
type: 'frame',
|
||||
frameHandle: args.frameId,
|
||||
};
|
||||
const pins: ObjectVariableReference = {
|
||||
type: "object",
|
||||
varobjName: "__pins",
|
||||
frameHandle: args.frameId,
|
||||
}
|
||||
// const pins: ObjectVariableReference = {
|
||||
// type: "object",
|
||||
// varobjName: "__pins",
|
||||
// frameHandle: 42000,
|
||||
// }
|
||||
|
||||
response.body = {
|
||||
scopes: [
|
||||
new Scope('Pins', this.variableHandles.create(pins), false),
|
||||
// new Scope('Pins', this.variableHandles.create(pins), false),
|
||||
new Scope('Local', this.variableHandles.create(frame), false),
|
||||
new Scope('Global', GLOBAL_HANDLE_ID, false),
|
||||
new Scope('Static', STATIC_HANDLES_START + parseInt(args.frameId as any, 10), false)
|
||||
@@ -162,6 +162,8 @@ export class CmsisDebugSession extends GDBDebugSession {
|
||||
} else if (ref && ref.type === 'frame') {
|
||||
// List variables for current frame
|
||||
response.body.variables = await this.handleVariableRequestFrame(ref);
|
||||
} else if (ref && ref.varobjName === '__pins') {
|
||||
response.body.variables = await this.handlePinStatusRequest();
|
||||
} else if (ref && ref.type === 'object') {
|
||||
// List data under any variable
|
||||
response.body.variables = await this.handleVariableRequestObject(ref);
|
||||
@@ -300,6 +302,17 @@ export class CmsisDebugSession extends GDBDebugSession {
|
||||
return variables;
|
||||
}
|
||||
|
||||
private async handlePinStatusRequest(): Promise<DebugProtocol.Variable[]> {
|
||||
const variables: DebugProtocol.Variable[] = [];
|
||||
variables.push({
|
||||
name: "D2",
|
||||
type: "gpio",
|
||||
value: "0x00",
|
||||
variablesReference: 0
|
||||
})
|
||||
return variables;
|
||||
}
|
||||
|
||||
private async getStaticVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
|
||||
const frame = this.frameHandles.get(frameHandle);
|
||||
const result = await mi.sendStackInfoFrame(this.gdb, frame.threadId, frame.frameId);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PortScanner } from './port-scanner';
|
||||
import { CmsisRequestArguments } from './cmsis-debug-session';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
|
||||
const LAUNCH_REGEX = /GDB server started/;
|
||||
const ERROR_REGEX = /:ERROR:gdbserver:/;
|
||||
@@ -20,7 +21,7 @@ export class OpenocdServer extends AbstractServer {
|
||||
}
|
||||
sessionConfigFile += `echo "GDB server started"${"\n"}`
|
||||
|
||||
const tmpdir = await fs.mkdtemp("arduino-debugger");
|
||||
const tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), "arduino-debugger"));
|
||||
const sessionCfgPath = path.join(tmpdir, "gdb.cfg");
|
||||
await fs.writeFile(sessionCfgPath, sessionConfigFile);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user