Removed unused CMSIS code

This commit is contained in:
Miro Spönemann 2020-02-24 15:05:09 +01:00
parent a72533b208
commit beb529cf15
8 changed files with 0 additions and 1199 deletions

View File

@ -1,164 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2017-2019 Marcel Ball
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { EOL } from 'os';
import { spawn, ChildProcess } from 'child_process';
import { EventEmitter } from 'events';
import { dirname } from 'path';
import { CmsisRequestArguments } from './cmsis-debug-session';
const TIMEOUT = 1000 * 10; // 10 seconds
export abstract class AbstractServer extends EventEmitter {
protected process?: ChildProcess;
protected outBuffer: string = '';
protected errBuffer: string = '';
protected launchResolve?: () => void;
protected launchReject?: (error: any) => void;
protected timer?: NodeJS.Timer;
serverErrorEmitted = false;
get isRunning(): boolean {
return !!this.process && !this.process.killed;
}
spawn(args: CmsisRequestArguments): Promise<void> {
return new Promise(async (resolve, reject) => {
this.launchResolve = resolve;
this.launchReject = reject;
try {
this.timer = setTimeout(() => this.onSpawnError(new Error('Timeout waiting for gdb server to start')), TIMEOUT);
const command = args.gdbServer;
if (!command) {
throw new Error('Missing parameter: gdbServer');
}
const varRegexp = /\$\{.*\}/;
if (varRegexp.test(command)) {
throw new Error(`Unresolved variable: ${command}`)
}
const serverArguments = await this.resolveServerArguments(args);
this.process = spawn(command, serverArguments, {
cwd: dirname(command),
});
if (!this.process) {
throw new Error('Unable to spawn gdb server');
}
this.process.on('exit', this.onExit.bind(this));
this.process.on('error', this.onSpawnError.bind(this));
if (this.process.stdout) {
this.process.stdout.on('data', this.onStdout.bind(this));
}
if (this.process.stderr) {
this.process.stderr.on('data', this.onStderr.bind(this));
}
} catch (error) {
this.onSpawnError(error);
}
});
}
kill() {
if (this.process) {
this.process.kill('SIGINT');
}
}
protected async resolveServerArguments(req: CmsisRequestArguments): Promise<string[]> {
return req.gdbServerArguments || [];
}
protected onExit(code: number, signal: string) {
this.emit('exit', code, signal);
// Code can be undefined, null or 0 and we want to ignore those values
if (!!code && !this.serverErrorEmitted) {
this.emit('error', `GDB server stopped unexpectedly with exit code ${code}`);
}
}
protected onSpawnError(error: Error) {
if (this.launchReject) {
this.clearTimer();
this.launchReject(error);
this.clearPromises();
}
}
protected onStdout(chunk: string | Buffer) {
this.onData(chunk, this.outBuffer, 'stdout');
}
protected onStderr(chunk: string | Buffer) {
this.onData(chunk, this.errBuffer, 'stderr');
}
protected onData(chunk: string | Buffer, buffer: string, event: string) {
buffer += typeof chunk === 'string' ? chunk
: chunk.toString('utf8');
const end = buffer.lastIndexOf('\n');
if (end !== -1) {
const data = buffer.substring(0, end);
this.emit(event, data);
this.handleData(data);
buffer = buffer.substring(end + 1);
}
}
protected handleData(data: string) {
if (this.launchResolve && this.serverStarted(data)) {
this.clearTimer();
this.launchResolve();
this.clearPromises();
}
if (this.serverError(data)) {
this.emit('error', data.split(EOL)[0]);
this.serverErrorEmitted = true;
}
}
protected clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = undefined;
}
}
protected clearPromises() {
this.launchResolve = undefined;
this.launchReject = undefined;
}
protected abstract serverStarted(data: string): boolean;
protected abstract serverError(data: string): boolean;
}

View File

@ -1,39 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
import * as mi from './mi';
export class CmsisBackend extends GDBBackend {
public get isRunning(): boolean {
return !!this.out;
}
public pause() {
mi.sendExecInterrupt(this);
return true;
}
}

View File

@ -1,458 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { normalize } from 'path';
import { DebugProtocol } from 'vscode-debugprotocol';
import { Logger, logger, InitializedEvent, OutputEvent, Scope, TerminatedEvent, ErrorDestination } from 'vscode-debugadapter';
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';
import { PortScanner } from './port-scanner';
import { SymbolTable } from './symbols';
import { VarManager } from 'cdt-gdb-adapter/dist/varManager';
import * as mi from './mi';
import { OpenocdServer } from './openocd-server';
export interface CmsisRequestArguments extends RequestArguments {
runToMain?: boolean;
gdbServer?: string;
gdbServerArguments?: string[];
gdbServerPort?: number;
objdump?: string;
}
const GLOBAL_HANDLE_ID = 0xFE;
const STATIC_HANDLES_START = 0x010000;
const STATIC_HANDLES_FINISH = 0x01FFFF;
export class CmsisDebugSession extends GDBDebugSession {
protected readonly gdbServer = new OpenocdServer();
protected readonly portScanner = new PortScanner();
protected symbolTable!: SymbolTable;
protected globalHandle!: number;
protected varMgr: VarManager;
constructor() {
super();
this.addProcessListeners();
}
protected addProcessListeners(): void {
process.on('exit', () => {
if (this.gdbServer.isRunning) {
this.gdbServer.kill();
}
});
const signalHandler = () => {
if (this.gdbServer.isRunning) {
this.gdbServer.kill();
}
process.exit();
}
process.on('SIGINT', signalHandler);
process.on('SIGTERM', signalHandler);
process.on('SIGHUP', signalHandler);
}
protected createBackend(): GDBBackend {
const gdb = new CmsisBackend();
this.varMgr = new VarManager(gdb);
return gdb;
}
protected async launchRequest(response: DebugProtocol.LaunchResponse, args: CmsisRequestArguments): Promise<void> {
try {
await this.runSession(args);
this.sendResponse(response);
} catch (err) {
const message = `Failed to launch the debugger:\n${err.message}`;
this.sendErrorResponse(response, 1, message);
}
}
protected async attachRequest(response: DebugProtocol.AttachResponse, args: CmsisRequestArguments): Promise<void> {
try {
await this.runSession(args);
this.sendResponse(response);
} catch (err) {
const message = `Failed to attach the debugger:\n${err.message}`;
this.sendErrorResponse(response, 1, message);
}
}
protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise<void> {
try {
await mi.sendExecContinue(this.gdb);
this.sendResponse(response);
} catch (err) {
this.sendErrorResponse(response, 100, err.message);
}
}
protected async pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments): Promise<void> {
try {
await mi.sendExecInterrupt(this.gdb, args.threadId);
this.sendResponse(response);
} catch (err) {
this.sendErrorResponse(response, 1, err.message);
}
}
protected async stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): Promise<void> {
try {
this.globalHandle = this.frameHandles.create({
threadId: -1,
frameId: -1
});
return super.stackTraceRequest(response, args);
} catch (err) {
this.sendErrorResponse(response, 1, err.message);
}
}
protected async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): Promise<void> {
await super.setBreakPointsRequest(response, args);
}
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: new Array<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.getGlobalVariables(this.globalHandle);
} 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.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.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);
}
}
protected async evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): Promise<void> {
try {
if (args.context === 'repl') {
const command = args.expression;
const output = await mi.sendUserInput(this.gdb, command);
if (typeof output === 'undefined') {
response.body = {
result: '',
variablesReference: 0
};
} else {
response.body = {
result: JSON.stringify(output),
variablesReference: 0
};
}
this.sendResponse(response);
} else {
return super.evaluateRequest(response, args);
}
} catch (err) {
this.sendErrorResponse(response, 1, err.message);
}
}
protected async disconnectRequest(response: DebugProtocol.DisconnectResponse, args: DebugProtocol.DisconnectArguments): Promise<void> {
try {
this.stopSession();
this.sendResponse(response);
} catch (err) {
this.sendErrorResponse(response, 1, err.message);
}
}
private async runSession(args: CmsisRequestArguments): Promise<void> {
logger.setup(args.verbose ? Logger.LogLevel.Verbose : Logger.LogLevel.Warn, args.logFile || false);
this.gdb.on('consoleStreamOutput', (output, category) => this.sendEvent(new OutputEvent(output, category)));
this.gdb.on('execAsync', (resultClass, resultData) => this.handleGDBAsync(resultClass, resultData));
this.gdb.on('notifyAsync', (resultClass, resultData) => this.handleGDBNotify(resultClass, resultData));
// gdb server has main info channel on stderr
this.gdbServer.on('stderr', data => this.sendEvent(new OutputEvent(data, 'stdout')));
const gdbServerErrors: any[] = []
const gdbServerErrorAccumulator = (message: any) => gdbServerErrors.push(message);
this.gdbServer.on('error', gdbServerErrorAccumulator);
try {
this.symbolTable = new SymbolTable(args.program, args.objdump);
await this.symbolTable.loadSymbols();
} catch (error) {
throw new Error(`Unable to load debug symbols: ${error.message}`);
}
const port = await this.portScanner.findFreePort();
if (!port) {
throw new Error('Unable to find a free port to use for debugging');
}
this.sendEvent(new OutputEvent(`Selected port ${port} for debugging`));
const remote = `localhost:${port}`;
// Set gdb arguments
if (!args.gdbArguments) {
args.gdbArguments = [];
}
args.gdbArguments.push('-q', args.program);
// Set gdb server arguments
if (!args.gdbServerArguments) {
args.gdbServerArguments = [];
}
args.gdbServerPort = port;
// Start gdb client and server
this.progressEvent(0, 'Starting Debugger');
this.sendEvent(new OutputEvent(`Starting debugger: ${JSON.stringify(args)}`));
await this.gdbServer.spawn(args);
await this.spawn(args);
this.checkServerErrors(gdbServerErrors);
// Send commands
await mi.sendTargetAsyncOn(this.gdb);
await mi.sendTargetSelectRemote(this.gdb, remote);
await mi.sendMonitorResetHalt(this.gdb);
this.sendEvent(new OutputEvent(`Attached to debugger on port ${port}`));
this.checkServerErrors(gdbServerErrors);
// Download image
const progressListener = (percent: number) => this.progressEvent(percent, 'Loading Image');
progressListener(0);
this.gdbServer.on('progress', progressListener);
await mi.sendTargetDownload(this.gdb);
this.gdbServer.removeListener('progress', progressListener);
progressListener(100);
// Halt after image download
await mi.sendMonitorResetHalt(this.gdb);
await this.gdb.sendEnablePrettyPrint();
if (args.runToMain === true) {
await mi.sendBreakOnFunction(this.gdb);
}
this.checkServerErrors(gdbServerErrors);
this.sendEvent(new OutputEvent(`Image loaded: ${args.program}`));
this.sendEvent(new InitializedEvent());
this.gdbServer.removeListener('error', gdbServerErrorAccumulator);
this.gdbServer.on('error', message => {
logger.error(JSON.stringify(message));
if (!this.gdbServer.serverErrorEmitted) {
this.sendEvent(new TerminatedEvent());
}
});
}
private checkServerErrors(errors: any[]): void {
if (errors.length > 0) {
throw new Error(errors.join('\n'));
}
}
protected spawn(args: CmsisRequestArguments): Promise<void> {
const varRegexp = /\$\{.*\}/;
if (args.gdb && varRegexp.test(args.gdb)) {
throw new Error(`Unresolved variable: ${args.gdb}`)
}
return super.spawn(args);
}
private async getGlobalVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
const frame = this.frameHandles.get(frameHandle);
const symbolInfo = 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;
}
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);
const file = normalize(result.frame.file || '');
const symbolInfo = 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,
};
}
private progressEvent(percent: number, message: string) {
this.sendEvent(new OutputEvent('progress', 'telemetry', {
percent,
message
}));
}
protected sendErrorResponse(response: DebugProtocol.Response,
codeOrMessage: number | DebugProtocol.Message, format?: string,
variables?: any, dest?: ErrorDestination): void {
if (!!format && (dest === undefined || dest === ErrorDestination.User)) {
format = format.replace(/\n/g, '<br>');
}
super.sendErrorResponse(response, codeOrMessage, format, variables, dest);
}
protected async stopSession() {
// Pause debugging
if (this.isRunning) {
// Need to pause first
const waitPromise = new Promise(resolve => this.waitPaused = resolve);
this.gdb.pause();
await waitPromise;
}
// Detach
if ((this.gdb as CmsisBackend).isRunning) {
try {
await mi.sendTargetDetach(this.gdb);
} catch (e) {
// Need to catch here as the command result being returned will never exist as it's detached
}
}
// Stop gdb client
try {
await this.gdb.sendGDBExit();
} catch (e) {
// Need to catch here in case the connection has already been closed
}
}
public async shutdown() {
await this.stopSession();
super.shutdown();
}
}

View File

@ -1,80 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { MIFrameInfo } from 'cdt-gdb-adapter/dist/mi';
import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend';
export function sendTargetAsyncOn(gdb: GDBBackend) {
const set = 'target-async on';
return gdb.sendGDBSet(set);
}
export function sendMonitorResetHalt(gdb: GDBBackend) {
const command = '-interpreter-exec console "monitor reset halt"';
return gdb.sendCommand(command);
}
export function sendTargetSelectRemote(gdb: GDBBackend, remote: string) {
const command = `-target-select extended-remote ${remote}`;
return gdb.sendCommand(command);
}
export function sendTargetDownload(gdb: GDBBackend) {
// const command = '-target-download';
// return gdb.sendCommand(command);
}
export function sendBreakOnFunction(gdb: GDBBackend, fn: string = 'main') {
const command = `-break-insert -t --function ${fn}`;
return gdb.sendCommand(command);
}
export function sendExecInterrupt(gdb: GDBBackend, threadId?: number) {
let command = '-exec-interrupt';
if (threadId) {
command += ` --thread ${threadId}`;
}
return gdb.sendCommand(command);
}
export function sendStackInfoFrame(gdb: GDBBackend, threadId: number, frameId: number): Promise<{frame: MIFrameInfo}> {
const command = `-stack-info-frame --thread ${threadId} --frame ${frameId}`;
return gdb.sendCommand(command);
}
export function sendUserInput(gdb: GDBBackend, command: string): Promise<any> {
if (!command.startsWith('-')) {
command = `interpreter-exec console "${command}"`;
}
return gdb.sendCommand(command);
}
export function sendTargetDetach(gdb: GDBBackend) {
const command = '-target-detach';
return gdb.sendCommand(command);
}
export * from 'cdt-gdb-adapter/dist/mi';

View File

@ -1,54 +0,0 @@
import { AbstractServer } from './abstract-server';
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:/mi;
const PERCENT_MULTIPLIER = 100 / 40; // pyOCD outputs 40 markers for progress
export class OpenocdServer extends AbstractServer {
protected portScanner = new PortScanner();
protected progress = 0;
protected async resolveServerArguments(req: CmsisRequestArguments): Promise<string[]> {
let sessionConfigFile = `gdb_port ${req.gdbServerPort!}${"\n"}`;
const telnetPort = await this.portScanner.findFreePort(4444);
if (!!telnetPort) {
sessionConfigFile += `telnet_port ${telnetPort}${"\n"}`
}
sessionConfigFile += `echo "GDB server started"${"\n"}`
const tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), "arduino-debugger"));
const sessionCfgPath = path.join(tmpdir, "gdb.cfg");
await fs.writeFile(sessionCfgPath, sessionConfigFile);
let serverArguments = req.gdbServerArguments || [];
serverArguments.push("--file", sessionCfgPath);
return serverArguments;
}
protected onStdout(chunk: string | Buffer) {
super.onStdout(chunk);
const buffer = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
const match = buffer.match(/=/g);
if (match) {
this.progress += match.length;
const percent = Math.round(this.progress * PERCENT_MULTIPLIER);
this.emit('progress', percent);
}
}
protected serverStarted(data: string): boolean {
return LAUNCH_REGEX.test(data);
}
protected serverError(data: string): boolean {
return ERROR_REGEX.test(data);
}
}

View File

@ -1,192 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2016 Zoujie
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { exec } from 'child_process';
const maxBuffer = 2 * 1024 * 1024;
export class PortScanner {
public async findFreePort(start: number = 50000, length: number = 100): Promise<number | undefined> {
const fn = this.getFunction().bind(this);
for (let i = start; i <= start + length; i++) {
try {
// Try to find pid of port
await fn(i);
} catch (_e) {
// Port is free when pid not found
return i;
}
}
return undefined;
}
private getFunction(): (port: number) => Promise<number> {
switch (process.platform) {
case 'darwin':
case 'freebsd':
case 'sunos': {
return this.darwin;
break;
}
case 'linux': {
return this.linux;
break;
}
case 'win32': {
return this.windows;
break;
}
}
return () => Promise.resolve(0);
}
private async darwin(port: number): Promise<number> {
const result = await this.execute('netstat -anv -p TCP && netstat -anv -p UDP');
// Replace header
const data = this.stripLine(result.toString(), 2);
const found = this.extractColumns(data, [0, 3, 8], 10)
.filter(row => !!String(row[0]).match(/^(udp|tcp)/))
.find(row => {
const matches = String(row[1]).match(/\.(\d+)$/);
return (matches && matches[1] === String(port));
});
if (found && found[2].length) {
return parseInt(found[2], 10);
}
throw new Error(`pid of port (${port}) not found`);
}
private async linux(port: number): Promise<number> {
// netstat -p ouputs warning if user is no-root
const result = await this.execute('netstat -tunlp');
// Replace header
const data = this.stripLine(result.toString(), 2);
const columns = this.extractColumns(data, [3, 6], 7)
.find(column => {
const matches = String(column[0]).match(/:(\d+)$/);
return (matches && matches[1] === String(port));
});
if (columns && columns[1]) {
const pid = columns[1].split('/', 1)[0];
if (pid.length) {
return parseInt(pid, 10);
}
}
throw new Error(`pid of port (${port}) not found`);
}
private async windows(port: number): Promise<number> {
const result = await this.execute('netstat -ano');
// Replace header
const data = this.stripLine(result.toString(), 4);
const columns = this.extractColumns(data, [1, 4], 5)
.find(column => {
const matches = String(column[0]).match(/:(\d+)$/);
return (matches && matches[1] === String(port));
});
if (columns && columns[1].length && parseInt(columns[1], 10) > 0) {
return parseInt(columns[1], 10);
}
throw new Error(`pid of port (${port}) not found`);
}
private execute(cmd: string): Promise<string> {
return new Promise((resolve, reject) => {
exec(cmd, {
maxBuffer,
windowsHide: true
}, (error: Error | null, stdout: string) => {
if (error) {
return reject(error);
}
return resolve(stdout);
});
});
}
private stripLine(text: string, num: number): string {
let idx = 0;
while (num-- > 0) {
const nIdx = text.indexOf('\n', idx);
if (nIdx >= 0) {
idx = nIdx + 1;
}
}
return idx > 0 ? text.substring(idx) : text;
}
private extractColumns(text: string, idxes: number[], max: number): string[][] {
const lines = text.split(/(\r\n|\n|\r)/);
const columns: string[][] = [];
if (!max) {
max = Math.max.apply(null, idxes) + 1;
}
lines.forEach(line => {
const cols = this.split(line, max);
const column: string[] = [];
idxes.forEach(idx => {
column.push(cols[idx] || '');
});
columns.push(column);
});
return columns;
}
private split(line: string, max: number): string[] {
const cols = line.trim().split(/\s+/);
if (cols.length > max) {
cols[max - 1] = cols.slice(max - 1).join(' ');
}
return cols;
}
}

View File

@ -1,71 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { AbstractServer } from './abstract-server';
import { PortScanner } from './port-scanner';
import { CmsisRequestArguments } from './cmsis-debug-session';
const LAUNCH_REGEX = /GDB server started/;
const ERROR_REGEX = /:ERROR:gdbserver:/;
const PERCENT_MULTIPLIER = 100 / 40; // pyOCD outputs 40 markers for progress
export class PyocdServer extends AbstractServer {
protected portScanner = new PortScanner();
protected progress = 0;
protected async resolveServerArguments(req: CmsisRequestArguments): Promise<string[]> {
let serverArguments = req.gdbServerArguments || [];
serverArguments.push('--port', req.gdbServerPort!.toString())
const telnetPort = await this.portScanner.findFreePort(4444);
if (!!telnetPort) {
serverArguments.push('--telnet-port', telnetPort.toString())
}
return serverArguments;
}
protected onStdout(chunk: string | Buffer) {
super.onStdout(chunk);
const buffer = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
const match = buffer.match(/=/g);
if (match) {
this.progress += match.length;
const percent = Math.round(this.progress * PERCENT_MULTIPLIER);
this.emit('progress', percent);
}
}
protected serverStarted(data: string): boolean {
return LAUNCH_REGEX.test(data);
}
protected serverError(data: string): boolean {
return ERROR_REGEX.test(data);
}
}

View File

@ -1,141 +0,0 @@
/*
* CMSIS Debug Adapter
* Copyright (c) 2017-2019 Marcel Ball
* Copyright (c) 2019 Arm Limited
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { EOL } from 'os';
import { normalize, basename } from 'path';
import { spawnCommand } from 'arduino-ide-extension/lib/node/exec-util';
export enum SymbolType {
Function,
File,
Object,
Normal
}
export enum SymbolScope {
Local,
Global,
Neither,
Both
}
export interface SymbolInformation {
address: number;
length: number;
name: string;
section: string;
type: SymbolType;
scope: SymbolScope;
file?: string;
hidden: boolean;
}
const SYMBOL_REGEX = /^([0-9a-f]{8})\s([lg\ !])([w\ ])([C\ ])([W\ ])([I\ ])([dD\ ])([FfO\ ])\s([^\s]+)\s([0-9a-f]+)\s(.*)\r?$/;
const TYPE_MAP: { [id: string]: SymbolType } = {
'F': SymbolType.Function,
'f': SymbolType.File,
'O': SymbolType.Object,
' ': SymbolType.Normal
};
const SCOPE_MAP: { [id: string]: SymbolScope } = {
'l': SymbolScope.Local,
'g': SymbolScope.Global,
' ': SymbolScope.Neither,
'!': SymbolScope.Both
};
export class SymbolTable {
private symbols: SymbolInformation[] = [];
constructor(private program: string, private objdump?: string) {
const varRegexp = /\$\{.*\}/;
if (varRegexp.test(program)) {
throw new Error(`Unresolved variable: ${program}`);
}
if (objdump && varRegexp.test(objdump)) {
throw new Error(`Unresolved variable: ${objdump}`);
}
}
public async loadSymbols(): Promise<void> {
const results = await this.execute();
const output = results.toString();
const lines = output.split(EOL);
let currentFile: string | undefined;
for (const line of lines) {
const match = line.match(SYMBOL_REGEX);
if (match) {
if (match[7] === 'd' && match[8] === 'f') {
currentFile = match[11].trim();
}
const type = TYPE_MAP[match[8]];
const scope = SCOPE_MAP[match[2]];
let name = match[11].trim();
let hidden = false;
if (name.startsWith('.hidden')) {
name = name.substring(7).trim();
hidden = true;
}
this.symbols.push({
address: parseInt(match[1], 16),
type: type,
scope: scope,
section: match[9].trim(),
length: parseInt(match[10], 16),
name: name,
file: scope === SymbolScope.Local ? currentFile : undefined,
hidden: hidden
});
}
}
}
public getGlobalVariables(): SymbolInformation[] {
const matches = this.symbols.filter(s => s.type === SymbolType.Object && s.scope === SymbolScope.Global);
return matches;
}
public getStaticVariables(file: string): SymbolInformation[] {
return this.symbols.filter(s =>
s.type === SymbolType.Object // Only load objects
&& s.scope === SymbolScope.Local // Scoped to this file
&& !s.name.startsWith('.') // Ignore names beginning with '.'
&& (normalize(s.file || '') === normalize(file) || normalize(s.file || '') === basename(file))); // Match full path or file name
}
private execute(): Promise<string> {
if (!this.objdump) {
return Promise.reject(new Error('Missing parameter: objdump'));
}
return spawnCommand(this.objdump, ['--syms', this.program]);
}
}