Skip to content

Commit 005bc28

Browse files
committed
Added watchpoints
1 parent 1ff878c commit 005bc28

File tree

8 files changed

+89
-29
lines changed

8 files changed

+89
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# ChangeLog
22

33
# V1.13.0-pre8
4-
* Added `hardwareBreakpoints: {required: boolean, limit: numer}` to debug configuration. You can force all breakpoints to use hardware breakpoints. You can also ask cortex-debug to limit the number such breakpoints or you can let gdb or your gdb-server enforce a limit. Please use `hardwareBreakpoints` with caution as in most cases it is better to let gdb decide which type of breakpoint to use based on memory types while making sure you are not over the limit -- this is especially true for devices with large SRAMs where you can have near inifite breakpoints and not even have a performance penalty. HW breakpoints are not inherently better than SW breakpoints so long as GDB can read/write to memory locations.
4+
* Added `hardwareBreakpoints: {require: boolean, limit: numer}` to debug configuration. You can force all breakpoints to use hardware breakpoints. You can also ask cortex-debug to limit the number such breakpoints or you can let gdb or your gdb-server enforce a limit. Please use `hardwareBreakpoints` with caution as in most cases it is better to let gdb decide which type of breakpoint to use based on memory types while making sure you are not over the limit -- this is especially true for devices with large SRAMs where you can have near inifite breakpoints and not even have a performance penalty. HW breakpoints are not inherently better than SW breakpoints so long as GDB can read/write to memory locations.
5+
* Also added `hardwareWatchpoints` similar to `hardwareBreakpoints`. Note however that GDB always prefers to use hardware resources for this as doing it in software, while possible, is super slow and unusable.
56

67
# V1.13.0-pre7
78
* Added `overridePreEndSessionCommands` to control how a session ends. If the session does not end your way, we sill end the session the normal way

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
![Visual Studio Code with Cortex-Debug Installed](./images/vs-code-screenshot.png)
44

5-
Debugging support for ARM Cortex-M Microcontrollers with the following features:
5+
Debugging support for ARM Cortex-M (and others) Microcontrollers with the following features:
66

7+
* While we cannot change the extension name due to VSCode Marketplace rules, this extension can be used by non Cortex-M devices and non ARM devices. People have reported using it with Cortex-R/A, Xtensa, RISC-V and even x86. Your mileage may vary.
78
* Highly configurable. See https://github.com/Marus/cortex-debug/blob/master/debug_attributes.md
89
* Support J-Link, OpenOCD GDB Server, STMicroelectronic's ST-LINK GDB server, pyOCD and the Black Magic Probe
910
* Partial support textane/stlink (st-util) GDB Servers (SWO can only be captured via a serial port)

debug_attributes.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ If the type is marked as `{...}` it means that it is a complex item can have mul
3838
| graphConfig | {object} | Both | Description of how graphing can be done. See our Wiki for details |
3939
| hardwareBreakpoints | object | Both | WARNING: Force only HW breakpoints to be used. By default GDB will use HW or SW breakpoints depending on memory type. Use this only in rare circumstances to work around issues in your gdb-server or hardware. This setting is NOT recommended for general use. |
4040
| hardwareBreakpoints<br>.limit | number | Both | If limit > 0, enforce a limit on the number of hardware breakpoints that can be used |
41-
| hardwareBreakpoints<br>.required | boolean | Both | If true, forces the use of hardware breakpoints |
41+
| hardwareBreakpoints<br>.require | boolean | Both | If true, forces the use of hardware breakpoints |
42+
| hardwareWatchpoints | object | Both | WARNING: Force only HW watchpoints to be used. By default GDB will use HW or SW watchpoints depending on memory type. Use this only in rare circumstances |
43+
| hardwareWatchpoints<br>.limit | number | Both | If limit > 0, enforce a limit on the number of hardware watchpoints that can be used |
44+
| hardwareWatchpoints<br>.require | boolean | Both | If true, forces the use of hardware watchpoints |
4245
| interface | string | Both | Debug Interface type to use for connections (defaults to SWD) - Used for J-Link, ST-LINK and BMP probes. |
4346
| ipAddress | string | Both | IP Address for networked J-Link Adapter |
4447
| jlinkscript | string | Both | J-Link script file - optional input file for customizing J-Link actions. |

package.json

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@
804804
"description": "WARNING: Force only HW breakpoints to be used. By default GDB will use HW or SW breakpoints depending on memory type. Use this only in rare circumstances to work around issues in your gdb-server or hardware. This setting is NOT recommended for general use.",
805805
"type": "object",
806806
"properties": {
807-
"required": {
807+
"require": {
808808
"type": "boolean",
809809
"default": false,
810810
"description": "If true, forces the use of hardware breakpoints"
@@ -818,6 +818,24 @@
818818
}
819819
}
820820
},
821+
"hardwareWatchpoints": {
822+
"description": "WARNING: Force only HW watchpoints to be used. By default GDB will use HW or SW watchpoints depending on memory type. Use this only in rare circumstances",
823+
"type": "object",
824+
"properties": {
825+
"require": {
826+
"type": "boolean",
827+
"default": false,
828+
"description": "If true, forces the use of hardware watchpoints"
829+
},
830+
"limit": {
831+
"type": "number",
832+
"minimum": 0,
833+
"multipleOf": 1,
834+
"default": 0,
835+
"description": "If limit > 0, enforce a limit on the number of hardware watchpoints that can be used"
836+
}
837+
}
838+
},
821839
"liveWatch": {
822840
"description": "An object with parameters for Live Watch",
823841
"properties": {
@@ -1972,7 +1990,7 @@
19721990
"description": "WARNING: Force only HW breakpoints to be used. By default GDB will use HW or SW breakpoints depending on memory type. Use this only in rare circumstances to work around issues in your gdb-server or hardware. This setting is NOT recommended for general use.",
19731991
"type": "object",
19741992
"properties": {
1975-
"required": {
1993+
"require": {
19761994
"type": "boolean",
19771995
"default": false,
19781996
"description": "If true, forces the use of hardware breakpoints"
@@ -1986,6 +2004,24 @@
19862004
}
19872005
}
19882006
},
2007+
"hardwareWatchpoints": {
2008+
"description": "WARNING: Force only HW watchpoints to be used. By default GDB will use HW or SW watchpoints depending on memory type. Use this only in rare circumstances",
2009+
"type": "object",
2010+
"properties": {
2011+
"require": {
2012+
"type": "boolean",
2013+
"default": false,
2014+
"description": "If true, forces the use of hardware watchpoints"
2015+
},
2016+
"limit": {
2017+
"type": "number",
2018+
"minimum": 0,
2019+
"multipleOf": 1,
2020+
"default": 0,
2021+
"description": "If limit > 0, enforce a limit on the number of hardware watchpoints that can be used"
2022+
}
2023+
}
2024+
},
19892025
"liveWatch": {
19902026
"description": "An object with parameters for Live Watch",
19912027
"properties": {

src/backend/symbols.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -487,15 +487,15 @@ export class SymbolTable {
487487
});
488488
this.objdumpReader.on('exit', (code, signal) => {
489489
if (code !== 0) {
490-
this.gdbSession.handleMsg('log', `'objdump' exited with a nonzero exit status ${code}, ${signal}\n`);
490+
this.gdbSession.handleMsg('log', `'objdump' exited with a nonzero exit status ${code}, ${signal}. File: ${executable}\n`);
491491
}
492492
});
493493
this.objdumpReader.on('close', (code, signal) => {
494494
this.objdumpReader = undefined;
495495
this.currentObjDumpFile = null;
496496
if (trace || this.gdbSession.args.showDevDebugOutput) {
497497
const ms = Date.now() - objdumpStart;
498-
this.gdbSession.handleMsg('log', `Finished reading symbols from objdump: Time: ${ms} ms\n`);
498+
this.gdbSession.handleMsg('log', `Finished reading symbols from objdump: Time: ${ms} ms. File: ${executable}\n`);
499499
}
500500
});
501501

@@ -531,13 +531,13 @@ export class SymbolTable {
531531
});
532532
nmReader.on('exit', (code, signal) => {
533533
if (code !== 0) {
534-
this.gdbSession.handleMsg('log', `'nm' exited with a nonzero exit status ${code}, ${signal}\n`);
534+
this.gdbSession.handleMsg('log', `'nm' exited with a nonzero exit status ${code}, ${signal}. File: ${executable}\n`);
535535
}
536536
});
537537
nmReader.on('close', () => {
538538
if (trace || this.gdbSession.args.showDevDebugOutput) {
539539
const ms = Date.now() - nmStart;
540-
this.gdbSession.handleMsg('log', `Finished reading symbols from nm: Time: ${ms} ms\n`);
540+
this.gdbSession.handleMsg('log', `Finished reading symbols from nm: Time: ${ms} ms. File: ${executable}\n`);
541541
}
542542
});
543543

src/common.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,15 @@ export function defSymbolFile(file: string): SymbolFile {
253253
}
254254

255255
export interface HWBreakpointInfo {
256-
required: boolean;
257-
limit: number;
256+
require: boolean;
257+
limit?: number;
258258
}
259+
260+
export interface HWWatchpointInfo {
261+
require: boolean;
262+
limit?: number;
263+
}
264+
259265
export interface ConfigurationArguments extends DebugProtocol.LaunchRequestArguments {
260266
name: string;
261267
request: string;
@@ -312,7 +318,7 @@ export interface ConfigurationArguments extends DebugProtocol.LaunchRequestArgum
312318
variableUseNaturalFormat: boolean;
313319
chainedConfigurations: ChainedConfigurations;
314320
hardwareBreakpoints: HWBreakpointInfo;
315-
321+
hardwareWatchpoints: HWWatchpointInfo;
316322
pvtIsReset: boolean;
317323
pvtPorts: { [name: string]: number };
318324
pvtParent: ConfigurationArguments;

src/gdb.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class HWBreakpointMgr {
6565
private readonly forceHwBreakpoints: boolean = false) {
6666
}
6767

68-
areHwBreakpointsForced(): boolean {
68+
isHWForced(): boolean {
6969
return this.forceHwBreakpoints;
7070
}
7171

@@ -99,9 +99,8 @@ export class HWBreakpointMgr {
9999
this.breakpointSet.clear();
100100
}
101101

102-
// This should only be called when the limiter has not been reached
103102
getGdbMiArg(bptType: HWBreakpointType): string {
104-
if (this.forceHwBreakpoints && !this.limitReached()) {
103+
if (this.forceHwBreakpoints) {
105104
return (bptType === 'data') ? '' : '-h';
106105
}
107106
return '';
@@ -278,6 +277,7 @@ export class GDBDebugSession extends LoggingDebugSession {
278277
protected currentThreadId: number = 0;
279278
protected activeThreadIds = new Set<number>(); // Used for consistency check
280279
protected hwBreakpointMgr: HWBreakpointMgr;
280+
protected hwWatchpointMgr: HWBreakpointMgr;
281281

282282
/**
283283
* If we are requested a major switch like restart/disconnect/detach we may have to interrupt the
@@ -419,17 +419,12 @@ export class GDBDebugSession extends LoggingDebugSession {
419419
args.showDevDebugOutput = ADAPTER_DEBUG_MODE.RAW;
420420
}
421421
this.args = this.normalizeArguments(args);
422-
const forceHW = this.args.hardwareBreakpoints?.required || false;
423-
const limit = this.args.hardwareBreakpoints?.limit || 0;
424-
this.hwBreakpointMgr = new HWBreakpointMgr(limit, forceHW);
425422

426423
this.handleMsg('stdout',
427424
`Cortex-Debug: VSCode debugger extension version ${args.pvtVersion} git(${__COMMIT_HASH__}). `
428425
+ 'Usage info: https://github.com/Marus/cortex-debug#usage');
429-
if (forceHW) {
430-
const limitMsg = (limit > 0) ? `Limit of ${limit} breakpoints will be enforced by cortex-debug` : 'GDB enforces any limits you may have configured';
431-
this.handleMsg('stderr', `INFO: All breakpoints will be requested as hardware breakpoints. ${limitMsg}\n`);
432-
}
426+
427+
this.setHWBreakpointInfo();
433428

434429
if (this.args.showDevDebugOutput) {
435430
this.handleMsg('log', '"configuration": ' + JSON.stringify(args, undefined, 4) + '\n');
@@ -461,6 +456,23 @@ export class GDBDebugSession extends LoggingDebugSession {
461456
}
462457
}
463458

459+
private setHWBreakpointInfo() {
460+
let forceHW = this.args.hardwareBreakpoints?.require || false;
461+
let limit = this.args.hardwareBreakpoints?.limit || 0;
462+
this.hwBreakpointMgr = new HWBreakpointMgr(limit, forceHW);
463+
if (forceHW) {
464+
const limitMsg = (limit > 0) ? `Limit of ${limit} breakpoints will be enforced by cortex-debug` : 'GDB enforces any limits you may have configured';
465+
this.handleMsg('stderr', `INFO: All breakpoints will be requested as hardware breakpoints. ${limitMsg}\n`);
466+
}
467+
forceHW = this.args.hardwareWatchpoints?.require || false;
468+
limit = this.args.hardwareWatchpoints?.limit || 0;
469+
this.hwWatchpointMgr = new HWBreakpointMgr(limit, forceHW);
470+
if (forceHW && (limit > 0)) {
471+
const limitMsg = `Limit of ${limit} watchpoints will be enforced by cortex-debug`;
472+
this.handleMsg('stderr', `INFO: All watchpoints are done by GDB as hardware watchpoints (default). ${limitMsg}\n`);
473+
}
474+
}
475+
464476
public isDebugLoggingAvailable() {
465477
return (this.debugLogFd >= 0);
466478
}
@@ -728,7 +740,7 @@ export class GDBDebugSession extends LoggingDebugSession {
728740
let feedbackTimer = setInterval(() => {
729741
const svrName = this.serverController.name || this.args.servertype;
730742
elapsed += 5;
731-
this.handleMsg('stdout', `Still waiting for ${svrName} GDB Server to be ready, ${elapsed} seconds...\n`);
743+
this.handleMsg('stdout', `Still waiting for ${svrName} GDB Server to be ready, ${elapsed} seconds...looking for '${initMatch}'\n`);
732744
}, 5 * 1000);
733745

734746
this.serverController.serverLaunchStarted();
@@ -2188,23 +2200,24 @@ export class GDBDebugSession extends LoggingDebugSession {
21882200
resolve: (value: any) => void;
21892201
}[] = [];
21902202

2191-
protected async addBptSync<T>(func: () => Promise<T>, obj: any): Promise<T | MIError> {
2203+
protected async addBptSync<T>(func: () => Promise<T>, obj: any, isData: boolean = false): Promise<T | MIError> {
21922204
return new Promise<T | MIError>(async (resolve) => {
2205+
const mgr = isData ? this.hwWatchpointMgr : this.hwBreakpointMgr;
21932206
this.addBptSyncQueue.push({ func: func, obj: obj, resolve: resolve });
21942207
while (this.addBptSyncRunning) {
21952208
await new Promise((resolve) => setTimeout(resolve, 0));
21962209
}
21972210
this.addBptSyncRunning = true;
21982211
while (this.addBptSyncQueue.length > 0) {
21992212
const item = this.addBptSyncQueue.shift();
2200-
if (this.hwBreakpointMgr.limitReached()) {
2213+
if (mgr.limitReached()) {
22012214
item.resolve(this.reportHwBreakptLimit(obj));
22022215
} else {
22032216
try {
22042217
const ret = await item.func() as T;
22052218
if (ret && !(ret instanceof MIError)) {
22062219
const brk = ret as { number: number };
2207-
this.hwBreakpointMgr.addBreakpoint(brk?.number || -1);
2220+
mgr.addBreakpoint(brk?.number || -1);
22082221
}
22092222
item.resolve(ret);
22102223
} catch (err) {
@@ -2556,7 +2569,7 @@ export class GDBDebugSession extends LoggingDebugSession {
25562569
const createBreakpoints = async () => {
25572570
try {
25582571
const currentBreakpoints = Array.from(this.dataBreakpointMap.keys());
2559-
this.hwBreakpointMgr.removeBreakpoints(currentBreakpoints);
2572+
this.hwWatchpointMgr.removeBreakpoints(currentBreakpoints);
25602573
this.dataBreakpointMap.clear();
25612574

25622575
// It is not clear how gdb implements. It has to use the DWT in the Cortex-M for hardware data
@@ -2572,7 +2585,7 @@ export class GDBDebugSession extends LoggingDebugSession {
25722585
const bkp: OurDataBreakpoint = { ...brk };
25732586
all.push(this.addBptSync<OurDataBreakpoint | MIError>(() => {
25742587
return this.miDebugger.addDataBreakPoint(bkp);
2575-
}, bkp));
2588+
}, bkp, true));
25762589
});
25772590

25782591
const brkpoints = await Promise.all(all);

src/pyocd.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export class PyOCDServerController extends EventEmitter implements GDBServerCont
130130
}
131131

132132
public initMatch(): RegExp {
133-
return /GDB server started (at|on) port/;
133+
return /GDB server (listening|started) (at|on) port/;
134134
}
135135

136136
public serverLaunchStarted(): void {}

0 commit comments

Comments
 (0)