mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-09 21:56:32 +00:00
Removed the arduino-debugger-extension
extension
We use the `cortex-debug` VSX. Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
48c6c53b9b
commit
96f0722d56
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "arduino-debugger-extension",
|
|
||||||
"version": "2.0.0-beta.1",
|
|
||||||
"description": "An extension for debugging Arduino programs",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@theia/debug": "next",
|
|
||||||
"arduino-ide-extension": "2.0.0-beta.1",
|
|
||||||
"cdt-gdb-adapter": "^0.0.14",
|
|
||||||
"vscode-debugadapter": "^1.26.0",
|
|
||||||
"vscode-debugprotocol": "^1.26.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"prepare": "yarn run clean && yarn run build",
|
|
||||||
"clean": "rimraf lib",
|
|
||||||
"lint": "tslint -c ./tslint.json --project ./tsconfig.json",
|
|
||||||
"build": "tsc && yarn lint",
|
|
||||||
"watch": "tsc -w"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"lib",
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"theiaExtensions": [
|
|
||||||
{
|
|
||||||
"backend": "lib/node/arduino-debug-backend-module",
|
|
||||||
"frontend": "lib/browser/arduino-debug-frontend-module"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
import { DebugConfigurationManager } from "@theia/debug/lib/browser/debug-configuration-manager";
|
|
||||||
import { injectable } from "inversify";
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ArduinoDebugConfigurationManager extends DebugConfigurationManager {
|
|
||||||
|
|
||||||
get defaultDebugger(): Promise<string | undefined> {
|
|
||||||
return this.debug.getDebuggersForLanguage('ino').then(debuggers => {
|
|
||||||
if (debuggers.length === 0)
|
|
||||||
return undefined;
|
|
||||||
return debuggers[0].type;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async selectDebugType(): Promise<string | undefined> {
|
|
||||||
const widget = this.editorManager.currentEditor;
|
|
||||||
if (!widget) {
|
|
||||||
return this.defaultDebugger;
|
|
||||||
}
|
|
||||||
const { languageId } = widget.editor.document;
|
|
||||||
const debuggers = await this.debug.getDebuggersForLanguage(languageId);
|
|
||||||
if (debuggers.length === 0) {
|
|
||||||
return this.defaultDebugger;
|
|
||||||
}
|
|
||||||
return this.quickPick.show(debuggers.map(
|
|
||||||
({ label, type }) => ({ label, value: type }),
|
|
||||||
{ placeholder: 'Select Environment' })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async createDefaultConfiguration(): Promise<void> {
|
|
||||||
const { model } = this;
|
|
||||||
if (model) {
|
|
||||||
await this.doCreate(model);
|
|
||||||
await this.updateModels();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
import { injectable, inject } from 'inversify';
|
|
||||||
import { MenuModelRegistry, MessageService, Command, CommandRegistry } from '@theia/core';
|
|
||||||
import { KeybindingRegistry } from '@theia/core/lib/browser';
|
|
||||||
import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
||||||
import { DebugFrontendApplicationContribution, DebugCommands } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
|
|
||||||
import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options";
|
|
||||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
|
||||||
import { FileSystem } from '@theia/filesystem/lib/common';
|
|
||||||
import URI from '@theia/core/lib/common/uri';
|
|
||||||
import { EditorManager } from '@theia/editor/lib/browser';
|
|
||||||
import { EditorMode } from "arduino-ide-extension/lib/browser/editor-mode";
|
|
||||||
import { SketchesService } from 'arduino-ide-extension/lib/common/protocol/sketches-service';
|
|
||||||
import { ArduinoToolbar } from 'arduino-ide-extension/lib/browser/toolbar/arduino-toolbar';
|
|
||||||
import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager';
|
|
||||||
|
|
||||||
export namespace ArduinoDebugCommands {
|
|
||||||
export const START_DEBUG: Command = {
|
|
||||||
id: 'arduino-start-debug',
|
|
||||||
label: 'Start Debugging'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendApplicationContribution {
|
|
||||||
|
|
||||||
@inject(EditorMode)
|
|
||||||
protected readonly editorMode: EditorMode;
|
|
||||||
|
|
||||||
@inject(WorkspaceService)
|
|
||||||
protected readonly workspaceService: WorkspaceService;
|
|
||||||
|
|
||||||
@inject(SketchesService)
|
|
||||||
protected readonly sketchesService: SketchesService;
|
|
||||||
|
|
||||||
@inject(FileSystem)
|
|
||||||
protected readonly fileSystem: FileSystem;
|
|
||||||
|
|
||||||
@inject(EditorManager)
|
|
||||||
protected readonly editorManager: EditorManager;
|
|
||||||
|
|
||||||
@inject(MessageService)
|
|
||||||
protected readonly messageService: MessageService;
|
|
||||||
|
|
||||||
async start(noDebug?: boolean, debugSessionOptions?: DebugSessionOptions): Promise<void> {
|
|
||||||
const configurations = this.configurations as ArduinoDebugConfigurationManager;
|
|
||||||
let current = debugSessionOptions ? debugSessionOptions : configurations.current;
|
|
||||||
// If no configurations are currently present, create them
|
|
||||||
if (!current) {
|
|
||||||
await configurations.createDefaultConfiguration();
|
|
||||||
current = configurations.current;
|
|
||||||
}
|
|
||||||
if (current) {
|
|
||||||
if (noDebug !== undefined) {
|
|
||||||
current = {
|
|
||||||
...current,
|
|
||||||
configuration: {
|
|
||||||
...current.configuration,
|
|
||||||
noDebug
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (current.configuration.type === 'arduino') {
|
|
||||||
const wsStat = this.workspaceService.workspace;
|
|
||||||
let sketchFileURI: URI | undefined;
|
|
||||||
if (wsStat && await this.sketchesService.isSketchFolder(wsStat.resource.toString())) {
|
|
||||||
const wsPath = wsStat.resource.path;
|
|
||||||
const sketchFilePath = wsPath.join(wsPath.name + '.ino').toString();
|
|
||||||
sketchFileURI = new URI(sketchFilePath);
|
|
||||||
} else if (this.editorManager.currentEditor) {
|
|
||||||
const editorURI = this.editorManager.currentEditor.getResourceUri();
|
|
||||||
if (editorURI && editorURI.path && editorURI.path.ext === '.ino') {
|
|
||||||
sketchFileURI = editorURI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sketchFileURI) {
|
|
||||||
await this.editorManager.open(sketchFileURI);
|
|
||||||
await this.manager.start(current);
|
|
||||||
} else {
|
|
||||||
this.messageService.error('Please open a sketch file to start debugging.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.manager.start(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeLayout(): Promise<void> {
|
|
||||||
if (this.editorMode.proMode) {
|
|
||||||
return super.initializeLayout();
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
registerMenus(menus: MenuModelRegistry): void {
|
|
||||||
if (this.editorMode.proMode) {
|
|
||||||
super.registerMenus(menus);
|
|
||||||
menus.unregisterMenuAction(DebugCommands.START_NO_DEBUG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerKeybindings(keybindings: KeybindingRegistry): void {
|
|
||||||
if (this.editorMode.proMode) {
|
|
||||||
super.registerKeybindings(keybindings);
|
|
||||||
keybindings.unregisterKeybinding({
|
|
||||||
command: DebugCommands.START_NO_DEBUG.id,
|
|
||||||
keybinding: 'ctrl+f5'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
|
|
||||||
super.registerToolbarItems(toolbar);
|
|
||||||
toolbar.registerItem({
|
|
||||||
id: ArduinoDebugCommands.START_DEBUG.id,
|
|
||||||
command: ArduinoDebugCommands.START_DEBUG.id,
|
|
||||||
tooltip: 'Start Debugging',
|
|
||||||
priority: 3
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
registerCommands(registry: CommandRegistry): void {
|
|
||||||
super.registerCommands(registry);
|
|
||||||
registry.registerCommand(ArduinoDebugCommands.START_DEBUG, {
|
|
||||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
|
||||||
isEnabled: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
|
||||||
execute: () => {
|
|
||||||
registry.executeCommand(DebugCommands.START.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { ContainerModule } from 'inversify';
|
|
||||||
import { VariableContribution } from '@theia/variable-resolver/lib/browser';
|
|
||||||
import { ArduinoVariableResolver } from './arduino-variable-resolver';
|
|
||||||
import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
|
||||||
import { DebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
|
|
||||||
import { DebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
|
|
||||||
import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager';
|
|
||||||
import { ArduinoDebugFrontendApplicationContribution } from './arduino-debug-frontend-application-contribution';
|
|
||||||
import { ArduinoDebugSessionManager } from './arduino-debug-session-manager';
|
|
||||||
|
|
||||||
import '../../src/browser/style/index.css';
|
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
||||||
bind(ArduinoVariableResolver).toSelf().inSingletonScope();
|
|
||||||
bind(VariableContribution).toService(ArduinoVariableResolver);
|
|
||||||
rebind(DebugSessionManager).to(ArduinoDebugSessionManager).inSingletonScope();
|
|
||||||
rebind(DebugConfigurationManager).to(ArduinoDebugConfigurationManager).inSingletonScope();
|
|
||||||
rebind(DebugFrontendApplicationContribution).to(ArduinoDebugFrontendApplicationContribution);
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
import { DebugSessionManager } from "@theia/debug/lib/browser/debug-session-manager";
|
|
||||||
import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options";
|
|
||||||
|
|
||||||
export class ArduinoDebugSessionManager extends DebugSessionManager {
|
|
||||||
|
|
||||||
start(options: DebugSessionOptions) {
|
|
||||||
if (options.configuration.type === 'arduino' && this.sessions.find(s => s.configuration.type === 'arduino')) {
|
|
||||||
this.messageService.info('A debug session is already running. You must stop the running session before starting a new one.')
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
return super.start(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser';
|
|
||||||
import { injectable, inject } from 'inversify';
|
|
||||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
|
||||||
import { BoardsServiceProvider } from 'arduino-ide-extension/lib/browser/boards/boards-service-provider';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ArduinoVariableResolver implements VariableContribution {
|
|
||||||
|
|
||||||
@inject(BoardsServiceProvider)
|
|
||||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
|
||||||
|
|
||||||
@inject(MessageService)
|
|
||||||
protected readonly messageService: MessageService
|
|
||||||
|
|
||||||
registerVariables(variables: VariableRegistry): void {
|
|
||||||
variables.registerVariable(<Variable>{
|
|
||||||
name: 'fqbn',
|
|
||||||
description: 'Qualified name of the selected board',
|
|
||||||
resolve: this.resolveFqbn.bind(this),
|
|
||||||
});
|
|
||||||
variables.registerVariable({
|
|
||||||
name: 'port',
|
|
||||||
description: 'Selected upload port',
|
|
||||||
resolve: this.resolvePort.bind(this)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async resolveFqbn(): Promise<string | undefined> {
|
|
||||||
const { boardsConfig } = this.boardsServiceProvider;
|
|
||||||
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
|
||||||
this.messageService.error('No board selected. Please select a board for debugging.');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return boardsConfig.selectedBoard.fqbn;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async resolvePort(): Promise<string | undefined> {
|
|
||||||
const { boardsConfig } = this.boardsServiceProvider;
|
|
||||||
if (!boardsConfig || !boardsConfig.selectedPort) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return boardsConfig.selectedPort.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
.arduino-start-debug-icon {
|
|
||||||
-webkit-mask: url('debug-dark.svg') 50%;
|
|
||||||
mask: url('debug-dark.svg') 50%;
|
|
||||||
-webkit-mask-size: 100%;
|
|
||||||
mask-size: 100%;
|
|
||||||
-webkit-mask-repeat: no-repeat;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--theia-ui-button-font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.arduino-start-debug {
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import { injectable, inject } from 'inversify';
|
|
||||||
import { DebugAdapterContribution, DebugAdapterExecutable } from '@theia/debug/lib/common/debug-model';
|
|
||||||
import { DebugConfiguration } from '@theia/debug/lib/common/debug-configuration';
|
|
||||||
import { IJSONSchema } from '@theia/core/lib/common/json-schema';
|
|
||||||
import { ArduinoDaemonImpl } from 'arduino-ide-extension/lib/node/arduino-daemon-impl';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ArduinoDebugAdapterContribution implements DebugAdapterContribution {
|
|
||||||
|
|
||||||
readonly type = 'arduino';
|
|
||||||
readonly label = 'Arduino';
|
|
||||||
readonly languages = ['c', 'cpp', 'ino'];
|
|
||||||
|
|
||||||
@inject(ArduinoDaemonImpl) daemon: ArduinoDaemonImpl;
|
|
||||||
|
|
||||||
getSchemaAttributes(): IJSONSchema[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
'properties': {
|
|
||||||
'sketch': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'path to the sketch root ino file',
|
|
||||||
'default': '${file}',
|
|
||||||
},
|
|
||||||
'pauseAtMain': {
|
|
||||||
'description': 'If enabled the debugger will pause at the start of the main function.',
|
|
||||||
'type': 'boolean',
|
|
||||||
'default': false
|
|
||||||
},
|
|
||||||
'debugDebugAdapter': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Start the debug adapter in debug mode (with --inspect-brk)',
|
|
||||||
'default': false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
provideDebugAdapterExecutable(config: DebugConfiguration): DebugAdapterExecutable {
|
|
||||||
const debugAdapterMain = path.join(__dirname, 'debug-adapter', 'main');
|
|
||||||
if (config.debugDebugAdapter) {
|
|
||||||
return {
|
|
||||||
command: process.execPath,
|
|
||||||
args: ['--inspect-brk', debugAdapterMain],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
modulePath: debugAdapterMain,
|
|
||||||
args: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
provideDebugConfigurations(): DebugConfiguration[] {
|
|
||||||
return [
|
|
||||||
<DebugConfiguration>{
|
|
||||||
name: this.label,
|
|
||||||
type: this.type,
|
|
||||||
request: 'launch'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
async resolveDebugConfiguration(config: DebugConfiguration): Promise<DebugConfiguration> {
|
|
||||||
const startFunction = config.pauseAtMain ? 'main' : 'setup';
|
|
||||||
const res: ActualDebugConfig = {
|
|
||||||
...config,
|
|
||||||
arduinoCli: await this.daemon.getExecPath(),
|
|
||||||
fqbn: '${fqbn}',
|
|
||||||
uploadPort: '${port}',
|
|
||||||
initCommands: [
|
|
||||||
`-break-insert -t --function ${startFunction}`
|
|
||||||
]
|
|
||||||
}
|
|
||||||
if (!res.sketch) {
|
|
||||||
res.sketch = '${file}';
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ActualDebugConfig extends DebugConfiguration {
|
|
||||||
arduinoCli?: string;
|
|
||||||
sketch?: string;
|
|
||||||
fqbn?: string;
|
|
||||||
uploadPort?: string;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { ContainerModule } from 'inversify';
|
|
||||||
import { DebugAdapterContribution } from '@theia/debug/lib/common/debug-model';
|
|
||||||
import { ArduinoDebugAdapterContribution } from './arduino-debug-adapter-contribution';
|
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
||||||
bind(DebugAdapterContribution).to(ArduinoDebugAdapterContribution).inSingletonScope();
|
|
||||||
});
|
|
@ -1,140 +0,0 @@
|
|||||||
import { DebugProtocol } from 'vscode-debugprotocol';
|
|
||||||
import { GDBDebugSession, FrameVariableReference } from 'cdt-gdb-adapter/dist/GDBDebugSession';
|
|
||||||
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
|
|
||||||
import * as mi from 'cdt-gdb-adapter/dist/mi';
|
|
||||||
import { ArduinoGDBBackend } from './arduino-gdb-backend';
|
|
||||||
import { ArduinoVariableHandler } from './arduino-variable-handler';
|
|
||||||
import { Scope, OutputEvent } from 'vscode-debugadapter';
|
|
||||||
|
|
||||||
export interface ArduinoLaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
|
|
||||||
arduinoCli?: string;
|
|
||||||
sketch?: string;
|
|
||||||
fqbn?: string;
|
|
||||||
uploadPort?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLOBAL_HANDLE_ID = 0xFE;
|
|
||||||
const STATIC_HANDLES_START = 0x010000;
|
|
||||||
const STATIC_HANDLES_FINISH = 0x01FFFF;
|
|
||||||
|
|
||||||
export class ArduinoDebugSession extends GDBDebugSession {
|
|
||||||
|
|
||||||
private _variableHandler: ArduinoVariableHandler;
|
|
||||||
|
|
||||||
get arduinoBackend(): ArduinoGDBBackend {
|
|
||||||
return this.gdb as ArduinoGDBBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get variableHandler() {
|
|
||||||
if (this._variableHandler) {
|
|
||||||
return this._variableHandler;
|
|
||||||
}
|
|
||||||
if (!this.gdb) {
|
|
||||||
throw new Error("GDB backend is not ready.");
|
|
||||||
}
|
|
||||||
const handler = new ArduinoVariableHandler(this, this.frameHandles, this.variableHandles);
|
|
||||||
this._variableHandler = handler;
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createBackend(): GDBBackend {
|
|
||||||
return new ArduinoGDBBackend();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise<void> {
|
|
||||||
try {
|
|
||||||
await this.gdb.sendCommand('-interpreter-exec console "monitor reset halt"')
|
|
||||||
await mi.sendExecContinue(this.gdb);
|
|
||||||
this.sendResponse(response);
|
|
||||||
} catch (err) {
|
|
||||||
this.sendErrorResponse(response, 100, err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments): Promise<void> {
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
const message = 'Pause is not supported on Windows. Please stop the debug session and set a breakpoint instead.';
|
|
||||||
this.sendEvent(new OutputEvent(message));
|
|
||||||
this.sendErrorResponse(response, 1, message);
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return super.pauseRequest(response, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async disconnectRequest(response: DebugProtocol.DisconnectResponse): Promise<void> {
|
|
||||||
try {
|
|
||||||
if (this.isRunning) {
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
// We cannot pause on Windows
|
|
||||||
this.arduinoBackend.kill();
|
|
||||||
this.sendResponse(response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Need to pause first
|
|
||||||
const waitPromise = new Promise(resolve => this.waitPaused = resolve);
|
|
||||||
this.gdb.pause();
|
|
||||||
await waitPromise;
|
|
||||||
}
|
|
||||||
await this.gdb.sendGDBExit();
|
|
||||||
this.sendResponse(response);
|
|
||||||
} catch (err) {
|
|
||||||
this.sendErrorResponse(response, 1, err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
|
|
||||||
try {
|
|
||||||
const frame: FrameVariableReference = {
|
|
||||||
type: 'frame',
|
|
||||||
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('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)
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.sendResponse(response);
|
|
||||||
} catch (err) {
|
|
||||||
this.sendErrorResponse(response, 1, err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise<void> {
|
|
||||||
try {
|
|
||||||
response.body = {
|
|
||||||
variables: [] as DebugProtocol.Variable[]
|
|
||||||
};
|
|
||||||
const ref = this.variableHandles.get(args.variablesReference);
|
|
||||||
if (args.variablesReference === GLOBAL_HANDLE_ID) {
|
|
||||||
// Use hardcoded global handle to load and store global variables
|
|
||||||
response.body.variables = await this.variableHandler.getGlobalVariables();
|
|
||||||
} else if (args.variablesReference >= STATIC_HANDLES_START && args.variablesReference <= STATIC_HANDLES_FINISH) {
|
|
||||||
// Use STATIC_HANDLES_START to shift the framehandles back
|
|
||||||
const frameHandle = args.variablesReference - STATIC_HANDLES_START;
|
|
||||||
response.body.variables = await this.variableHandler.getStaticVariables(frameHandle);
|
|
||||||
} 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.variableHandler.handlePinStatusRequest();
|
|
||||||
} else if (ref && ref.type === 'object') {
|
|
||||||
// List data under any variable
|
|
||||||
response.body.variables = await this.handleVariableRequestObject(ref);
|
|
||||||
}
|
|
||||||
this.sendResponse(response);
|
|
||||||
} catch (err) {
|
|
||||||
this.sendErrorResponse(response, 1, err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as fs from 'arduino-ide-extension/lib/node/fs-extra'
|
|
||||||
import { spawn } from 'child_process';
|
|
||||||
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
|
|
||||||
import { MIFrameInfo } from 'cdt-gdb-adapter/dist/mi';
|
|
||||||
import { ArduinoLaunchRequestArguments } from './arduino-debug-session';
|
|
||||||
import { ArduinoParser } from './arduino-parser';
|
|
||||||
|
|
||||||
export class ArduinoGDBBackend extends GDBBackend {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.parser = new ArduinoParser(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn(requestArgs: ArduinoLaunchRequestArguments): Promise<void> {
|
|
||||||
if (!requestArgs.sketch) {
|
|
||||||
throw new Error('Missing argument: sketch');
|
|
||||||
}
|
|
||||||
if (!requestArgs.fqbn) {
|
|
||||||
throw new Error('Missing argument: fqbn')
|
|
||||||
}
|
|
||||||
const sketchFS = fs.statSync(requestArgs.sketch);
|
|
||||||
const sketchDir = sketchFS.isFile() ? path.dirname(requestArgs.sketch) : requestArgs.sketch;
|
|
||||||
const command = requestArgs.arduinoCli || 'arduino-cli';
|
|
||||||
const args = [
|
|
||||||
'debug',
|
|
||||||
'-p', requestArgs.uploadPort || 'none',
|
|
||||||
'-b', requestArgs.fqbn,
|
|
||||||
'--interpreter', 'mi2',
|
|
||||||
sketchDir
|
|
||||||
];
|
|
||||||
console.log('Starting debugger:', command, JSON.stringify(args));
|
|
||||||
const proc = spawn(command, args);
|
|
||||||
this.proc = proc;
|
|
||||||
this.out = proc.stdin;
|
|
||||||
return (this.parser as ArduinoParser).parseFull(proc);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendFileExecAndSymbols(): Promise<void> {
|
|
||||||
// The program file is already sent by `arduino-cli`
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
sendExecInterrupt(threadId?: number) {
|
|
||||||
let command = '-exec-interrupt';
|
|
||||||
if (threadId) {
|
|
||||||
command += ` --thread ${threadId}`;
|
|
||||||
}
|
|
||||||
return this.sendCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendStackInfoFrame(threadId: number, frameId: number): Promise<{ frame: MIFrameInfo }> {
|
|
||||||
const command = `-stack-info-frame --thread ${threadId} --frame ${frameId}`;
|
|
||||||
return this.sendCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendTargetDetach(): Promise<void> {
|
|
||||||
return this.sendCommand('-target-detach');
|
|
||||||
}
|
|
||||||
|
|
||||||
kill(): void {
|
|
||||||
if (!this.proc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
spawn('taskkill', ['/pid', this.proc.pid.toString(), '/f', '/t']);
|
|
||||||
} else {
|
|
||||||
this.proc.kill('SIGKILL');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
import { ChildProcessWithoutNullStreams } from 'child_process';
|
|
||||||
import { Readable } from 'stream';
|
|
||||||
import { MIParser } from "cdt-gdb-adapter/dist/MIParser";
|
|
||||||
|
|
||||||
const LINE_REGEX = /(.*)(\r?\n)/;
|
|
||||||
|
|
||||||
export class ArduinoParser extends MIParser {
|
|
||||||
|
|
||||||
protected rejectReady?: (error: Error) => void;
|
|
||||||
|
|
||||||
parseFull(proc: ChildProcessWithoutNullStreams): Promise<void> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Detect errors when the child process could not be spawned
|
|
||||||
proc.on('error', reject);
|
|
||||||
|
|
||||||
this.waitReady = () => {
|
|
||||||
this.rejectReady = undefined;
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
this.rejectReady = (error: Error) => {
|
|
||||||
this.waitReady = undefined;
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
// Detect unexpected termination
|
|
||||||
proc.on('exit', () => {
|
|
||||||
if (this.rejectReady) {
|
|
||||||
this.rejectReady(new Error('The gdb debugger terminated unexpectedly.'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.readInputStream(proc.stdout);
|
|
||||||
this.readErrorStream(proc.stderr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private readInputStream(stream: Readable) {
|
|
||||||
let buff = '';
|
|
||||||
stream.on('data', chunk => {
|
|
||||||
buff += chunk.toString();
|
|
||||||
let regexArray = LINE_REGEX.exec(buff);
|
|
||||||
while (regexArray) {
|
|
||||||
const line = regexArray[1];
|
|
||||||
this.line = line;
|
|
||||||
this.pos = 0;
|
|
||||||
this.handleLine();
|
|
||||||
// Detect error emitted as log message
|
|
||||||
if (this.rejectReady && line.toLowerCase().startsWith('&"error')) {
|
|
||||||
this.pos = 1;
|
|
||||||
this.rejectReady(new Error(this.handleCString() || regexArray[1]));
|
|
||||||
this.rejectReady = undefined;
|
|
||||||
}
|
|
||||||
buff = buff.substring(regexArray[1].length + regexArray[2].length);
|
|
||||||
regexArray = LINE_REGEX.exec(buff);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private readErrorStream(stream: Readable) {
|
|
||||||
let buff = '';
|
|
||||||
stream.on('data', chunk => {
|
|
||||||
buff += chunk.toString();
|
|
||||||
let regexArray = LINE_REGEX.exec(buff);
|
|
||||||
while (regexArray) {
|
|
||||||
const line = regexArray[1];
|
|
||||||
this.gdb.emit('consoleStreamOutput', line + '\n', 'stderr');
|
|
||||||
// Detect error emitted on the stderr stream
|
|
||||||
if (this.rejectReady && line.toLowerCase().startsWith('error')) {
|
|
||||||
this.rejectReady(new Error(line));
|
|
||||||
this.rejectReady = undefined;
|
|
||||||
}
|
|
||||||
buff = buff.substring(regexArray[1].length + regexArray[2].length);
|
|
||||||
regexArray = LINE_REGEX.exec(buff);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import { DebugProtocol } from "vscode-debugprotocol";
|
|
||||||
import { Handles } from 'vscode-debugadapter/lib/handles';
|
|
||||||
import { FrameReference, VariableReference } from "cdt-gdb-adapter/dist/GDBDebugSession";
|
|
||||||
import { VarManager } from 'cdt-gdb-adapter/dist/varManager';
|
|
||||||
import * as mi from 'cdt-gdb-adapter/dist/mi';
|
|
||||||
import { ArduinoDebugSession } from "./arduino-debug-session";
|
|
||||||
import { ArduinoGDBBackend } from './arduino-gdb-backend';
|
|
||||||
|
|
||||||
export class ArduinoVariableHandler {
|
|
||||||
|
|
||||||
protected readonly gdb: ArduinoGDBBackend;
|
|
||||||
protected readonly varMgr: VarManager;
|
|
||||||
|
|
||||||
protected globalHandle: number;
|
|
||||||
|
|
||||||
constructor(protected readonly session: ArduinoDebugSession,
|
|
||||||
protected frameHandles: Handles<FrameReference>,
|
|
||||||
protected variableHandles: Handles<VariableReference>) {
|
|
||||||
this.gdb = session.arduinoBackend;
|
|
||||||
this.varMgr = new VarManager(this.gdb);
|
|
||||||
}
|
|
||||||
|
|
||||||
createGlobalHandle() {
|
|
||||||
this.globalHandle = this.frameHandles.create({
|
|
||||||
threadId: -1,
|
|
||||||
frameId: -1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** TODO */
|
|
||||||
async getGlobalVariables(): Promise<DebugProtocol.Variable[]> {
|
|
||||||
throw new Error('Global variables are not supported yet.');
|
|
||||||
const frame = this.frameHandles.get(this.globalHandle);
|
|
||||||
const symbolInfo: any[] = [] // this.symbolTable.getGlobalVariables();
|
|
||||||
const variables: DebugProtocol.Variable[] = [];
|
|
||||||
|
|
||||||
for (const symbol of symbolInfo) {
|
|
||||||
const name = `global_var_${symbol.name}`;
|
|
||||||
const variable = await this.getVariables(frame, name, symbol.name, -1);
|
|
||||||
variables.push(variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** TODO */
|
|
||||||
async getStaticVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
|
|
||||||
throw new Error('Static variables are not supported yet.');
|
|
||||||
const frame = this.frameHandles.get(frameHandle);
|
|
||||||
const result = await this.gdb.sendStackInfoFrame(frame.threadId, frame.frameId);
|
|
||||||
const file = path.normalize(result.frame.file || '');
|
|
||||||
const symbolInfo: any[] = [] // this.symbolTable.getStaticVariables(file);
|
|
||||||
const variables: DebugProtocol.Variable[] = [];
|
|
||||||
|
|
||||||
// Fetch stack depth to obtain frameId/threadId/depth tuple
|
|
||||||
const stackDepth = await mi.sendStackInfoDepth(this.gdb, { maxDepth: 100 });
|
|
||||||
const depth = parseInt(stackDepth.depth, 10);
|
|
||||||
|
|
||||||
for (const symbol of symbolInfo) {
|
|
||||||
const name = `${file}_static_var_${symbol.name}`;
|
|
||||||
const variable = await this.getVariables(frame, name, symbol.name, depth);
|
|
||||||
variables.push(variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getVariables(frame: FrameReference, name: string, expression: string, depth: number): Promise<DebugProtocol.Variable> {
|
|
||||||
let global = this.varMgr.getVar(frame.frameId, frame.threadId, depth, name);
|
|
||||||
|
|
||||||
if (global) {
|
|
||||||
// Update value if it is already loaded
|
|
||||||
const vup = await mi.sendVarUpdate(this.gdb, { name });
|
|
||||||
const update = vup.changelist[0];
|
|
||||||
if (update && update.in_scope === 'true' && update.name === global.varname) {
|
|
||||||
global.value = update.value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// create var in GDB and store it in the varMgr
|
|
||||||
const varCreateResponse = await mi.sendVarCreate(this.gdb, {
|
|
||||||
name,
|
|
||||||
frame: 'current',
|
|
||||||
expression,
|
|
||||||
});
|
|
||||||
|
|
||||||
global = this.varMgr.addVar(frame.frameId, frame.threadId, depth, name, true, false, varCreateResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: expression,
|
|
||||||
value: (global.value === void 0) ? '<unknown>' : global.value,
|
|
||||||
type: global.type,
|
|
||||||
variablesReference: parseInt(global.numchild, 10) > 0
|
|
||||||
? this.variableHandles.create({
|
|
||||||
frameHandle: this.globalHandle,
|
|
||||||
type: 'object',
|
|
||||||
varobjName: global.varname,
|
|
||||||
})
|
|
||||||
: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async handlePinStatusRequest(): Promise<DebugProtocol.Variable[]> {
|
|
||||||
const variables: DebugProtocol.Variable[] = [];
|
|
||||||
variables.push({
|
|
||||||
name: "D2",
|
|
||||||
type: "gpio",
|
|
||||||
value: "0x00",
|
|
||||||
variablesReference: 0
|
|
||||||
})
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import * as process from 'process';
|
|
||||||
import { logger } from 'vscode-debugadapter/lib/logger';
|
|
||||||
import { ArduinoDebugSession } from './arduino-debug-session';
|
|
||||||
import { DebugSession } from 'vscode-debugadapter';
|
|
||||||
|
|
||||||
process.on('uncaughtException', (err: any) => {
|
|
||||||
logger.error(JSON.stringify(err));
|
|
||||||
});
|
|
||||||
|
|
||||||
DebugSession.run(ArduinoDebugSession);
|
|
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"noEmitOnError": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"strictNullChecks": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"downlevelIteration": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es6",
|
|
||||||
"outDir": "lib",
|
|
||||||
"lib": [
|
|
||||||
"es6",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"jsx": "react",
|
|
||||||
"sourceMap": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"../node_modules/@theia/monaco/src/typings/monaco/index.d.ts"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
"class-name": true,
|
|
||||||
"comment-format": [true, "check-space"],
|
|
||||||
"curly": false,
|
|
||||||
"forin": false,
|
|
||||||
"indent": [true, "spaces"],
|
|
||||||
"max-line-length": [true, 180],
|
|
||||||
"no-trailing-whitespace": false,
|
|
||||||
"no-unused-expression": true,
|
|
||||||
"no-var-keyword": true,
|
|
||||||
"one-line": [true,
|
|
||||||
"check-open-brace",
|
|
||||||
"check-catch",
|
|
||||||
"check-else",
|
|
||||||
"check-whitespace"
|
|
||||||
],
|
|
||||||
"radix": true,
|
|
||||||
"trailing-comma": [false],
|
|
||||||
"triple-equals": [true, "allow-null-check"],
|
|
||||||
"typedef-whitespace": [true, {
|
|
||||||
"call-signature": "nospace",
|
|
||||||
"index-signature": "nospace",
|
|
||||||
"parameter": "nospace",
|
|
||||||
"property-declaration": "nospace",
|
|
||||||
"variable-declaration": "nospace"
|
|
||||||
}],
|
|
||||||
"variable-name": false,
|
|
||||||
"whitespace": [true,
|
|
||||||
"check-branch",
|
|
||||||
"check-decl",
|
|
||||||
"check-operator",
|
|
||||||
"check-separator",
|
|
||||||
"check-type"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,8 +28,7 @@ for (const toUpdate of [
|
|||||||
path.join(repoRootPath, 'package.json'),
|
path.join(repoRootPath, 'package.json'),
|
||||||
path.join(repoRootPath, 'electron-app', 'package.json'),
|
path.join(repoRootPath, 'electron-app', 'package.json'),
|
||||||
path.join(repoRootPath, 'browser-app', 'package.json'),
|
path.join(repoRootPath, 'browser-app', 'package.json'),
|
||||||
path.join(repoRootPath, 'arduino-ide-extension', 'package.json'),
|
path.join(repoRootPath, 'arduino-ide-extension', 'package.json')
|
||||||
path.join(repoRootPath, 'arduino-debugger-extension', 'package.json') // Currently unused. The debugger functionality comes from the `cortex.debug` VS Code extension.
|
|
||||||
]) {
|
]) {
|
||||||
process.stdout.write(` Updating ${toUpdate}'...`);
|
process.stdout.write(` Updating ${toUpdate}'...`);
|
||||||
const pkg = require(toUpdate);
|
const pkg = require(toUpdate);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user