Skip to content

Commit ab0a3bb

Browse files
committed
Support multi-threading
1 parent a7fd6a7 commit ab0a3bb

File tree

7 files changed

+173
-90
lines changed

7 files changed

+173
-90
lines changed

src/backend/backend.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ export interface Breakpoint {
1111
countCondition?: string;
1212
}
1313

14+
export interface Thread {
15+
id: number;
16+
name: string;
17+
}
18+
1419
export interface Stack {
1520
level: number;
1621
address: string;
@@ -58,9 +63,10 @@ export interface IBackend {
5863
addBreakPoint(breakpoint: Breakpoint): Thenable<[boolean, Breakpoint]>;
5964
removeBreakPoint(breakpoint: Breakpoint): Thenable<boolean>;
6065
clearBreakPoints(): Thenable<any>;
61-
getStack(maxLevels: number): Thenable<Stack[]>;
66+
getThreads(): Thenable<Thread[]>;
67+
getStack(maxLevels: number, thread: number): Thenable<Stack[]>;
6268
getStackVariables(thread: number, frame: number): Thenable<Variable[]>;
63-
evalExpression(name: string): Thenable<any>;
69+
evalExpression(name: string, thread: number, frame: number): Thenable<any>;
6470
isReady(): boolean;
6571
changeVariable(name: string, rawValue: string): Thenable<any>;
6672
examineMemory(from: number, to: number): Thenable<any>;
@@ -114,12 +120,8 @@ export class VariableObject {
114120
evaluateName: this.name,
115121
value: (this.value === void 0) ? "<unknown>" : this.value,
116122
type: this.type,
117-
// kind: this.displayhint,
118123
variablesReference: this.id
119124
};
120-
if (this.displayhint) {
121-
res.kind = this.displayhint;
122-
}
123125
return res;
124126
}
125127
}

src/backend/gdb_expansion.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const variableRegex = /^[a-zA-Z_\-][a-zA-Z0-9_\-]*/;
55
const errorRegex = /^\<.+?\>/;
66
const referenceStringRegex = /^(0x[0-9a-fA-F]+\s*)"/;
77
const referenceRegex = /^0x[0-9a-fA-F]+/;
8+
const cppReferenceRegex = /^@0x[0-9a-fA-F]+/;
89
const nullpointerRegex = /^0x0+\b/;
910
const charRegex = /^(\d+) ['"]/;
1011
const numberRegex = /^\d+(\.\d+)?/;
@@ -168,6 +169,10 @@ export function expandValue(variableCreate: Function, value: string, root: strin
168169
primitive = "*" + match[0];
169170
value = value.substr(match[0].length).trim();
170171
}
172+
else if (match = cppReferenceRegex.exec(value)) {
173+
primitive = match[0];
174+
value = value.substr(match[0].length).trim();
175+
}
171176
else if (match = charRegex.exec(value)) {
172177
primitive = match[1];
173178
value = value.substr(match[0].length - 1);
@@ -222,19 +227,21 @@ export function expandValue(variableCreate: Function, value: string, root: strin
222227
ref = variableCreate(val);
223228
val = "Object";
224229
}
225-
if (typeof val == "string" && val.startsWith("*0x")) {
226-
if (extra && MINode.valueOf(extra, "arg") == "1")
227-
{
230+
else if (typeof val == "string" && val.startsWith("*0x")) {
231+
if (extra && MINode.valueOf(extra, "arg") == "1") {
228232
ref = variableCreate(getNamespace("*(" + name), { arg: true });
229233
val = "<args>";
230234
}
231-
else
232-
{
235+
else {
233236
ref = variableCreate(getNamespace("*" + name));
234237
val = "Object@" + val;
235238
}
236239
}
237-
if (typeof val == "string" && val.startsWith("<...>")) {
240+
else if (typeof val == "string" && val.startsWith("@0x")) {
241+
ref = variableCreate(getNamespace("*&" + name.substr));
242+
val = "Ref" + val;
243+
}
244+
else if (typeof val == "string" && val.startsWith("<...>")) {
238245
ref = variableCreate(getNamespace(name));
239246
val = "...";
240247
}

src/backend/mi2/mi2.ts

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Breakpoint, IBackend, Stack, SSHArguments, Variable, VariableObject, MIError } from "../backend"
1+
import { Breakpoint, IBackend, Thread, Stack, SSHArguments, Variable, VariableObject, MIError } from "../backend"
22
import * as ChildProcess from "child_process"
33
import { EventEmitter } from "events"
44
import { parseMI, MINode } from '../mi_parse';
@@ -365,6 +365,12 @@ export class MI2 extends EventEmitter implements IBackend {
365365
}
366366
} else
367367
this.log("log", JSON.stringify(parsed));
368+
} else if (record.type == "notify") {
369+
if (record.asyncClass == "thread-created") {
370+
this.emit("thread-created", parsed);
371+
} else if (record.asyncClass == "thread-exited") {
372+
this.emit("thread-exited", parsed);
373+
}
368374
}
369375
}
370376
});
@@ -584,39 +590,55 @@ export class MI2 extends EventEmitter implements IBackend {
584590
});
585591
}
586592

587-
getStack(maxLevels: number): Thenable<Stack[]> {
588-
if (trace)
589-
this.log("stderr", "getStack");
590-
return new Promise((resolve, reject) => {
591-
let command = "stack-list-frames";
592-
if (maxLevels) {
593-
command += " 0 " + maxLevels;
594-
}
595-
this.sendCommand(command).then((result) => {
596-
let stack = result.result("stack");
597-
let ret: Stack[] = [];
598-
stack.forEach(element => {
599-
let level = MINode.valueOf(element, "@frame.level");
600-
let addr = MINode.valueOf(element, "@frame.addr");
601-
let func = MINode.valueOf(element, "@frame.func");
602-
let filename = MINode.valueOf(element, "@frame.file");
603-
let file = MINode.valueOf(element, "@frame.fullname");
604-
let line = 0;
605-
let lnstr = MINode.valueOf(element, "@frame.line");
606-
if (lnstr)
607-
line = parseInt(lnstr);
608-
let from = parseInt(MINode.valueOf(element, "@frame.from"));
609-
ret.push({
610-
address: addr,
611-
fileName: filename,
612-
file: file,
613-
function: func || from,
614-
level: level,
615-
line: line
616-
});
617-
});
618-
resolve(ret);
619-
}, reject);
593+
async getThreads(): Promise<Thread[]> {
594+
if (trace) this.log("stderr", "getThreads");
595+
596+
let command = "thread-info";
597+
let result = await this.sendCommand(command);
598+
let threads = result.result("threads");
599+
let ret: Thread[] = [];
600+
return threads.map(element => {
601+
let id = parseInt(MINode.valueOf(element, "id"));
602+
let name = MINode.valueOf(element, "name") + "";
603+
return {
604+
id,
605+
name
606+
};
607+
});
608+
}
609+
610+
async getStack(maxLevels: number, thread: number): Promise<Stack[]> {
611+
if (trace) this.log("stderr", "getStack");
612+
613+
let command = "stack-list-frames";
614+
if (thread != 0) {
615+
command += ` --thread ${thread}`;
616+
}
617+
if (maxLevels) {
618+
command += " 0 " + maxLevels;
619+
}
620+
let result = await this.sendCommand(command);
621+
let stack = result.result("stack");
622+
let ret: Stack[] = [];
623+
return stack.map(element => {
624+
let level = MINode.valueOf(element, "@frame.level");
625+
let addr = MINode.valueOf(element, "@frame.addr");
626+
let func = MINode.valueOf(element, "@frame.func");
627+
let filename = MINode.valueOf(element, "@frame.file");
628+
let file = MINode.valueOf(element, "@frame.fullname");
629+
let line = 0;
630+
let lnstr = MINode.valueOf(element, "@frame.line");
631+
if (lnstr)
632+
line = parseInt(lnstr);
633+
let from = parseInt(MINode.valueOf(element, "@frame.from"));
634+
return {
635+
address: addr,
636+
fileName: filename,
637+
file: file,
638+
function: func || from,
639+
level: level,
640+
line: line
641+
};
620642
});
621643
}
622644

@@ -651,14 +673,17 @@ export class MI2 extends EventEmitter implements IBackend {
651673
});
652674
}
653675

654-
evalExpression(name: string): Thenable<any> {
676+
async evalExpression(name: string, thread: number, frame: number): Promise<MINode> {
655677
if (trace)
656678
this.log("stderr", "evalExpression");
657-
return new Promise((resolve, reject) => {
658-
this.sendCommand("data-evaluate-expression " + name).then((result) => {
659-
resolve(result);
660-
}, reject);
661-
});
679+
680+
let command = "data-evaluate-expression ";
681+
if (thread != 0) {
682+
command += `--thread ${thread} --frame ${frame} `;
683+
}
684+
command += name;
685+
686+
return await this.sendCommand(command);
662687
}
663688

664689
async varCreate(expression: string, name: string = "-"): Promise<VariableObject> {
@@ -704,13 +729,12 @@ export class MI2 extends EventEmitter implements IBackend {
704729
this.emit("msg", type, msg[msg.length - 1] == '\n' ? msg : (msg + "\n"));
705730
}
706731

707-
sendUserInput(command: string): Thenable<any> {
732+
sendUserInput(command: string, threadId: number = 0, frameLevel: number = 0): Thenable<any> {
708733
if (command.startsWith("-")) {
709734
return this.sendCommand(command.substr(1));
710735
}
711736
else {
712-
this.sendRaw(command);
713-
return Promise.resolve(undefined);
737+
return this.sendCliCommand(command, threadId, frameLevel);
714738
}
715739
}
716740

@@ -723,6 +747,15 @@ export class MI2 extends EventEmitter implements IBackend {
723747
this.process.stdin.write(raw + "\n");
724748
}
725749

750+
async sendCliCommand(command: string, threadId: number = 0, frameLevel: number = 0) {
751+
let mi_command = "interpreter-exec ";
752+
if (threadId != 0) {
753+
mi_command += `--thread ${threadId} --frame ${frameLevel} `;
754+
}
755+
mi_command += `console "${command.replace(/[\\"']/g, '\\$&')}"`;
756+
await this.sendCommand(mi_command);
757+
}
758+
726759
sendCommand(command: string, suppressFailure: boolean = false): Thenable<MINode> {
727760
let sel = this.currentToken++;
728761
return new Promise((resolve, reject) => {

src/backend/mi2/mi2mago.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Stack } from "../backend"
33
import { MINode } from "../mi_parse"
44

55
export class MI2_Mago extends MI2_LLDB {
6-
getStack(maxLevels: number): Thenable<Stack[]> {
6+
getStack(maxLevels: number, thread: number): Promise<Stack[]> {
77
return new Promise((resolve, reject) => {
88
let command = "stack-list-frames";
99
this.sendCommand(command).then((result) => {

src/backend/mi_parse.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,10 @@ export function parseMI(output: string): MINode {
205205
let oldContent = output;
206206
let canBeValueList = output[0] == '[';
207207
output = output.substr(1);
208-
if (output[0] == '}' || output[0] == ']')
208+
if (output[0] == '}' || output[0] == ']') {
209+
output = output.substr(1); // ] or }
209210
return [];
211+
}
210212
if (canBeValueList) {
211213
let value = parseValue();
212214
if (value) { // is value list

src/mago.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum
3232

3333
class MagoDebugSession extends MI2DebugSession {
3434
public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false) {
35-
super(debuggerLinesStartAt1, isServer, 0);
35+
super(debuggerLinesStartAt1, isServer);
3636
}
3737

3838
protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {

0 commit comments

Comments
 (0)