Support multi-threading
This commit is contained in:
parent
a7fd6a7369
commit
ab0a3bb4be
7 changed files with 173 additions and 90 deletions
|
|
@ -11,6 +11,11 @@ export interface Breakpoint {
|
|||
countCondition?: string;
|
||||
}
|
||||
|
||||
export interface Thread {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Stack {
|
||||
level: number;
|
||||
address: string;
|
||||
|
|
@ -58,9 +63,10 @@ export interface IBackend {
|
|||
addBreakPoint(breakpoint: Breakpoint): Thenable<[boolean, Breakpoint]>;
|
||||
removeBreakPoint(breakpoint: Breakpoint): Thenable<boolean>;
|
||||
clearBreakPoints(): Thenable<any>;
|
||||
getStack(maxLevels: number): Thenable<Stack[]>;
|
||||
getThreads(): Thenable<Thread[]>;
|
||||
getStack(maxLevels: number, thread: number): Thenable<Stack[]>;
|
||||
getStackVariables(thread: number, frame: number): Thenable<Variable[]>;
|
||||
evalExpression(name: string): Thenable<any>;
|
||||
evalExpression(name: string, thread: number, frame: number): Thenable<any>;
|
||||
isReady(): boolean;
|
||||
changeVariable(name: string, rawValue: string): Thenable<any>;
|
||||
examineMemory(from: number, to: number): Thenable<any>;
|
||||
|
|
@ -114,12 +120,8 @@ export class VariableObject {
|
|||
evaluateName: this.name,
|
||||
value: (this.value === void 0) ? "<unknown>" : this.value,
|
||||
type: this.type,
|
||||
// kind: this.displayhint,
|
||||
variablesReference: this.id
|
||||
};
|
||||
if (this.displayhint) {
|
||||
res.kind = this.displayhint;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const variableRegex = /^[a-zA-Z_\-][a-zA-Z0-9_\-]*/;
|
|||
const errorRegex = /^\<.+?\>/;
|
||||
const referenceStringRegex = /^(0x[0-9a-fA-F]+\s*)"/;
|
||||
const referenceRegex = /^0x[0-9a-fA-F]+/;
|
||||
const cppReferenceRegex = /^@0x[0-9a-fA-F]+/;
|
||||
const nullpointerRegex = /^0x0+\b/;
|
||||
const charRegex = /^(\d+) ['"]/;
|
||||
const numberRegex = /^\d+(\.\d+)?/;
|
||||
|
|
@ -168,6 +169,10 @@ export function expandValue(variableCreate: Function, value: string, root: strin
|
|||
primitive = "*" + match[0];
|
||||
value = value.substr(match[0].length).trim();
|
||||
}
|
||||
else if (match = cppReferenceRegex.exec(value)) {
|
||||
primitive = match[0];
|
||||
value = value.substr(match[0].length).trim();
|
||||
}
|
||||
else if (match = charRegex.exec(value)) {
|
||||
primitive = match[1];
|
||||
value = value.substr(match[0].length - 1);
|
||||
|
|
@ -222,19 +227,21 @@ export function expandValue(variableCreate: Function, value: string, root: strin
|
|||
ref = variableCreate(val);
|
||||
val = "Object";
|
||||
}
|
||||
if (typeof val == "string" && val.startsWith("*0x")) {
|
||||
if (extra && MINode.valueOf(extra, "arg") == "1")
|
||||
{
|
||||
else if (typeof val == "string" && val.startsWith("*0x")) {
|
||||
if (extra && MINode.valueOf(extra, "arg") == "1") {
|
||||
ref = variableCreate(getNamespace("*(" + name), { arg: true });
|
||||
val = "<args>";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ref = variableCreate(getNamespace("*" + name));
|
||||
val = "Object@" + val;
|
||||
}
|
||||
}
|
||||
if (typeof val == "string" && val.startsWith("<...>")) {
|
||||
else if (typeof val == "string" && val.startsWith("@0x")) {
|
||||
ref = variableCreate(getNamespace("*&" + name.substr));
|
||||
val = "Ref" + val;
|
||||
}
|
||||
else if (typeof val == "string" && val.startsWith("<...>")) {
|
||||
ref = variableCreate(getNamespace(name));
|
||||
val = "...";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Breakpoint, IBackend, Stack, SSHArguments, Variable, VariableObject, MIError } from "../backend"
|
||||
import { Breakpoint, IBackend, Thread, Stack, SSHArguments, Variable, VariableObject, MIError } from "../backend"
|
||||
import * as ChildProcess from "child_process"
|
||||
import { EventEmitter } from "events"
|
||||
import { parseMI, MINode } from '../mi_parse';
|
||||
|
|
@ -365,6 +365,12 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
}
|
||||
} else
|
||||
this.log("log", JSON.stringify(parsed));
|
||||
} else if (record.type == "notify") {
|
||||
if (record.asyncClass == "thread-created") {
|
||||
this.emit("thread-created", parsed);
|
||||
} else if (record.asyncClass == "thread-exited") {
|
||||
this.emit("thread-exited", parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -584,39 +590,55 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
});
|
||||
}
|
||||
|
||||
getStack(maxLevels: number): Thenable<Stack[]> {
|
||||
if (trace)
|
||||
this.log("stderr", "getStack");
|
||||
return new Promise((resolve, reject) => {
|
||||
let command = "stack-list-frames";
|
||||
if (maxLevels) {
|
||||
command += " 0 " + maxLevels;
|
||||
}
|
||||
this.sendCommand(command).then((result) => {
|
||||
let stack = result.result("stack");
|
||||
let ret: Stack[] = [];
|
||||
stack.forEach(element => {
|
||||
let level = MINode.valueOf(element, "@frame.level");
|
||||
let addr = MINode.valueOf(element, "@frame.addr");
|
||||
let func = MINode.valueOf(element, "@frame.func");
|
||||
let filename = MINode.valueOf(element, "@frame.file");
|
||||
let file = MINode.valueOf(element, "@frame.fullname");
|
||||
let line = 0;
|
||||
let lnstr = MINode.valueOf(element, "@frame.line");
|
||||
if (lnstr)
|
||||
line = parseInt(lnstr);
|
||||
let from = parseInt(MINode.valueOf(element, "@frame.from"));
|
||||
ret.push({
|
||||
address: addr,
|
||||
fileName: filename,
|
||||
file: file,
|
||||
function: func || from,
|
||||
level: level,
|
||||
line: line
|
||||
});
|
||||
});
|
||||
resolve(ret);
|
||||
}, reject);
|
||||
async getThreads(): Promise<Thread[]> {
|
||||
if (trace) this.log("stderr", "getThreads");
|
||||
|
||||
let command = "thread-info";
|
||||
let result = await this.sendCommand(command);
|
||||
let threads = result.result("threads");
|
||||
let ret: Thread[] = [];
|
||||
return threads.map(element => {
|
||||
let id = parseInt(MINode.valueOf(element, "id"));
|
||||
let name = MINode.valueOf(element, "name") + "";
|
||||
return {
|
||||
id,
|
||||
name
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getStack(maxLevels: number, thread: number): Promise<Stack[]> {
|
||||
if (trace) this.log("stderr", "getStack");
|
||||
|
||||
let command = "stack-list-frames";
|
||||
if (thread != 0) {
|
||||
command += ` --thread ${thread}`;
|
||||
}
|
||||
if (maxLevels) {
|
||||
command += " 0 " + maxLevels;
|
||||
}
|
||||
let result = await this.sendCommand(command);
|
||||
let stack = result.result("stack");
|
||||
let ret: Stack[] = [];
|
||||
return stack.map(element => {
|
||||
let level = MINode.valueOf(element, "@frame.level");
|
||||
let addr = MINode.valueOf(element, "@frame.addr");
|
||||
let func = MINode.valueOf(element, "@frame.func");
|
||||
let filename = MINode.valueOf(element, "@frame.file");
|
||||
let file = MINode.valueOf(element, "@frame.fullname");
|
||||
let line = 0;
|
||||
let lnstr = MINode.valueOf(element, "@frame.line");
|
||||
if (lnstr)
|
||||
line = parseInt(lnstr);
|
||||
let from = parseInt(MINode.valueOf(element, "@frame.from"));
|
||||
return {
|
||||
address: addr,
|
||||
fileName: filename,
|
||||
file: file,
|
||||
function: func || from,
|
||||
level: level,
|
||||
line: line
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -651,14 +673,17 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
});
|
||||
}
|
||||
|
||||
evalExpression(name: string): Thenable<any> {
|
||||
async evalExpression(name: string, thread: number, frame: number): Promise<MINode> {
|
||||
if (trace)
|
||||
this.log("stderr", "evalExpression");
|
||||
return new Promise((resolve, reject) => {
|
||||
this.sendCommand("data-evaluate-expression " + name).then((result) => {
|
||||
resolve(result);
|
||||
}, reject);
|
||||
});
|
||||
|
||||
let command = "data-evaluate-expression ";
|
||||
if (thread != 0) {
|
||||
command += `--thread ${thread} --frame ${frame} `;
|
||||
}
|
||||
command += name;
|
||||
|
||||
return await this.sendCommand(command);
|
||||
}
|
||||
|
||||
async varCreate(expression: string, name: string = "-"): Promise<VariableObject> {
|
||||
|
|
@ -704,13 +729,12 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
this.emit("msg", type, msg[msg.length - 1] == '\n' ? msg : (msg + "\n"));
|
||||
}
|
||||
|
||||
sendUserInput(command: string): Thenable<any> {
|
||||
sendUserInput(command: string, threadId: number = 0, frameLevel: number = 0): Thenable<any> {
|
||||
if (command.startsWith("-")) {
|
||||
return this.sendCommand(command.substr(1));
|
||||
}
|
||||
else {
|
||||
this.sendRaw(command);
|
||||
return Promise.resolve(undefined);
|
||||
return this.sendCliCommand(command, threadId, frameLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -723,6 +747,15 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
this.process.stdin.write(raw + "\n");
|
||||
}
|
||||
|
||||
async sendCliCommand(command: string, threadId: number = 0, frameLevel: number = 0) {
|
||||
let mi_command = "interpreter-exec ";
|
||||
if (threadId != 0) {
|
||||
mi_command += `--thread ${threadId} --frame ${frameLevel} `;
|
||||
}
|
||||
mi_command += `console "${command.replace(/[\\"']/g, '\\$&')}"`;
|
||||
await this.sendCommand(mi_command);
|
||||
}
|
||||
|
||||
sendCommand(command: string, suppressFailure: boolean = false): Thenable<MINode> {
|
||||
let sel = this.currentToken++;
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Stack } from "../backend"
|
|||
import { MINode } from "../mi_parse"
|
||||
|
||||
export class MI2_Mago extends MI2_LLDB {
|
||||
getStack(maxLevels: number): Thenable<Stack[]> {
|
||||
getStack(maxLevels: number, thread: number): Promise<Stack[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let command = "stack-list-frames";
|
||||
this.sendCommand(command).then((result) => {
|
||||
|
|
|
|||
|
|
@ -205,8 +205,10 @@ export function parseMI(output: string): MINode {
|
|||
let oldContent = output;
|
||||
let canBeValueList = output[0] == '[';
|
||||
output = output.substr(1);
|
||||
if (output[0] == '}' || output[0] == ']')
|
||||
if (output[0] == '}' || output[0] == ']') {
|
||||
output = output.substr(1); // ] or }
|
||||
return [];
|
||||
}
|
||||
if (canBeValueList) {
|
||||
let value = parseValue();
|
||||
if (value) { // is value list
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue