Merge debugger features into master branch (#306)
* Add Arduino debugger start session. (#285) * Add Arduino debugger start session. * Fix based on code review. * Support debugger for az3166 (#302) * fix some debug issues: 1. findFile resolve multiple openocd.exe 2. openocd has two possible script folders : ../scripts and ../share/openocd/scripts 3. if output is undefined, the elf path is not right. * revert unexpected change * fix a issue when source file is change but elf is not compiled again. * fix errors on mac * fix tslint error * Debug feature bug fix. (#303) * 1. make the load for serialport native lazy 2. stop the debugger when early check fails 3. resolve the openocd/gdb from other package if current package doesn't contain them * refine the output folder name. * 1. use static getter method to implement lazy load * fix tslint error * refact debug code (#305) * 1. make the load for serialport native lazy 2. stop the debugger when early check fails 3. resolve the openocd/gdb from other package if current package doesn't contain them * refine the output folder name. * 1. use static getter method to implement lazy load * fix tslint error * 1. refact debug code for separate interface and target, interfaces are listed due to usb pid/vid, target is linked to target board, swd is the only transport currently supported. * fix tslint error. * refine the current board code from this._boardManager in debugmanager. * Update usbmapping.json typo * fix the issue of package name and multiple pid match * 1.add arduino zero board 2. change debug manager to configurator * 1. for boards which support debug, check whether the expected debugger is connected. * change tab size for package.jsonfeature/builder
parent
e3b9c5e45a
commit
6d7ed7d75a
@ -0,0 +1,66 @@ |
||||
[ |
||||
{ |
||||
"vid": "0483", |
||||
"pid": "3744", |
||||
"name": "ST Link V1.0", |
||||
"short_name": "stlink-1.0", |
||||
"config_file": "stlink-v1.cfg" |
||||
}, |
||||
{ |
||||
"vid": "0483", |
||||
"pid": "3748", |
||||
"name": "ST Link V2.0", |
||||
"short_name": "stlink-2.0", |
||||
"config_file": "stlink-v2.cfg" |
||||
}, |
||||
{ |
||||
"vid": "0483", |
||||
"pid": "374b", |
||||
"name": "ST Link V2.1", |
||||
"short_name": "stlink-2.1", |
||||
"config_file": "stlink-v2-1.cfg" |
||||
}, |
||||
{ |
||||
"pid": [ |
||||
"0101", |
||||
"0102", |
||||
"0103", |
||||
"0104", |
||||
"0105", |
||||
"0106", |
||||
"0107", |
||||
"0108", |
||||
"0109", |
||||
"0110", |
||||
"0111", |
||||
"0112", |
||||
"0113", |
||||
"0114", |
||||
"0115", |
||||
"0116", |
||||
"0117", |
||||
"0118" |
||||
], |
||||
"vid": "1366", |
||||
"name": "JLink", |
||||
"short_name": "jlink", |
||||
"config_file": "jlink.cfg" |
||||
}, |
||||
{ |
||||
"vid": "03eb", |
||||
"pid": "2111", |
||||
"name": "CMSIS-DAP", |
||||
"short_name": "cmsis-dap", |
||||
"config_file": "cmsis-dap.cfg" |
||||
}, |
||||
{ |
||||
"vid": "03eb", |
||||
"pid": [ |
||||
"2111", |
||||
"2157" |
||||
], |
||||
"name": "CMSIS-DAP", |
||||
"short_name": "cmsis-dap", |
||||
"config_file": "cmsis-dap.cfg" |
||||
} |
||||
] |
@ -0,0 +1,18 @@ |
||||
[ |
||||
{ |
||||
"board": "arduino:samd:mzero_pro_bl_dbg", |
||||
"interface": "interface/cmsis-dap.cfg", |
||||
"target": "target/at91samdXX.cfg" |
||||
}, |
||||
{ |
||||
"board": "arduino:samd:mzero_bl", |
||||
"interface": "interface/cmsis-dap.cfg", |
||||
"target": "target/at91samdXX.cfg" |
||||
}, |
||||
{ |
||||
"board": "AZ3166:stm32f4:MXCHIP_AZ3166", |
||||
"interface": "interface/stlink-v2-1.cfg", |
||||
"target": "target/stm32f4x.cfg" |
||||
} |
||||
] |
||||
|
@ -1,76 +1,147 @@ |
||||
[{ |
||||
[ |
||||
{ |
||||
"index_file": "package_index.json", |
||||
"boards": [{ |
||||
"boards": [ |
||||
{ |
||||
"vid": "2341", |
||||
"pid": "804e", |
||||
"name": "Arduino/Genuino MKR1000", |
||||
"package": "arduino", |
||||
"architecture": "samd", |
||||
"id": "mkr1000" |
||||
}, { |
||||
"id": "mkr1000", |
||||
"target": "at91samdXX.cfg" |
||||
}, |
||||
{ |
||||
"vid": "8087", |
||||
"pid": "0a9e", |
||||
"name": "Intel Edison", |
||||
"package": "arduino", |
||||
"architecture": "samd", |
||||
"id": "edison" |
||||
}, { |
||||
}, |
||||
{ |
||||
"vid": "03eb", |
||||
"pid": "2111", |
||||
"name": "Arduino M0 Pro (Programming USB Port)", |
||||
"package": "arduino", |
||||
"architecture": "samd", |
||||
"id": "mzero_pro_bl_dbg", |
||||
"target": "at91samdXX.cfg", |
||||
"interface": "cmsis-dap.cfg" |
||||
}, |
||||
{ |
||||
"vid": "03eb", |
||||
"pid": "2157", |
||||
"name": "Arduino/Genuino Zero (Programming Port)", |
||||
"package": "arduino", |
||||
"architecture": "samd", |
||||
"id": "arduino_zero_edbg", |
||||
"target": "at91samdXX.cfg", |
||||
"interface": "cmsis-dap.cfg" |
||||
}, |
||||
{ |
||||
"vid": "2341", |
||||
"pid": ["804d", "004d", "824d"], |
||||
"name": "Arduino/Genuino Zero (Native USB Port)", |
||||
"package": "arduino", |
||||
"architecture": "samd", |
||||
"id": "arduino_zero_edbg", |
||||
"target": "at91samdXX.cfg", |
||||
"interface": "cmsis-dap.cfg" |
||||
}, |
||||
{ |
||||
"vid": "2a03", |
||||
"pid": "804f", |
||||
"name": "Arduino M0 Pro (Native USB Port)", |
||||
"package": "arduino", |
||||
"architecture": "samd", |
||||
"id": "mzero_pro_bl" |
||||
}, { |
||||
"id": "mzero_pro_bl", |
||||
"target": "at91samdXX.cfg" |
||||
}, |
||||
{ |
||||
"vid": "2341", |
||||
"pid": "8041", |
||||
"name": "Arduino Yún", |
||||
"package": "arduino", |
||||
"architecture": "avr", |
||||
"id": "yun" |
||||
}, { |
||||
}, |
||||
{ |
||||
"vid": "2341", |
||||
"pid": "003e", |
||||
"name": "Arduino Due", |
||||
"package": "arduino", |
||||
"architecture": "sam", |
||||
"id": "arduino_due_x" |
||||
}] |
||||
}, { |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"index_file": "https://www.adafruit.com/package_adafruit_index.json", |
||||
"boards": [ |
||||
{ |
||||
"vid": "239a", |
||||
"pid": [ |
||||
"0010", |
||||
"8010", |
||||
"0008" |
||||
], |
||||
"name": "Adafruit WICED Feather", |
||||
"package": "adafruit", |
||||
"architecture": "wiced", |
||||
"id": "feather", |
||||
"target": "stm32f2x.cfg" |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"index_file": "http://arduino.esp8266.com/stable/package_esp8266com_index.json", |
||||
"boards": [{ |
||||
"boards": [ |
||||
{ |
||||
"vid": "10c4", |
||||
"pid": "ea60", |
||||
"name": "Adafruit HUZZAH ESP8266", |
||||
"package": "esp8266", |
||||
"architecture": "esp8266", |
||||
"id": "huzzah" |
||||
}, { |
||||
}, |
||||
{ |
||||
"vid": "0403", |
||||
"pid": "6015", |
||||
"name": "SparkFun ESP8266 Thing Dev", |
||||
"package": "esp8266", |
||||
"architecture": "esp8266", |
||||
"id": "thingdev" |
||||
}] |
||||
}, { |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"index_file": "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json", |
||||
"boards": [{ |
||||
"boards": [ |
||||
{ |
||||
"vid": "239a", |
||||
"pid": "800b", |
||||
"name": "Adafruit Feather M0", |
||||
"package": "adafruit", |
||||
"architecture": "samd", |
||||
"id": "adafruit_feather_m0" |
||||
}] |
||||
}, { |
||||
"id": "adafruit_feather_m0", |
||||
"target": "at91samdXX.cfg" |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"index_file": "https://raw.githubusercontent.com/VSChina/azureiotdevkit_tools/master/package_azureboard_index.json", |
||||
"boards": [{ |
||||
"boards": [ |
||||
{ |
||||
"vid": "0483", |
||||
"pid": "374b", |
||||
"name": "MXCHIP AZ3166", |
||||
"package": "AZ3166", |
||||
"architecture": "stm32f4", |
||||
"id": "MXCHIP_AZ3166" |
||||
}] |
||||
}] |
||||
"id": "MXCHIP_AZ3166", |
||||
"interface": "stlink-v2-1.cfg", |
||||
"target": "stm32f4x.cfg" |
||||
} |
||||
] |
||||
} |
||||
] |
||||
|
@ -0,0 +1,174 @@ |
||||
/*-------------------------------------------------------------------------------------------- |
||||
* Copyright (C) Microsoft Corporation. All rights reserved. |
||||
* Licensed under the MIT License. See License.txt in the project root for license information. |
||||
*-------------------------------------------------------------------------------------------*/ |
||||
|
||||
import * as childProcess from "child_process"; |
||||
import * as fs from "fs"; |
||||
import * as path from "path"; |
||||
import * as vscode from "vscode"; |
||||
|
||||
import { ArduinoApp } from "../arduino/arduino"; |
||||
import { ArduinoSettings } from "../arduino/arduinoSettings"; |
||||
import { BoardManager } from "../arduino/boardManager"; |
||||
import * as platform from "../common/platform"; |
||||
import * as util from "../common/util"; |
||||
import { DeviceContext } from "../deviceContext"; |
||||
import { DebuggerManager } from "./debuggerManager"; |
||||
|
||||
/** |
||||
* Automatically generate the Arduino board's debug settings. |
||||
*/ |
||||
export class DebugConfigurator { |
||||
private _debuggerManager: DebuggerManager; |
||||
constructor( |
||||
private _extensionRoot: string, |
||||
private _arduinoApp: ArduinoApp, |
||||
private _arduinoSettings: ArduinoSettings, |
||||
private _boardManager: BoardManager, |
||||
) { |
||||
this._debuggerManager = new DebuggerManager(_extensionRoot, _arduinoSettings, _boardManager); |
||||
} |
||||
|
||||
public async run(config) { |
||||
if (!this._debuggerManager.initialized) { |
||||
this._debuggerManager.initialize(); |
||||
} |
||||
// Default settings:
|
||||
if (!config.request) { |
||||
config = { |
||||
name: "Arduino", |
||||
type: "arduino", |
||||
request: "launch", |
||||
program: "${file}", |
||||
cwd: "${workspaceRoot}", |
||||
MIMode: "gdb", |
||||
logging: { |
||||
engineLogging: true, |
||||
}, |
||||
targetArchitecture: "arm", |
||||
customLaunchSetupCommands: [ |
||||
{ |
||||
text: "target remote localhost:3333", |
||||
}, |
||||
{ |
||||
text: "file ${file}", |
||||
}, |
||||
{ |
||||
text: "load", |
||||
}, |
||||
{ |
||||
text: "monitor reset halt", |
||||
}, |
||||
{ |
||||
text: "monitor reset init", |
||||
}, |
||||
], |
||||
stopAtEntry: true, |
||||
serverStarted: "Info\\ :\\ [\\w\\d\\.]*:\\ hardware", |
||||
launchCompleteCommand: "exec-continue", |
||||
filterStderr: true, |
||||
args: [], |
||||
}; |
||||
} |
||||
|
||||
if (!this.resolveOpenOcd(config)) { |
||||
return; |
||||
} |
||||
if (!await this.resolveOpenOcdOptions(config)) { |
||||
return; |
||||
} |
||||
|
||||
if (!this.resolveDebuggerPath(config)) { |
||||
return; |
||||
} |
||||
|
||||
if (!await this.resolveProgramPath(config)) { |
||||
return; |
||||
} |
||||
|
||||
// Use the C++ debugger MIEngine as the real internal debugger
|
||||
config.type = "cppdbg"; |
||||
vscode.commands.executeCommand("vscode.startDebug", config); |
||||
} |
||||
|
||||
private async resolveProgramPath(config) { |
||||
const dc = DeviceContext.getIntance(); |
||||
|
||||
if (!config.program || config.program === "${file}") { |
||||
// make a unique temp folder because keeping same temp folder will corrupt the build when board is changed
|
||||
const outputFolder = path.join(dc.output || `.build`, this._boardManager.currentBoard.board); |
||||
util.mkdirRecursivelySync(path.join(vscode.workspace.rootPath, outputFolder)); |
||||
config.program = path.join(vscode.workspace.rootPath, outputFolder, `${path.basename(dc.sketch)}.elf`); |
||||
|
||||
// always compile elf to make sure debug the right elf
|
||||
if (!await this._arduinoApp.verify(outputFolder)) { |
||||
vscode.window.showErrorMessage("Failure to verify the program, please check output for details."); |
||||
return false; |
||||
} |
||||
|
||||
config.program = config.program.replace(/\\/g, "/"); |
||||
|
||||
config.customLaunchSetupCommands.forEach((obj) => { |
||||
if (obj.text && obj.text.indexOf("${file}") > 0) { |
||||
obj.text = obj.text.replace(/\$\{file\}/, config.program); |
||||
} |
||||
}); |
||||
} |
||||
if (!util.fileExistsSync(config.program)) { |
||||
vscode.window.showErrorMessage("Cannot find the elf file."); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private resolveDebuggerPath(config) { |
||||
if (!config.miDebuggerPath) { |
||||
config.miDebuggerPath = platform.findFile(platform.getExecutableFileName("arm-none-eabi-gdb"), |
||||
path.join(this._arduinoSettings.packagePath, "packages", this._boardManager.currentBoard.getPackageName())); |
||||
} |
||||
if (!util.fileExistsSync(config.miDebuggerPath)) { |
||||
config.miDebuggerPath = this._debuggerManager.miDebuggerPath; |
||||
} |
||||
if (!util.fileExistsSync(config.miDebuggerPath)) { |
||||
vscode.window.showErrorMessage("Cannot find the debugger path."); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private resolveOpenOcd(config) { |
||||
const dc = DeviceContext.getIntance(); |
||||
if (!config.debugServerPath) { |
||||
config.debugServerPath = platform.findFile(platform.getExecutableFileName("openocd"), |
||||
path.join(this._arduinoSettings.packagePath, "packages", |
||||
this._boardManager.currentBoard.getPackageName())); |
||||
} |
||||
if (!util.fileExistsSync(config.debugServerPath)) { |
||||
config.debugServerPath = this._debuggerManager.debugServerPath; |
||||
} |
||||
if (!util.fileExistsSync(config.debugServerPath)) { |
||||
vscode.window.showErrorMessage("Cannot find the OpenOCD from the launch.json debugServerPath property." + |
||||
"Please input the right path of OpenOCD"); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
private async resolveOpenOcdOptions(config) { |
||||
|
||||
if (config.debugServerPath && !config.debugServerArgs) { |
||||
try { |
||||
config.debugServerArgs = await this._debuggerManager.resolveOpenOcdOptions(config); |
||||
if (!config.debugServerArgs) { |
||||
return false; |
||||
} |
||||
} catch (error) { |
||||
vscode.window.showErrorMessage(error.message); |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,154 @@ |
||||
import * as fs from "fs"; |
||||
import * as path from "path"; |
||||
import * as vscode from "vscode"; |
||||
|
||||
import { ArduinoSettings } from "../arduino/arduinoSettings"; |
||||
import { BoardManager } from "../arduino/boardManager"; |
||||
import * as platform from "../common/platform"; |
||||
import * as util from "../common/util"; |
||||
import { DeviceContext } from "../deviceContext"; |
||||
|
||||
export class DebuggerManager { |
||||
private _initialized: boolean = false; |
||||
private _usbDector; |
||||
private _debugServerPath: string; |
||||
private _miDebuggerPath: string; |
||||
private _debuggerMappings: any = {}; |
||||
private _debuggerBoardMappings: any = {}; |
||||
constructor( |
||||
private _extensionRoot: string, |
||||
private _arduinoSettings: ArduinoSettings, |
||||
private _boardManager: BoardManager) { |
||||
} |
||||
|
||||
public initialize() { |
||||
const debugFileContent = fs.readFileSync(path.join(this._extensionRoot, "misc", "debuggerUsbMapping.json"), "utf8"); |
||||
const usbFileContent = fs.readFileSync(path.join(this._extensionRoot, "misc", "usbmapping.json"), "utf8"); |
||||
|
||||
for (const _debugger of JSON.parse(debugFileContent)) { |
||||
if (Array.isArray(_debugger.pid)) { |
||||
for (const pid of _debugger.pid) { |
||||
this._debuggerMappings[`${pid}%${_debugger.vid}`] = { ..._debugger, pid, vid: _debugger.vid }; |
||||
} |
||||
} else { |
||||
this._debuggerMappings[`${_debugger.pid}%${_debugger.vid}`] = { ..._debugger, pid: _debugger.pid, vid: _debugger.vid }; |
||||
} |
||||
} |
||||
for (const config of JSON.parse(usbFileContent)) { |
||||
for (const board of config.boards) { |
||||
if (board.interface || board.target) { |
||||
this._debuggerBoardMappings[[board.package, board.architecture, board.id].join(":")] = board; |
||||
} |
||||
} |
||||
} |
||||
this._usbDector = require("../../../vendor/node-usb-native").detector; |
||||
this._debugServerPath = platform.findFile(platform.getExecutableFileName("openocd"), |
||||
path.join(this._arduinoSettings.packagePath, "packages")); |
||||
if (!util.fileExistsSync(this._debugServerPath)) { |
||||
this._debugServerPath = ""; |
||||
} |
||||
|
||||
this._miDebuggerPath = platform.findFile(platform.getExecutableFileName("arm-none-eabi-gdb"), |
||||
path.join(this._arduinoSettings.packagePath, "packages")); |
||||
if (!util.fileExistsSync(this._miDebuggerPath)) { |
||||
this._miDebuggerPath = ""; |
||||
} |
||||
this._initialized = true; |
||||
} |
||||
public get initialized(): boolean { |
||||
return this._initialized; |
||||
} |
||||
public get miDebuggerPath(): string { |
||||
return this._miDebuggerPath; |
||||
} |
||||
|
||||
public get debugServerPath(): string { |
||||
return this._debugServerPath; |
||||
} |
||||
|
||||
public async listDebuggers(): Promise<any[]> { |
||||
const usbDeviceList = await this._usbDector.find(); |
||||
const keys = []; |
||||
const results = []; |
||||
usbDeviceList.forEach((device) => { |
||||
if (device.vendorId && device.productId) { |
||||
/* tslint:disable:max-line-length*/ |
||||
const key = util.convertToHex(device.productId, 4) + "%" + util.convertToHex(device.vendorId, 4); |
||||
const relatedDebugger = this._debuggerMappings[key]; |
||||
if (relatedDebugger && keys.indexOf(key) < 0) { |
||||
keys.push(key); |
||||
results.push(relatedDebugger); |
||||
} |
||||
} |
||||
}); |
||||
return results; |
||||
} |
||||
|
||||
public async resolveOpenOcdOptions(config): Promise<string> { |
||||
const board = this._boardManager.currentBoard.key; |
||||
const debugConfig = this._debuggerBoardMappings[board]; |
||||
const dc = DeviceContext.getIntance(); |
||||
const debuggerConfiged: string = dc.debugger_; |
||||
if (!debugConfig) { |
||||
throw new Error(`Debug for board ${this._boardManager.currentBoard.name} is not supported by now.`); |
||||
} |
||||
let resolvedDebugger; |
||||
const debuggers = await this.listDebuggers(); |
||||
if (!debuggers.length) { |
||||
throw new Error(`No supported debuggers are connected.`); |
||||
} |
||||
// rule 1: if this board has debuggers, use its own debugger
|
||||
if (debugConfig.interface) { |
||||
resolvedDebugger = debuggers.find((_debugger) => { |
||||
return _debugger.short_name === debugConfig.interface || _debugger.config_file === debugConfig.interface; |
||||
}); |
||||
if (!resolvedDebugger) { |
||||
throw new Error(`Debug port for board ${this._boardManager.currentBoard.name} is not connected.`); |
||||
} |
||||
} |
||||
// rule 2: if there is only one debugger, use the only debugger
|
||||
if (!resolvedDebugger && !debuggerConfiged && debuggers.length === 1) { |
||||
resolvedDebugger = debuggers[0]; |
||||
} |
||||
|
||||
// rule 3: if there is any configuration about debugger, use this configuration
|
||||
if (!resolvedDebugger && debuggerConfiged) { |
||||
resolvedDebugger = debuggers.find((_debugger) => { |
||||
return _debugger.short_name === debuggerConfiged || _debugger.config_file === debuggerConfiged; |
||||
}); |
||||
} |
||||
if (!resolvedDebugger) { |
||||
const chosen = await vscode.window.showQuickPick(<vscode.QuickPickItem[]>debuggers.map((l): vscode.QuickPickItem => { |
||||
return { |
||||
description: `(0x${l.vid}:0x${l.pid})`, |
||||
label: l.name, |
||||
}; |
||||
}).sort((a, b): number => { |
||||
return a.label === b.label ? 0 : (a.label > b.label ? 1 : -1); |
||||
}), { placeHolder: "Select a debugger" }); |
||||
if (chosen && chosen.label) { |
||||
resolvedDebugger = debuggers.find((_debugger) => _debugger.name === chosen.label); |
||||
if (resolvedDebugger) { |
||||
dc.debugger_ = resolvedDebugger.config_file; |
||||
} |
||||
} |
||||
if (!resolvedDebugger) { |
||||
return ""; |
||||
} |
||||
} |
||||
|
||||
const debugServerPath = config.debugServerPath; |
||||
let scriptsFolder = path.join(path.dirname(debugServerPath), "../scripts/"); |
||||
if (!util.directoryExistsSync(scriptsFolder)) { |
||||
scriptsFolder = path.join(path.dirname(debugServerPath), "../share/openocd/scripts/"); |
||||
} |
||||
if (!util.directoryExistsSync(scriptsFolder)) { |
||||
throw new Error("Cannot find scripts folder from openocd."); |
||||
} |
||||
if (resolvedDebugger.config_file.includes("jlink")) { |
||||
// only swd is supported now
|
||||
return `-s ${scriptsFolder} -f interface/${resolvedDebugger.config_file} -c "transport select swd" -f target/${debugConfig.target}`; |
||||
} |
||||
return `-s ${scriptsFolder} -f interface/${resolvedDebugger.config_file} -f target/${debugConfig.target}`; |
||||
} |
||||
} |
Loading…
Reference in new issue