Skip to content

Commit 39aebe4

Browse files
author
Haneef Mohammed
committed
Merge branch 'master' of https://github.com/Marus/cortex-debug
2 parents 0428a2b + 4b73034 commit 39aebe4

File tree

22 files changed

+2831
-7109
lines changed

22 files changed

+2831
-7109
lines changed

.vscode/launch.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"name": "Debug Server",
1919
"type": "node",
2020
"request": "launch",
21-
2221
"runtimeArgs": [ "--nolazy", "--trace-warnings" ],
2322
"program": "${workspaceRoot}/dist/debugadapter.js",
2423
"stopOnEntry": false,

TODO.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ Low, medium, high are importance/usefulness. But they sometimes represent *bang-
1616
# Other
1717

1818
* High: WSL: First class support. This includes Docker, WSL and perhaps 'ssh'. VSCode and WSL seem to be maturing. Still not there but...there may be enough
19-
*
19+
* Low: Live Debug: See if we can update program status without stopping the program. Not sure what will work and with which gdb-server
20+
* invasive method: program is periodically paused and updates provided -- it has the look of live but is invasive for sure
21+
* non-invasive (non-stop): Update without pausing the program. I should say minimally invasive.

package-lock.json

Lines changed: 2024 additions & 6941 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 266 additions & 15 deletions
Large diffs are not rendered by default.

src/backend/backend.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ export interface Breakpoint {
1111
number?: number;
1212
}
1313

14+
export interface DataBreakpoint {
15+
exp: string;
16+
accessType: 'read' | 'write' | 'readWrite';
17+
condition?: string;
18+
countCondition?: string;
19+
number?: number;
20+
}
21+
1422
export interface Stack {
1523
level: number;
1624
address: string;
@@ -59,8 +67,10 @@ export class VariableObject {
5967
public hasMore: boolean;
6068
public id: number;
6169
public fullExp: string;
70+
public parent: number; // Variable Reference
6271
public children: {[name: string]: string};
63-
constructor(node: any) {
72+
constructor(p: number, node: any) {
73+
this.parent = p;
6474
this.name = MINode.valueOf(node, 'name');
6575
this.exp = MINode.valueOf(node, 'exp');
6676
this.numchild = parseInt(MINode.valueOf(node, 'numchild'));
@@ -86,7 +96,7 @@ export class VariableObject {
8696
val = parseInt(value.toLowerCase());
8797

8898
ret += ' ' + name + ';\n' ;
89-
ret += toStringDecHexOctBin(val);
99+
ret += toStringDecHexOctBin(val);
90100
}
91101
return ret;
92102
}

src/backend/mi2/mi2.ts

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Breakpoint, IBackend, Stack, Variable, VariableObject, MIError } from '../backend';
1+
import { Breakpoint, DataBreakpoint, IBackend, Stack, Variable, VariableObject, MIError } from '../backend';
22
import * as ChildProcess from 'child_process';
33
import { EventEmitter } from 'events';
44
import { parseMI, MINode } from '../mi_parse';
@@ -28,8 +28,7 @@ function couldBeOutput(line: string) {
2828
const trace = false;
2929

3030
export class MI2 extends EventEmitter implements IBackend {
31-
public printCalls: boolean;
32-
public debugOutput: boolean;
31+
public debugOutput: boolean | 'raw' | 'raw-only';
3332
public procEnv: any;
3433
protected currentToken: number = 1;
3534
protected handlers: { [index: number]: (info: MINode) => any } = {};
@@ -140,7 +139,12 @@ export class MI2 extends EventEmitter implements IBackend {
140139
else {
141140
const parsed = parseMI(line);
142141
if (this.debugOutput) {
143-
this.log('log', 'GDB -> App: ' + JSON.stringify(parsed));
142+
if ((this.debugOutput === 'raw-only') || (this.debugOutput === 'raw')) {
143+
this.log('log', '-> ' + line);
144+
}
145+
if (this.debugOutput !== 'raw-only') {
146+
this.log('log', 'GDB -> App: ' + JSON.stringify(parsed));
147+
}
144148
}
145149
let handled = false;
146150
if (parsed.token !== undefined && parsed.resultRecords) {
@@ -174,6 +178,13 @@ export class MI2 extends EventEmitter implements IBackend {
174178
if (reason === 'breakpoint-hit') {
175179
this.emit('breakpoint', parsed);
176180
}
181+
else if (reason && (reason as string).includes('watchpoint-trigger')) {
182+
this.emit('watchpoint', parsed);
183+
}
184+
else if (reason && (reason as string).includes('watchpoint-scope')) {
185+
// When a local variable goes out of scope
186+
this.emit('watchpoint-scope', parsed);
187+
}
177188
else if (reason === 'end-stepping-range') {
178189
this.emit('step-end', parsed);
179190
}
@@ -244,7 +255,7 @@ export class MI2 extends EventEmitter implements IBackend {
244255
}
245256

246257
private tryKill() {
247-
if (!this.exited) {
258+
if (!this.exited && this.process) {
248259
const proc = this.process;
249260
try {
250261
ServerConsoleLog('GDB kill()');
@@ -274,10 +285,31 @@ export class MI2 extends EventEmitter implements IBackend {
274285
if (trace) {
275286
this.log('stderr', 'detach');
276287
}
277-
this.sendCommand('target-detach'); // This may not be successful, go ahead and stop gdb as well. Sometimes hangs!
278-
setTimeout(() => {
288+
let to = setTimeout(() => {
289+
if (to) {
290+
ServerConsoleLog('target-detach hung: target probably running, thats okay, continue to stop()');
291+
to = null;
292+
this.stop();
293+
}
294+
}, 10);
295+
296+
// Following can hang if no response, or fail because the target is still running. Yes,
297+
// we sometimes detach when target is still running. This also causes unhandled rejection
298+
// warning/error from Node, so handle rejections.
299+
this.sendCommand('target-detach').then(() => {
300+
if (to) {
301+
clearTimeout(to);
302+
to = null;
303+
}
279304
this.stop();
280-
}, 5);
305+
}, (e) => {
306+
if (to) {
307+
clearTimeout(to);
308+
to = null;
309+
}
310+
ServerConsoleLog('target-detach failed: target probably running, thats okay, continue to stop()');
311+
this.stop();
312+
});
281313
}
282314

283315
public interrupt(arg: string = ''): Thenable<boolean> {
@@ -473,6 +505,57 @@ export class MI2 extends EventEmitter implements IBackend {
473505
});
474506
}
475507

508+
public addDataBreakPoint(breakpoint: DataBreakpoint): Promise<DataBreakpoint> {
509+
if (trace) {
510+
this.log('stderr', 'addBreakPoint');
511+
}
512+
return new Promise((resolve, reject) => {
513+
let location = '';
514+
if (breakpoint.countCondition) {
515+
if (breakpoint.countCondition[0] === '>') {
516+
location += '-i ' + numRegex.exec(breakpoint.countCondition.substr(1))[0] + ' ';
517+
}
518+
else {
519+
const match = numRegex.exec(breakpoint.countCondition)[0];
520+
if (match.length !== breakpoint.countCondition.length) {
521+
// tslint:disable-next-line:max-line-length
522+
this.log('stderr', 'Unsupported break count expression: \'' + breakpoint.countCondition + '\'. Only supports \'X\' for breaking once after X times or \'>X\' for ignoring the first X breaks');
523+
location += '-t ';
524+
}
525+
else if (parseInt(match) !== 0) {
526+
location += '-t -i ' + parseInt(match) + ' ';
527+
}
528+
}
529+
}
530+
531+
location += breakpoint.exp;
532+
const aType = breakpoint.accessType === 'read' ? '-r' : (breakpoint.accessType === 'readWrite' ? '-a' : '');
533+
this.sendCommand(`break-watch ${aType} ${location}`).then((result) => {
534+
if (result.resultRecords.resultClass === 'done') {
535+
const bkptNum = parseInt(result.result('bkpt.number'));
536+
const line = result.result('bkpt.line');
537+
breakpoint.number = bkptNum;
538+
539+
if (breakpoint.condition) {
540+
this.setBreakPointCondition(bkptNum, breakpoint.condition).then((result) => {
541+
if (result.resultRecords.resultClass === 'done') {
542+
resolve(breakpoint);
543+
} else {
544+
reject(new MIError(result.result('msg') || 'Internal error', 'Setting breakpoint condition'));
545+
}
546+
}, reject);
547+
}
548+
else {
549+
resolve(breakpoint);
550+
}
551+
}
552+
else {
553+
reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${location}`));
554+
}
555+
}, reject);
556+
});
557+
}
558+
476559
public getFrame(thread: number, frame: number): Thenable<Stack> {
477560
return new Promise((resolve, reject) => {
478561
const command = `stack-info-frame --thread ${thread} --frame ${frame}`;
@@ -600,7 +683,7 @@ export class MI2 extends EventEmitter implements IBackend {
600683
x: 'hexadecimal'
601684
};
602685

603-
public async varCreate(expression: string, name: string = '-', scope: string = '@'): Promise<VariableObject> {
686+
public async varCreate(parent: number, expression: string, name: string = '-', scope: string = '@'): Promise<VariableObject> {
604687
if (trace) {
605688
this.log('stderr', 'varCreate');
606689
}
@@ -623,7 +706,7 @@ export class MI2 extends EventEmitter implements IBackend {
623706
if (overrideVal) {
624707
result = result.map((r: string[]) => r[0] === 'value' ? ['value', overrideVal] : r);
625708
}
626-
return new VariableObject(result);
709+
return new VariableObject(parent, result);
627710
}
628711

629712
public async varEvalExpression(name: string): Promise<MINode> {
@@ -633,7 +716,7 @@ export class MI2 extends EventEmitter implements IBackend {
633716
return this.sendCommand(`var-evaluate-expression ${name}`);
634717
}
635718

636-
public async varListChildren(name: string, flattenAnonymous: boolean): Promise<VariableObject[]> {
719+
public async varListChildren(parent: number, name: string, flattenAnonymous: boolean): Promise<VariableObject[]> {
637720
if (trace) {
638721
this.log('stderr', 'varListChildren');
639722
}
@@ -642,9 +725,9 @@ export class MI2 extends EventEmitter implements IBackend {
642725
const children = res.result('children') || [];
643726
const omg: VariableObject[] = [];
644727
for (const item of children) {
645-
const child = new VariableObject(item[1]);
728+
const child = new VariableObject(parent, item[1]);
646729
if (flattenAnonymous && child.exp.startsWith('<anonymous ')) {
647-
omg.push(... await this.varListChildren(child.name, flattenAnonymous));
730+
omg.push(... await this.varListChildren(parent, child.name, flattenAnonymous));
648731
} else {
649732
omg.push(child);
650733
}
@@ -692,7 +775,7 @@ export class MI2 extends EventEmitter implements IBackend {
692775
}
693776

694777
public sendRaw(raw: string) {
695-
if (this.printCalls || trace) {
778+
if (this.debugOutput || trace) {
696779
this.log('log', raw);
697780
}
698781
if (raw.includes('undefined')) {

src/backend/server.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import * as fs from 'fs';
55
import { EventEmitter } from 'events';
66
import { setTimeout } from 'timers';
77

8-
const tmpDirName = os.platform() === 'win32' ? process.env.TEMP || process.env.TMP || '.' : '/tmp';
98
export function ServerConsoleLog(str: string) {
109
try {
10+
const tmpDirName = os.tmpdir();
1111
const date = new Date();
1212
str = `[${date.toISOString()}] ` + str;
1313
console.log(str);
@@ -85,10 +85,12 @@ export class GDBServer extends EventEmitter {
8585
}
8686

8787
private exitTimeout: NodeJS.Timeout = null;
88+
private killInProgress = false;
8889
public exit(): void {
89-
if (this.process) {
90+
if (this.process && !this.killInProgress) {
9091
try {
9192
ServerConsoleLog('GDBServer: forcing an exit with kill()');
93+
this.killInProgress = true;
9294
this.process.kill();
9395
}
9496
catch (e) {

src/backend/symbols.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class SymbolTable {
6969
* showed that it is both complex and very slow.
7070
*/
7171

72-
public loadSymbols(noCache: boolean = false) {
72+
public loadSymbols(noCache: boolean = false, useObjdump: string = '') {
7373
try {
7474
let objdumpExePath = os.platform() !== 'win32' ? `${this.toolchainPrefix}-objdump` : `${this.toolchainPrefix}-objdump.exe`;
7575
if (this.toolchainPath) {
@@ -85,14 +85,15 @@ export class SymbolTable {
8585
options.push('-C');
8686
}
8787

88-
const tmpName = tmp.tmpNameSync();
89-
const outFd = fs.openSync(tmpName, 'w');
90-
const objdump = childProcess.spawnSync(objdumpExePath, [...options, this.executable], {
91-
stdio: ['ignore', outFd, 'ignore']
92-
});
93-
fs.closeSync(outFd);
94-
95-
const str = this.readLinesAndFileMaps(tmpName, !restored);
88+
if (!useObjdump || !fs.existsSync(useObjdump)) {
89+
useObjdump = tmp.tmpNameSync();
90+
const outFd = fs.openSync(useObjdump, 'w');
91+
const objdump = childProcess.spawnSync(objdumpExePath, [...options, this.executable], {
92+
stdio: ['ignore', outFd, 'ignore']
93+
});
94+
fs.closeSync(outFd);
95+
}
96+
const str = this.readLinesAndFileMaps(useObjdump, !restored);
9697

9798
const regex = RegExp(SYMBOL_REGEX);
9899
let currentFile: string = null;

src/common.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { GDBServer } from './backend/server';
66

77
export enum CortexDebugKeys {
88
REGISTER_DISPLAY_MODE = 'registerUseNaturalFormat',
9-
VARIABLE_DISPLAY_MODE = 'variableUseNaturalFormat'
9+
VARIABLE_DISPLAY_MODE = 'variableUseNaturalFormat',
10+
SERVER_LOG_FILE_NAME = 'dbgServerLogfile'
1011
}
1112

1213
export enum NumberFormat {
@@ -189,7 +190,7 @@ export interface ConfigurationArguments extends DebugProtocol.LaunchRequestArgum
189190
rttConfig: RTTConfiguration;
190191
swoConfig: SWOConfiguration;
191192
graphConfig: any[];
192-
showDevDebugOutput: boolean;
193+
showDevDebugOutput: boolean | 'raw' | 'raw-only';
193194
showDevDebugTimestamps: boolean;
194195
cwd: string;
195196
extensionPath: string;

0 commit comments

Comments
 (0)