Skip to content

Commit bcbbda1

Browse files
authored
Merge pull request #129 from LeszekSwirski/multi-threading
Support multi-threading
2 parents 17fe66d + 8af4b28 commit bcbbda1

File tree

8 files changed

+203
-90
lines changed

8 files changed

+203
-90
lines changed

src/backend/backend.ts

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

14+
export interface Thread {
15+
id: number;
16+
targetId: string;
17+
name?: string;
18+
}
19+
1420
export interface Stack {
1521
level: number;
1622
address: string;
@@ -58,9 +64,10 @@ export interface IBackend {
5864
addBreakPoint(breakpoint: Breakpoint): Thenable<[boolean, Breakpoint]>;
5965
removeBreakPoint(breakpoint: Breakpoint): Thenable<boolean>;
6066
clearBreakPoints(): Thenable<any>;
61-
getStack(maxLevels: number): Thenable<Stack[]>;
67+
getThreads(): Thenable<Thread[]>;
68+
getStack(maxLevels: number, thread: number): Thenable<Stack[]>;
6269
getStackVariables(thread: number, frame: number): Thenable<Variable[]>;
63-
evalExpression(name: string): Thenable<any>;
70+
evalExpression(name: string, thread: number, frame: number): Thenable<any>;
6471
isReady(): boolean;
6572
changeVariable(name: string, rawValue: string): Thenable<any>;
6673
examineMemory(from: number, to: number): Thenable<any>;
@@ -114,12 +121,8 @@ export class VariableObject {
114121
evaluateName: this.name,
115122
value: (this.value === void 0) ? "<unknown>" : this.value,
116123
type: this.type,
117-
// kind: this.displayhint,
118124
variablesReference: this.id
119125
};
120-
if (this.displayhint) {
121-
res.kind = this.displayhint;
122-
}
123126
return res;
124127
}
125128
}

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: 80 additions & 42 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,60 @@ 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;
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 ret : Thread = {
602+
id: parseInt(MINode.valueOf(element, "id")),
603+
targetId: MINode.valueOf(element, "target-id")
604+
};
605+
606+
let name = MINode.valueOf(element, "name");
607+
if (name) {
608+
ret.name = name;
594609
}
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);
610+
611+
return ret;
612+
});
613+
}
614+
615+
async getStack(maxLevels: number, thread: number): Promise<Stack[]> {
616+
if (trace) this.log("stderr", "getStack");
617+
618+
let command = "stack-list-frames";
619+
if (thread != 0) {
620+
command += ` --thread ${thread}`;
621+
}
622+
if (maxLevels) {
623+
command += " 0 " + maxLevels;
624+
}
625+
let result = await this.sendCommand(command);
626+
let stack = result.result("stack");
627+
let ret: Stack[] = [];
628+
return stack.map(element => {
629+
let level = MINode.valueOf(element, "@frame.level");
630+
let addr = MINode.valueOf(element, "@frame.addr");
631+
let func = MINode.valueOf(element, "@frame.func");
632+
let filename = MINode.valueOf(element, "@frame.file");
633+
let file = MINode.valueOf(element, "@frame.fullname");
634+
let line = 0;
635+
let lnstr = MINode.valueOf(element, "@frame.line");
636+
if (lnstr)
637+
line = parseInt(lnstr);
638+
let from = parseInt(MINode.valueOf(element, "@frame.from"));
639+
return {
640+
address: addr,
641+
fileName: filename,
642+
file: file,
643+
function: func || from,
644+
level: level,
645+
line: line
646+
};
620647
});
621648
}
622649

@@ -651,14 +678,17 @@ export class MI2 extends EventEmitter implements IBackend {
651678
});
652679
}
653680

654-
evalExpression(name: string): Thenable<any> {
681+
async evalExpression(name: string, thread: number, frame: number): Promise<MINode> {
655682
if (trace)
656683
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-
});
684+
685+
let command = "data-evaluate-expression ";
686+
if (thread != 0) {
687+
command += `--thread ${thread} --frame ${frame} `;
688+
}
689+
command += name;
690+
691+
return await this.sendCommand(command);
662692
}
663693

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

707-
sendUserInput(command: string): Thenable<any> {
737+
sendUserInput(command: string, threadId: number = 0, frameLevel: number = 0): Thenable<any> {
708738
if (command.startsWith("-")) {
709739
return this.sendCommand(command.substr(1));
710740
}
711741
else {
712-
this.sendRaw(command);
713-
return Promise.resolve(undefined);
742+
return this.sendCliCommand(command, threadId, frameLevel);
714743
}
715744
}
716745

@@ -723,6 +752,15 @@ export class MI2 extends EventEmitter implements IBackend {
723752
this.process.stdin.write(raw + "\n");
724753
}
725754

755+
async sendCliCommand(command: string, threadId: number = 0, frameLevel: number = 0) {
756+
let mi_command = "interpreter-exec ";
757+
if (threadId != 0) {
758+
mi_command += `--thread ${threadId} --frame ${frameLevel} `;
759+
}
760+
mi_command += `console "${command.replace(/[\\"']/g, '\\$&')}"`;
761+
await this.sendCommand(mi_command);
762+
}
763+
726764
sendCommand(command: string, suppressFailure: boolean = false): Thenable<MINode> {
727765
let sel = this.currentToken++;
728766
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)