From 6931f3acea8a62bba2a53fe28ec106162bcebc8b Mon Sep 17 00:00:00 2001 From: paulober <44974737+paulober@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:53:57 +0100 Subject: [PATCH] [WIP] Defmt print support Signed-off-by: paulober <44974737+paulober@users.noreply.github.com> --- .vscodeignore | 2 + package.json | 6 + scripts/rttDecoder.cjs | 98 ++++++++++++++++ scripts/rttDecoder.mjs | 121 ++++++++++++++++++++ src/commands/getPaths.mts | 6 +- src/commands/rttDecoder.mts | 19 +++ src/extension.mts | 6 +- src/utils/download.mts | 4 +- src/utils/projectGeneration/projectRust.mts | 19 ++- src/utils/rustUtil.mts | 4 +- src/utils/sharedConstants.mts | 1 + src/webview/newProjectPanel.mts | 11 +- 12 files changed, 281 insertions(+), 16 deletions(-) create mode 100644 scripts/rttDecoder.cjs create mode 100644 scripts/rttDecoder.mjs create mode 100644 src/commands/rttDecoder.mts diff --git a/.vscodeignore b/.vscodeignore index 329f08d..c09f89c 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -36,6 +36,8 @@ tmp.py !scripts/Pico.code-profile !scripts/raspberrypi-swd.cfg !data/** +!scripts/rttDecoder.mjs +!scripts/rttDecoder.js scripts/*.ps1 scripts/fix_windows_reg.py scripts/vscodeUninstaller.mjs diff --git a/package.json b/package.json index 1ba934d..873fac3 100644 --- a/package.json +++ b/package.json @@ -220,6 +220,12 @@ "title": "Flash Pico Project (SWD)", "category": "Raspberry Pi Pico", "enablement": "raspberry-pi-pico.isPicoProject && !raspberry-pi-pico.isRustProject" + }, + { + "command": "raspberry-pi-pico.getRTTDecoderPath", + "title": "Get RTT Decoder module path", + "category": "Raspberry Pi Pico", + "enablement": "false" } ], "configuration": { diff --git a/scripts/rttDecoder.cjs b/scripts/rttDecoder.cjs new file mode 100644 index 0000000..e14b505 --- /dev/null +++ b/scripts/rttDecoder.cjs @@ -0,0 +1,98 @@ +const { spawn } = require('child_process'); + +class DefmtDecoder { + constructor() { + this.process = null; + this.elfPath = null; + this.displayOutput = null; + this.graphData = null; + this.ports = []; + } + + init(config, displayOutput, graphData) { + // Store the callbacks and elfPath from the config + this.elfPath = config.elfPath; + this.displayOutput = displayOutput; + this.graphData = graphData; + this.ports = config.ports; + + const defmtPrintPath = `${process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME}/.cargo/bin/defmt-print${process.platform === "win32" ? ".exe" : ""}`; + + // Spawn the defmt-print process with the provided ELF path + this.process = spawn(defmtPrintPath, ['-e', this.elfPath, "stdin"]); + + // Handle data from defmt-print stdout and relay it to the displayOutput callback + this.process.stdout.on('data', (data) => { + if (this.displayOutput) { + this.displayOutput(data.toString()); + } + }); + + // Handle errors from defmt-print stderr + this.process.stderr.on('data', (data) => { + if (this.displayOutput) { + this.displayOutput(data.toString()); + } + }); + + // Handle when the process closes + this.process.on('close', (code) => { + if (this.displayOutput) { + this.displayOutput(`Decoding process exited with code: ${code}`); + } + }); + } + + sendData(input) { + // Write input data to defmt-print's stdin + try { + if (this.process && this.process.stdin.writable) { + this.process.stdin.write(input); + return; + } + } catch { } + + throw new Error('Process stdin is not writable.'); + } + + // Expected methods from the SWODecoder API conforming to the AdvancedDecoder interface + + typeName() { + return 'DefmtDecoder'; + } + + outputLabel() { + return 'RPi Pico'; + } + + softwareEvent(port, data) { + if (this.ports.indexOf(port) !== -1) { + // Handle the software event, potentially by sending data to defmt-print stdin + this.sendData(data); + } + } + + synchronized() { + // Handle the synchronized event + if (this.displayOutput) { + this.displayOutput('Synchronized'); + } + } + + lostSynchronization() { + // Handle the lost synchronization event + if (this.displayOutput) { + this.displayOutput('Lost synchronization'); + } + } + + dispose() { + // Clean up the process + if (this.process) { + this.process.kill(); + this.process = null; + } + } +} + +module.exports = exports = DefmtDecoder; diff --git a/scripts/rttDecoder.mjs b/scripts/rttDecoder.mjs new file mode 100644 index 0000000..273abe7 --- /dev/null +++ b/scripts/rttDecoder.mjs @@ -0,0 +1,121 @@ +import { spawn } from 'child_process'; +import EventEmitter from 'events'; + +/* +interface AdvancedDecoder { + init( + config: SWOAdvancedDecoderConfig, + outputData: (output: string) => void, + graphData: (data: number, id: string) => void + ): void; + typeName(): string; + outputLabel(): string; + softwareEvent(port: number, data: Buffer): void; + synchronized(): void; + lostSynchronization(): void; +}*/ + +class DefmtDecoder extends EventEmitter { + constructor() { + this.process = null; + this.elfPath = null; + this.displayOutput = null; + this.graphData = null; + } + + init(config, displayOutput, graphData) { + // Store the callbacks and elfPath from the config + this.elfPath = config.config.elfPath; + this.displayOutput = displayOutput; + this.graphData = graphData; + + const defmtPrintPath = `${process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME}/.cargo/bin/defmt-print${process.platform === "win32" ? ".exe" : ""}`; + + // Spawn the defmt-print process with the provided ELF path + this.process = spawn(defmtPrintPath, ['-e', this.elfPath, "stdin"]); + + // Handle data from defmt-print stdout and relay it to the displayOutput callback + this.process.stdout.on('data', (data) => { + if (this.displayOutput) { + this.displayOutput(data.toString()); + } + }); + + // Handle errors from defmt-print stderr + this.process.stderr.on('data', (data) => { + if (this.displayOutput) { + //this.displayOutput(`Error: ${data.toString()}`); + this.displayOutput(data.toString()); + } + }); + + // Handle when the process closes + this.process.on('close', (code) => { + if (this.displayOutput) { + this.displayOutput(`Decoding process exited with code: ${code}`); + } + }); + } + + //sendData(input: Buffer): void; + sendData(input) { + // Write input data to defmt-print's stdin + try { + if (this.process && this.process.stdin.writable) { + this.process.stdin.write(input); + return; + } + } catch { } + + throw new Error('Process stdin is not writable.'); + } + + // Expected methods from the SWODecoder API conforming to the AdvancedDecoder interface + + //typeName(): string; + typeName() { + return 'DefmtDecoder'; + } + + //outputLabel(): string; + outputLabel() { + return 'RPi Pico'; + } + + //softwareEvent(port: number, data: Buffer): void; + softwareEvent(port, data) { + if (this.ports.indexOf(port) !== -1) { + // Handle the software event, potentially by sending data to defmt-print stdin + this.sendData(data); + } + } + + //synchronized(): void; + synchronized() { + // Handle the synchronized event + if (this.displayOutput) { + this.displayOutput('Synchronized'); + } + } + + //lostSynchronization(): void; + lostSynchronization() { + // Handle the lost synchronization event + if (this.displayOutput) { + this.displayOutput('Lost synchronization'); + } + } + + // own dispose method + + dispose() { + // Clean up the process + if (this.process) { + this.process.kill(); + this.process = null; + } + this.emit('dispose'); + } +} + +export default DefmtDecoder; diff --git a/src/commands/getPaths.mts b/src/commands/getPaths.mts index 0e29102..18010ec 100644 --- a/src/commands/getPaths.mts +++ b/src/commands/getPaths.mts @@ -20,12 +20,12 @@ import Settings, { SettingsKey } from "../settings.mjs"; import which from "which"; import { execSync } from "child_process"; import { getPicotoolReleases } from "../utils/githubREST.mjs"; -import { openOCDVersion } from "../webview/newProjectPanel.mjs"; import State from "../state.mjs"; import VersionBundlesLoader from "../utils/versionBundles.mjs"; import { getSupportedToolchains } from "../utils/toolchainUtil.mjs"; import Logger from "../logger.mjs"; import { rustProjectGetSelectedChip } from "../utils/rustUtil.mjs"; +import { OPENOCD_VERSION } from "../utils/sharedConstants.mjs"; export class GetPythonPathCommand extends CommandWithResult { constructor() { @@ -394,7 +394,7 @@ export class GetOpenOCDRootCommand extends CommandWithResult< this.running = true; // check if it is installed if not install it - const result = await downloadAndInstallOpenOCD(openOCDVersion); + const result = await downloadAndInstallOpenOCD(OPENOCD_VERSION); if (result === null || !result) { this.running = false; @@ -404,7 +404,7 @@ export class GetOpenOCDRootCommand extends CommandWithResult< this.running = false; - return buildOpenOCDPath(openOCDVersion); + return buildOpenOCDPath(OPENOCD_VERSION); } } diff --git a/src/commands/rttDecoder.mts b/src/commands/rttDecoder.mts new file mode 100644 index 0000000..548c81b --- /dev/null +++ b/src/commands/rttDecoder.mts @@ -0,0 +1,19 @@ +import Logger from "../logger.mjs"; +import { CommandWithResult } from "./command.mjs"; +import { Uri } from "vscode"; + +export default class GetRTTDecoderPathCommand extends CommandWithResult { + private readonly _logger = new Logger("GetRTTDecoderPathCommand"); + + public static readonly id = "getRTTDecoderPath"; + + constructor(private readonly _extensionUri: Uri) { + super(GetRTTDecoderPathCommand.id); + } + + execute(): string { + this._logger.debug("Retrieving RTT decoder path"); + + return Uri.joinPath(this._extensionUri, "scripts", "rttDecoder.cjs").fsPath; + } +} diff --git a/src/extension.mts b/src/extension.mts index d58a6fa..a734f36 100644 --- a/src/extension.mts +++ b/src/extension.mts @@ -61,7 +61,6 @@ import { getSupportedToolchains } from "./utils/toolchainUtil.mjs"; import { NewProjectPanel, getWebviewOptions, - openOCDVersion, } from "./webview/newProjectPanel.mjs"; import GithubApiCache from "./utils/githubApiCache.mjs"; import ClearGithubApiCacheCommand from "./commands/clearGithubApiCache.mjs"; @@ -87,6 +86,8 @@ import { } from "./utils/rustUtil.mjs"; import State from "./state.mjs"; import { NewRustProjectPanel } from "./webview/newRustProjectPanel.mjs"; +import GetRTTDecoderPathCommand from "./commands/rttDecoder.mjs"; +import { OPENOCD_VERSION } from "./utils/sharedConstants.mjs"; export async function activate(context: ExtensionContext): Promise { Logger.info(LoggerSource.extension, "Extension activation triggered"); @@ -135,6 +136,7 @@ export async function activate(context: ExtensionContext): Promise { new ImportProjectCommand(context.extensionUri), new NewExampleProjectCommand(context.extensionUri), new UninstallPicoSDKCommand(), + new GetRTTDecoderPathCommand(context.extensionUri), ]; // register all command handlers @@ -537,7 +539,7 @@ export async function activate(context: ExtensionContext): Promise { }, async progress => { const result = await downloadAndInstallOpenOCD( - openOCDVersion, + OPENOCD_VERSION, (prog: GotProgress) => { const percent = prog.percent * 100; progress.report({ diff --git a/src/utils/download.mts b/src/utils/download.mts index 5d134be..9a170f9 100644 --- a/src/utils/download.mts +++ b/src/utils/download.mts @@ -47,11 +47,11 @@ import { got, type Progress } from "got"; import { pipeline as streamPipeline } from "node:stream/promises"; import { CURRENT_PYTHON_VERSION, + OPENOCD_VERSION, WINDOWS_ARM64_PYTHON_DOWNLOAD_URL, WINDOWS_X86_PYTHON_DOWNLOAD_URL, } from "./sharedConstants.mjs"; import VersionBundlesLoader from "./versionBundles.mjs"; -import { openOCDVersion } from "../webview/newProjectPanel.mjs"; /// Translate nodejs platform names to ninja platform names const NINJA_PLATFORMS: { [key: string]: string } = { @@ -1390,7 +1390,7 @@ export async function installLatestRustRequirements( async progress => { let progressState = 0; - return downloadAndInstallOpenOCD(openOCDVersion, (prog: Progress) => { + return downloadAndInstallOpenOCD(OPENOCD_VERSION, (prog: Progress) => { const percent = prog.percent * 100; progress.report({ increment: percent - progressState }); progressState = percent; diff --git a/src/utils/projectGeneration/projectRust.mts b/src/utils/projectGeneration/projectRust.mts index 3c303e5..1edee8a 100644 --- a/src/utils/projectGeneration/projectRust.mts +++ b/src/utils/projectGeneration/projectRust.mts @@ -70,7 +70,7 @@ async function generateVSCodeConfig(projectRoot: string): Promise { openOCDLaunchCommands: ["adapter speed 5000"], preLaunchTask: "Compile Project (debug)", // TODO: does currently not work - rttConfig: { + /*rttConfig: { enabled: true, clearSearch: true, address: "0x2003fbc0", @@ -86,6 +86,23 @@ async function generateVSCodeConfig(projectRoot: string): Promise { port: 0, }, ], + },*/ + rttConfig: { + enabled: true, + address: "auto", + decoders: [ + { + label: "RPi Pico", + type: "advanced", + decoder: "${command:raspberry-pi-pico.getRTTDecoderPath}", + inputmode: "disabled", + noprompt: true, + ports: [0], + config: { + elfPath: "${command:raspberry-pi-pico.launchTargetPath}", + }, + }, + ], }, }, ], diff --git a/src/utils/rustUtil.mts b/src/utils/rustUtil.mts index 823933e..5bcd388 100644 --- a/src/utils/rustUtil.mts +++ b/src/utils/rustUtil.mts @@ -340,8 +340,8 @@ export async function downloadAndInstallRust(): Promise { return false; } - // install probe-rs-tools - const probeRsTools = "probe-rs-tools"; + // or install probe-rs-tools + const probeRsTools = "defmt-print"; result = await cargoInstall(probeRsTools, true); if (!result) { void window.showErrorMessage( diff --git a/src/utils/sharedConstants.mts b/src/utils/sharedConstants.mts index 48de290..690d90c 100644 --- a/src/utils/sharedConstants.mts +++ b/src/utils/sharedConstants.mts @@ -5,3 +5,4 @@ export const WINDOWS_ARM64_PYTHON_DOWNLOAD_URL = export const CURRENT_PYTHON_VERSION = "3.12.6"; export const CURRENT_DATA_VERSION = "0.17.0"; +export const OPENOCD_VERSION = "0.12.0+dev"; diff --git a/src/webview/newProjectPanel.mts b/src/webview/newProjectPanel.mts index 4e1028f..fea331b 100644 --- a/src/webview/newProjectPanel.mts +++ b/src/webview/newProjectPanel.mts @@ -61,12 +61,11 @@ import { import { unknownErrorToString } from "../utils/errorHelper.mjs"; import type { Progress as GotProgress } from "got"; import findPython, { showPythonNotFoundError } from "../utils/pythonHelper.mjs"; +import { OPENOCD_VERSION } from "../utils/sharedConstants.mjs"; export const NINJA_AUTO_INSTALL_DISABLED = false; // process.platform === "linux" && process.arch === "arm64"; -export const openOCDVersion = "0.12.0+dev"; - interface ImportProjectMessageValue { selectedSDK: string; selectedToolchain: string; @@ -999,7 +998,7 @@ export class NewProjectPanel { }, async progress2 => { const result = await downloadAndInstallOpenOCD( - openOCDVersion, + OPENOCD_VERSION, (prog: GotProgress) => { const per = prog.percent * 100; progress2.report({ @@ -1250,7 +1249,7 @@ export class NewProjectPanel { sdkVersion: selectedSDK, sdkPath: buildSDKPath(selectedSDK), picotoolVersion: selectedPicotool, - openOCDVersion: openOCDVersion, + openOCDVersion: OPENOCD_VERSION, }, ninjaExecutable, cmakeExecutable, @@ -1275,7 +1274,7 @@ export class NewProjectPanel { sdkVersion: selectedSDK, sdkPath: buildSDKPath(selectedSDK), picotoolVersion: selectedPicotool, - openOCDVersion: openOCDVersion, + openOCDVersion: OPENOCD_VERSION, }, ninjaExecutable, cmakeExecutable, @@ -1296,7 +1295,7 @@ export class NewProjectPanel { sdkVersion: selectedSDK, sdkPath: buildSDKPath(selectedSDK), picotoolVersion: selectedPicotool, - openOCDVersion: openOCDVersion, + openOCDVersion: OPENOCD_VERSION, }, ninjaExecutable, cmakeExecutable,