Skip to content

Commit

Permalink
add peripheral view support
Browse files Browse the repository at this point in the history
  • Loading branch information
brianignacio5 committed Mar 5, 2024
1 parent 01e16ec commit d64c530
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 104 deletions.
18 changes: 11 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1486,9 +1486,6 @@
"runtime": "node",
"configurationAttributes": {
"launch": {
"required": [
"program"
],
"properties": {
"gdb": {
"type": "string",
Expand Down Expand Up @@ -1530,6 +1527,11 @@
"description": "Produce verbose log output",
"default": false
},
"verifyAppBinBeforeDebug": {
"type": "boolean",
"description": "%esp_idf.verifyAppBinBeforeDebug.description%",
"default": false
},
"logFile": {
"type": "string",
"description": "Absolute path to the file to log interaction with gdb"
Expand Down Expand Up @@ -1727,9 +1729,6 @@
}
},
"attach": {
"required": [
"program"
],
"properties": {
"gdb": {
"type": "string",
Expand Down Expand Up @@ -1771,6 +1770,11 @@
"description": "Produce verbose log output",
"default": false
},
"verifyAppBinBeforeDebug": {
"type": "boolean",
"description": "%esp_idf.verifyAppBinBeforeDebug.description%",
"default": false
},
"logFile": {
"type": "string",
"description": "Absolute path to the file to log interaction with gdb"
Expand Down Expand Up @@ -1958,7 +1962,7 @@
"maintenance flush register-cache",
"thb app_main"
],
"gdb": "${command:espIdf.getXtensaGdb}",
"gdb": "^\"\\${command:espIdf.getXtensaGdb}\"",
"target": {
"port": "3333"
}
Expand Down
72 changes: 72 additions & 0 deletions src/cdtDebugAdapter/debugConfProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Project: ESP-IDF VSCode Extension
* File Created: Monday, 26th February 2024 2:54:26 pm
* Copyright 2024 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
CancellationToken,
DebugConfiguration,
DebugConfigurationProvider,
WorkspaceFolder,
} from "vscode";
import { readParameter } from "../idfConfiguration";
import { getProjectName } from "../workspaceConfig";
import { join } from "path";
import { pathExists } from "fs-extra";
import { verifyAppBinary } from "../espIdf/debugAdapter/verifyApp";
import { OpenOCDManager } from "../espIdf/openOcd/openOcdManager";
import { Logger } from "../logger/logger";

export class CDTDebugConfigurationProvider
implements DebugConfigurationProvider {
public async resolveDebugConfiguration(
folder: WorkspaceFolder | undefined,
config: DebugConfiguration,
token?: CancellationToken
): Promise<DebugConfiguration> {
try {
if (!config.program) {
const buildDirPath = readParameter("idf.buildPath", folder) as string;
const projectName = await getProjectName(buildDirPath);
const elfFilePath = join(buildDirPath, `${projectName}.elf`);
const elfFileExists = await pathExists(elfFilePath);
if (!elfFileExists) {
throw new Error(
`${elfFilePath} doesn't exist. Build this project first.`
);
}
}
if (folder && folder.uri && config.verifyAppBinBeforeDebug) {
const isSameAppBinary = await verifyAppBinary(folder.uri);
if (!isSameAppBinary) {
throw new Error(
`Current app binary is different from your project. Flash first.`
);
}
}
const openOCDManager = OpenOCDManager.init();
if (!openOCDManager.isRunning()) {
await openOCDManager.start();
}
} catch (error) {
const msg = error.message
? error.message
: "Some build files doesn't exist. Build this project first.";
Logger.error(error.message, error);
}
return config;
}
}
64 changes: 29 additions & 35 deletions src/espIdf/debugAdapter/nodes/peripheral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,47 +152,41 @@ export class Peripheral extends PeripheralBaseNode {
return this.format;
}

public updateData(): Thenable<boolean> {
return new Promise((resolve, reject) => {
if (!this.expanded) {
return resolve(false);
}
public async updateData(): Promise<boolean> {
if (!this.expanded) {
return false;
}

this.readMemory()
.then((unused) => {
this.updateChildData(resolve, reject, null);
})
.catch((error) => {
const msg = error.message || "unknown error";
const str = `Failed to update peripheral ${this.name}: ${msg}`;
if (debug.activeDebugConsole) {
try {
const errors = await this.readMemory();
for (const error of errors) {
const str = `Failed to update peripheral ${this.name}: ${error}`;
if (debug.activeDebugConsole) {
debug.activeDebugConsole.appendLine(str);
}
}
} catch (e) {
const msg = (e as Error).message || 'unknown error';
const str = `Failed to update peripheral ${this.name}: ${msg}`;
if (debug.activeDebugConsole) {
debug.activeDebugConsole.appendLine(str);
}
this.updateChildData(null, reject, new Error(str));
});
});
}

private updateChildData(resolve, reject, err: Error) {
if (err) {
return reject(err);
}
}
const promises = this.children.map((r) => r.updateData());
Promise.all(promises)
.then((result) => {
return resolve(true);
})
.catch((err) => {
const msg = err.message || "unknown error";
const str = `Failed to update peripheral ${this.name}: ${msg}`;

try {
const promises = this.children.map((r) => r.updateData());
await Promise.all(promises);
return true;
} catch (e) {
const str = `Internal error: Failed to update peripheral ${this.name} after memory reads`;
if (debug.activeDebugConsole) {
debug.activeDebugConsole.appendLine(str);
debug.activeDebugConsole.appendLine(str);
}
return reject(err ? err : new Error(str));
});
}
return true;
}
}

private readMemory(): Promise<boolean> {
private readMemory(): Promise<Error[]> {
if (!this.currentValue) {
this.currentValue = new Array<number>(this.totalLength);
}
Expand Down
34 changes: 19 additions & 15 deletions src/espIdf/debugAdapter/nodes/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,21 +333,25 @@ export class Register extends PeripheralBaseNode {
const bc = this.size / 8;
const bytes = this.parent.getBytes(this.offset, bc);
const buffer = Buffer.from(bytes);
switch (bc) {
case 1:
this.currentValue = buffer.readUInt8(0);
break;
case 2:
this.currentValue = buffer.readUInt16LE(0);
break;
case 4:
this.currentValue = buffer.readUInt32LE(0);
break;
default:
window.showErrorMessage(
`Register ${this.name} has invalid size: ${this.size}. Should be 8, 16 or 32.`
);
break;
try {
switch (bc) {
case 1:
this.currentValue = buffer.readUInt8(0);
break;
case 2:
this.currentValue = buffer.readUInt16LE(0);
break;
case 4:
this.currentValue = buffer.readUInt32LE(0);
break;
default:
window.showErrorMessage(
`Register ${this.name} has invalid size: ${this.size}. Should be 8, 16 or 32.`
);
break;
}
} catch (error) {
return Promise.reject(error);
}
this.children.forEach((f) => f.updateData());

Expand Down
2 changes: 1 addition & 1 deletion src/espIdf/debugAdapter/peripheralTreeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ export class PeripheralTreeView
public debugContinued() {}

public getActiveDebugSession() {
return debug.activeDebugSession?.type === "espidf"
return debug.activeDebugSession?.type === "gdbtarget"
? debug.activeDebugSession
: null;
}
Expand Down
72 changes: 27 additions & 45 deletions src/espIdf/debugAdapter/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,64 +134,46 @@ export function parseInteger(value: string): number {
return undefined;
}

export function readMemoryChunks(
export async function readMemoryChunks(
session: DebugSession,
startAddr: number,
specs: AddrRange[],
storeTo: number[]
): Promise<boolean> {
const promises = specs.map((r) => {
return new Promise((resolve, reject) => {
const addr = "0x" + r.base.toString(16);
session
.customRequest("readMemory", { memoryReference: addr, count: r.length, offset: 0 })
.then(
(result) => {
let dst = r.base - startAddr;
const bytes = [];
const numBytes = result.data[0].contents.length / 2;
const data = result.data[0].contents;
for (let i = 0, d = 0; i < numBytes; i++, d +=2) {
bytes.push([`${data[d]}${data[d+1]}`]);
}
for (const byte of bytes) {
storeTo[dst++] = byte;
}
resolve(true);
},
(e) => {
let dst = r.base - startAddr;
for (let ix = 0; ix < r.length; ix++) {
storeTo[dst++] = 0xff;
}
reject(e);
}
);
});
});

return new Promise(async (resolve, reject) => {
const results = await Promise.all(promises.map((p) => p.catch((e) => e)));
const errs: string[] = [];
results.map((e) => {
if (e instanceof Error) {
errs.push(e.message);
): Promise<Error[]> {
const errors: Error[] = [];
for (const spec of specs) {
const memoryReference = "0x" + spec.base.toString(16);

try {
const responseBody = await session.customRequest("readMemory", {
memoryReference,
count: spec.length,
});
if (responseBody && responseBody.data) {
const bytes = Buffer.from(responseBody.data, "base64");
let dst = spec.base - startAddr;
for (const byte of bytes) {
storeTo[dst++] = byte;
}
}
});
if (errs.length !== 0) {
reject(new Error(errs.join("\n")));
} else {
resolve(true);
} catch (e) {
const err = e ? e.toString() : "Unknown error";
errors.push(
new Error(
`peripheral-viewer: readMemory failed @ ${memoryReference} for ${spec.length} bytes: ${err}, session=${session.id}`
)
);
}
});
}
return errors;
}

export function readMemory(
session: DebugSession,
startAddr: number,
length: number,
storeTo: number[]
): Promise<boolean> {
): Promise<Error[]> {
const maxChunk = 4 * 1024;
const ranges = splitIntoChunks([new AddrRange(startAddr, length)], maxChunk);
return readMemoryChunks(session, startAddr, ranges, storeTo);
Expand Down
14 changes: 13 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ import { saveDefSdkconfig } from "./espIdf/menuconfig/saveDefConfig";
import { createSBOM, installEspSBOM } from "./espBom";
import { getEspHomeKitSdk } from "./espHomekit/espHomekitDownload";
import { checkDebugAdapterRequirements } from "./espIdf/debugAdapter/checkPyReqs";
import { CDTDebugConfigurationProvider } from "./cdtDebugAdapter/debugConfProvider";

// Global variables shared by commands
let workspaceRoot: vscode.Uri;
Expand Down Expand Up @@ -1322,6 +1323,14 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.debug.registerDebugConfigurationProvider("espidf", debugProvider)
);

const cdtDebugProvider = new CDTDebugConfigurationProvider();
context.subscriptions.push(
vscode.debug.registerDebugConfigurationProvider(
"gdbtarget",
cdtDebugProvider
)
);

vscode.debug.registerDebugAdapterDescriptorFactory("espidf", {
async createDebugAdapterDescriptor(session: vscode.DebugSession) {
try {
Expand Down Expand Up @@ -1391,12 +1400,15 @@ export async function activate(context: vscode.ExtensionContext) {
},
});

vscode.debug.onDidStartDebugSession((session) => {
vscode.debug.onDidStartDebugSession(async (session) => {
const svdFile = idfConf.readParameter(
"idf.svdFilePath",
workspaceRoot
) as string;
peripheralTreeProvider.debugSessionStarted(session, svdFile, 16); // Move svdFile and threshold as conf settings
if (openOCDManager.isRunning() && session.type === "gdbtarget") {
isOpenOCDLaunchedByDebug = true;
}
});

vscode.debug.onDidTerminateDebugSession((session) => {
Expand Down

0 comments on commit d64c530

Please sign in to comment.