parent
f4bca2266c
commit
3169fd6f9f
|
@ -19,26 +19,26 @@ const gdbMatch = /(?:\d*|undefined)\(gdb\)/;
|
|||
const numRegex = /\d+/;
|
||||
|
||||
function isPositiveInteger(str: string): boolean {
|
||||
const regex = /^[1-9]\d*$/;
|
||||
return regex.test(str);
|
||||
const regex = /^[1-9]\d*$/;
|
||||
return regex.test(str);
|
||||
}
|
||||
|
||||
export interface ReadMemResults {
|
||||
startAddress: string;
|
||||
endAddress: string;
|
||||
data: string;
|
||||
startAddress: string;
|
||||
endAddress: string;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export function parseReadMemResults(node: MINode): ReadMemResults {
|
||||
const startAddress = node.resultRecords.results[0][1][0][0][1];
|
||||
const endAddress = node.resultRecords.results[0][1][0][2][1];
|
||||
const data = node.resultRecords.results[0][1][0][3][1];
|
||||
const ret: ReadMemResults = {
|
||||
startAddress: startAddress,
|
||||
endAddress: endAddress,
|
||||
data: data
|
||||
};
|
||||
return ret;
|
||||
const startAddress = node.resultRecords.results[0][1][0][0][1];
|
||||
const endAddress = node.resultRecords.results[0][1][0][2][1];
|
||||
const data = node.resultRecords.results[0][1][0][3][1];
|
||||
const ret: ReadMemResults = {
|
||||
startAddress: startAddress,
|
||||
endAddress: endAddress,
|
||||
data: data
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
function couldBeOutput(line: string) {
|
||||
|
@ -173,7 +173,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
if (args.bootstrap) sshCMD = args.bootstrap + " && " + sshCMD;
|
||||
this.sshConn.exec(sshCMD, execArgs, (err, stream) => {
|
||||
if (err) {
|
||||
this.log("stderr", "Could not run " + this.application + "(" + sshCMD +") over ssh!");
|
||||
this.log("stderr", "Could not run " + this.application + "(" + sshCMD + ") over ssh!");
|
||||
if (err === undefined) {
|
||||
err = "<reason unknown>"
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
}
|
||||
|
||||
onOutputStderr(lines) {
|
||||
lines = <string[]> lines.split('\n');
|
||||
lines = <string[]>lines.split('\n');
|
||||
lines.forEach(line => {
|
||||
this.log("stderr", line);
|
||||
});
|
||||
|
@ -352,7 +352,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
}
|
||||
|
||||
onOutput(lines) {
|
||||
lines = <string[]> lines.split('\n');
|
||||
lines = <string[]>lines.split('\n');
|
||||
lines.forEach(line => {
|
||||
if (couldBeOutput(line)) {
|
||||
if (!gdbMatch.exec(line))
|
||||
|
@ -362,11 +362,11 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
if (this.debugOutput)
|
||||
this.log("log", "GDB -> App: " + JSON.stringify(parsed));
|
||||
if (parsed.token !== undefined) {
|
||||
if (this.needOutput[parsed.token] !== undefined) {
|
||||
parsed.output = this.needOutput[parsed.token];
|
||||
}
|
||||
this.nextTokenComing = parsed.token + 1;
|
||||
}
|
||||
if (this.needOutput[parsed.token] !== undefined) {
|
||||
parsed.output = this.needOutput[parsed.token];
|
||||
}
|
||||
this.nextTokenComing = parsed.token + 1;
|
||||
}
|
||||
let handled = false;
|
||||
if (parsed.token !== undefined && parsed.resultRecords) {
|
||||
if (this.handlers[parsed.token]) {
|
||||
|
@ -376,16 +376,16 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
}
|
||||
}
|
||||
if (!handled && parsed.resultRecords && parsed.resultRecords.resultClass == "error") {
|
||||
// this.log("stderr", parsed.result("msg") || line);
|
||||
// this.log("stderr", parsed.result("msg") || line);
|
||||
}
|
||||
if (parsed.outOfBandRecord) {
|
||||
parsed.outOfBandRecord.forEach(record => {
|
||||
if (record.isStream) {
|
||||
if ((record.type === 'console') && (this.needOutput[this.nextTokenComing] !== undefined)) {
|
||||
this.needOutput[this.nextTokenComing] += record.content;
|
||||
} else {
|
||||
this.log(record.type, record.content);
|
||||
}
|
||||
if ((record.type === 'console') && (this.needOutput[this.nextTokenComing] !== undefined)) {
|
||||
this.needOutput[this.nextTokenComing] += record.content;
|
||||
} else {
|
||||
this.log(record.type, record.content);
|
||||
}
|
||||
} else {
|
||||
if (record.type == "exec") {
|
||||
this.emit("exec-async-output", parsed);
|
||||
|
@ -405,55 +405,78 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
} else {
|
||||
if (trace)
|
||||
this.log("stderr", "stop: " + reason);
|
||||
switch (reason) {
|
||||
case "breakpoint-hit":
|
||||
this.emit("breakpoint", parsed);
|
||||
break;
|
||||
case "watchpoint-trigger":
|
||||
case "read-watchpoint-trigger":
|
||||
case "access-watchpoint-trigger":
|
||||
this.emit("watchpoint", parsed);
|
||||
break;
|
||||
case "function-finished":
|
||||
// identical result -> send step-end
|
||||
// this.emit("step-out-end", parsed);
|
||||
// break;
|
||||
case "location-reached":
|
||||
case "end-stepping-range":
|
||||
this.emit("step-end", parsed);
|
||||
break;
|
||||
case "watchpoint-scope":
|
||||
case "solib-event":
|
||||
case "syscall-entry":
|
||||
case "syscall-return":
|
||||
// TODO: inform the user
|
||||
this.emit("step-end", parsed);
|
||||
break;
|
||||
case "fork":
|
||||
case "vfork":
|
||||
case "exec":
|
||||
// TODO: inform the user, possibly add second inferiour
|
||||
this.emit("step-end", parsed);
|
||||
break;
|
||||
case "signal-received":
|
||||
this.emit("signal-stop", parsed);
|
||||
break;
|
||||
case "exited-normally":
|
||||
this.emit("exited-normally", parsed);
|
||||
break;
|
||||
case "exited": // exit with error code != 0
|
||||
this.log("stderr", "Program exited with code " + parsed.record("exit-code"));
|
||||
this.emit("exited-normally", parsed);
|
||||
break;
|
||||
// case "exited-signalled": // consider handling that explicit possible
|
||||
// this.log("stderr", "Program exited because of signal " + parsed.record("signal"));
|
||||
// this.emit("stoped", parsed);
|
||||
// break;
|
||||
|
||||
default:
|
||||
const fnSendMsg = (reason) =>{
|
||||
switch (reason) {
|
||||
case typeof reason == "object":
|
||||
{
|
||||
this.log("console", "Not implemented stop reason (assuming exception): " + reason + `${reason[0]}` + "<");
|
||||
this.emit("stopped", parsed);
|
||||
break;
|
||||
}
|
||||
case "function-finished,breakpoint-hit":
|
||||
case "breakpoint-hit,function-finished":
|
||||
case "breakpoint-hit":
|
||||
this.emit("breakpoint", parsed);
|
||||
break;
|
||||
case "watchpoint-trigger":
|
||||
case "read-watchpoint-trigger":
|
||||
case "access-watchpoint-trigger":
|
||||
this.emit("watchpoint", parsed);
|
||||
break;
|
||||
case "function-finished":
|
||||
// identical result -> send step-end
|
||||
// this.emit("step-out-end", parsed);
|
||||
// break;
|
||||
case "location-reached":
|
||||
case "end-stepping-range":
|
||||
this.emit("step-end", parsed);
|
||||
break;
|
||||
case "watchpoint-scope":
|
||||
case "solib-event":
|
||||
case "syscall-entry":
|
||||
case "syscall-return":
|
||||
// TODO: inform the user
|
||||
this.emit("step-end", parsed);
|
||||
break;
|
||||
case "fork":
|
||||
case "vfork":
|
||||
case "exec":
|
||||
// TODO: inform the user, possibly add second inferiour
|
||||
this.emit("step-end", parsed);
|
||||
break;
|
||||
case "signal-received":
|
||||
this.emit("signal-stop", parsed);
|
||||
break;
|
||||
case "exited-normally":
|
||||
this.emit("exited-normally", parsed);
|
||||
break;
|
||||
case "exited": // exit with error code != 0
|
||||
this.log("stderr", "Program exited with code " + parsed.record("exit-code"));
|
||||
this.emit("exited-normally", parsed);
|
||||
break;
|
||||
// case "exited-signalled": // consider handling that explicit possible
|
||||
// this.log("stderr", "Program exited because of signal " + parsed.record("signal"));
|
||||
// this.emit("stoped", parsed);
|
||||
// break;
|
||||
|
||||
default:
|
||||
this.log("console", "Not implemented stop reason (assuming exception): " + reason + ` ${reason[0]}` + "<");
|
||||
this.emit("stopped", parsed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reason == "object") {
|
||||
reason.forEach(item=>{
|
||||
fnSendMsg(item);
|
||||
})
|
||||
}
|
||||
else if (typeof reason == "string") {
|
||||
fnSendMsg(reason);
|
||||
}
|
||||
else {
|
||||
this.log("console", "Not implemented stop reason (assuming exception): " + reason);
|
||||
this.emit("stopped", parsed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
|
@ -550,7 +573,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
if (trace)
|
||||
this.log("stderr", "next");
|
||||
return new Promise((resolve, reject) => {
|
||||
this.sendCommand((instruction ? 'exec-next-instruction' :'exec-next') + (reverse ? " --reverse" : "")).then((info) => {
|
||||
this.sendCommand((instruction ? 'exec-next-instruction' : 'exec-next') + (reverse ? " --reverse" : "")).then((info) => {
|
||||
resolve(info.resultRecords.resultClass == "running");
|
||||
}, reject);
|
||||
});
|
||||
|
@ -571,6 +594,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
this.log("stderr", "stepOut");
|
||||
return new Promise((resolve, reject) => {
|
||||
this.sendCommand("exec-finish" + (reverse ? " --reverse" : "")).then((info) => {
|
||||
this.log("stderr", `WARNING: stepOutxh++++ '${JSON.stringify(info)}'`);
|
||||
resolve(info.resultRecords.resultClass == "running");
|
||||
}, reject);
|
||||
});
|
||||
|
@ -614,7 +638,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
setLogPoint(bkptNum, command): Thenable<any> {
|
||||
if (trace)
|
||||
this.log("stderr", "setLogoPoint");
|
||||
command = "printf \"log msg:%d\\n\", i" ;
|
||||
command = "printf \"log msg:%d\\n\", i";
|
||||
return this.sendCommand("break-commands " + bkptNum + " " + command);
|
||||
}
|
||||
|
||||
|
@ -633,10 +657,10 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
if (breakpoint.countCondition[0] == ">")
|
||||
location += "-i " + numRegex.exec(breakpoint.countCondition.substr(1))[0] + " ";
|
||||
else {
|
||||
if(!isPositiveInteger(breakpoint.countCondition)) {
|
||||
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");
|
||||
resolve([false, undefined]);
|
||||
return;
|
||||
if (!isPositiveInteger(breakpoint.countCondition)) {
|
||||
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");
|
||||
resolve([false, undefined]);
|
||||
return;
|
||||
}
|
||||
const match = numRegex.exec(breakpoint.countCondition)[0];
|
||||
if (match.length != breakpoint.countCondition.length) {
|
||||
|
@ -651,9 +675,9 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
location += '"' + escape(breakpoint.raw) + '"';
|
||||
else
|
||||
location += '"' + escape(breakpoint.file) + ":" + breakpoint.line + '"';
|
||||
if(breakpoint.logMessage){
|
||||
if (breakpoint.logMessage) {
|
||||
//do send logmsg command;
|
||||
this.log("stderr", "logmsg:"+breakpoint.logMessage);
|
||||
this.log("stderr", "logmsg:" + breakpoint.logMessage);
|
||||
// -break-commands 1 "printf \"log msg:%d\\n\", i" "continue"
|
||||
//this.sendCommand(" -break-commands 1 "printf \"log msg:%d\\n\", i" "continue" ")
|
||||
}
|
||||
|
@ -667,8 +691,8 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
raw: breakpoint.raw,
|
||||
line: parseInt(result.result("bkpt.line")),
|
||||
condition: breakpoint.condition,
|
||||
message:undefined,
|
||||
verified:undefined
|
||||
message: undefined,
|
||||
verified: undefined
|
||||
};
|
||||
if (breakpoint.condition) {
|
||||
this.setBreakPointCondition(bkptNum, breakpoint.condition).then((result) => {
|
||||
|
@ -678,12 +702,12 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
} else {
|
||||
resolve([false, undefined]);
|
||||
}
|
||||
}, (msg)=>{
|
||||
}, (msg) => {
|
||||
newBrk.message = msg;
|
||||
resolve([false, newBrk]);
|
||||
});
|
||||
}
|
||||
else if(breakpoint.logMessage) {
|
||||
}
|
||||
else if (breakpoint.logMessage) {
|
||||
this.setLogPoint(bkptNum, breakpoint.logMessage).then((result) => {
|
||||
if (result.resultRecords.resultClass == "done") {
|
||||
this.breakpoints.set(newBrk, bkptNum);
|
||||
|
@ -704,30 +728,30 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
});
|
||||
}
|
||||
|
||||
addInstrBreakPoint(breakpoint: OurInstructionBreakpoint): Promise<OurInstructionBreakpoint> {
|
||||
if (trace) {
|
||||
this.log('stderr', 'addInstrBreakPoint');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let bkptArgs = '';
|
||||
if (breakpoint.condition) {
|
||||
bkptArgs += `-c "${breakpoint.condition}" `;
|
||||
}
|
||||
addInstrBreakPoint(breakpoint: OurInstructionBreakpoint): Promise<OurInstructionBreakpoint> {
|
||||
if (trace) {
|
||||
this.log('stderr', 'addInstrBreakPoint');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let bkptArgs = '';
|
||||
if (breakpoint.condition) {
|
||||
bkptArgs += `-c "${breakpoint.condition}" `;
|
||||
}
|
||||
|
||||
bkptArgs += '*' + hexFormat(breakpoint.address);
|
||||
bkptArgs += '*' + hexFormat(breakpoint.address);
|
||||
|
||||
this.sendCommand(`break-insert ${bkptArgs}`).then((result) => {
|
||||
if (result.resultRecords.resultClass === 'done') {
|
||||
const bkptNum = parseInt(result.result('bkpt.number'));
|
||||
breakpoint.number = bkptNum;
|
||||
resolve(breakpoint);
|
||||
}
|
||||
else {
|
||||
reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${bkptArgs}`));
|
||||
}
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
this.sendCommand(`break-insert ${bkptArgs}`).then((result) => {
|
||||
if (result.resultRecords.resultClass === 'done') {
|
||||
const bkptNum = parseInt(result.result('bkpt.number'));
|
||||
breakpoint.number = bkptNum;
|
||||
resolve(breakpoint);
|
||||
}
|
||||
else {
|
||||
reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${bkptArgs}`));
|
||||
}
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
removeBreakPoint(breakpoint: Breakpoint): Thenable<boolean> {
|
||||
if (trace)
|
||||
|
@ -790,9 +814,9 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
|
||||
const options: string[] = [];
|
||||
|
||||
if (thread != 0){
|
||||
if (thread != 0) {
|
||||
options.push("--thread " + thread);
|
||||
if (this.status == 'stopped'){
|
||||
if (this.status == 'stopped') {
|
||||
this.currentThreadId = thread;
|
||||
}
|
||||
}
|
||||
|
@ -840,13 +864,13 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
|
||||
async getRegisters(): Promise<any[]> {
|
||||
|
||||
if (!this.regNames || this.regNames.length == 0){
|
||||
if (!this.regNames || this.regNames.length == 0) {
|
||||
const regNameResult = await this.sendCommand(`data-list-register-names`);
|
||||
this.regNames = regNameResult.result("register-names");
|
||||
if (!this.regNames|| this.regNames.length == 0) return [];
|
||||
if (!this.regNames || this.regNames.length == 0) return [];
|
||||
}
|
||||
|
||||
if (this.status == 'stopped' && this.currentThreadId){
|
||||
if (this.status == 'stopped' && this.currentThreadId) {
|
||||
await this.sendCommand(`thread-select ${this.currentThreadId}`);
|
||||
}
|
||||
|
||||
|
@ -866,7 +890,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
const type = MINode.valueOf(reg, "type");
|
||||
ret.push({
|
||||
name: name,
|
||||
evaluateName: '$'+name, //修复寄存器无法watch
|
||||
evaluateName: '$' + name, //修复寄存器无法watch
|
||||
value: value,
|
||||
type: type,
|
||||
variablesReference: 0
|
||||
|
@ -881,8 +905,8 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
if (trace)
|
||||
this.log("stderr", "getStackVariables");
|
||||
|
||||
if (!thread){
|
||||
if (this.status == 'stopped'){
|
||||
if (!thread) {
|
||||
if (this.status == 'stopped') {
|
||||
this.currentThreadId = thread;
|
||||
}
|
||||
}
|
||||
|
@ -920,7 +944,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (this.status != 'stopped') {
|
||||
resolve(this.isRecord ? {result:'0', oldValue:'1'} : { result:'0', oldValue:'0'});
|
||||
resolve(this.isRecord ? { result: '0', oldValue: '1' } : { result: '0', oldValue: '0' });
|
||||
this.log("stderr", `WARNING: The program is ${this.status} and cannot ${this.isRecord ? 'stop' : 'start'} reverse debugging.`);
|
||||
return;
|
||||
}
|
||||
|
@ -928,11 +952,11 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
this.isRecord = !this.isRecord;
|
||||
this.sendCliCommand(this.isRecord ? "record" : "record stop").then((result) => {
|
||||
if (result.resultRecords.resultClass == "done") {
|
||||
resolve(this.isRecord ? {result:'1', oldValue:'0'} : { result:'1', oldValue:'1'});
|
||||
resolve(this.isRecord ? { result: '1', oldValue: '0' } : { result: '1', oldValue: '1' });
|
||||
}
|
||||
else {
|
||||
this.isRecord = !this.isRecord; //restore
|
||||
resolve(this.isRecord ? {result:'0', oldValue:'1'} : { result:'0', oldValue:'0'});
|
||||
resolve(this.isRecord ? { result: '0', oldValue: '1' } : { result: '0', oldValue: '0' });
|
||||
}
|
||||
}, reject);
|
||||
});
|
||||
|
@ -944,7 +968,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
let command = "data-evaluate-expression ";
|
||||
if (thread != 0) {
|
||||
command += `--thread ${thread} --frame ${frame} `;
|
||||
if (this.status == 'stopped'){
|
||||
if (this.status == 'stopped') {
|
||||
this.currentThreadId = thread;
|
||||
}
|
||||
}
|
||||
|
@ -1015,8 +1039,7 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
this.log("log", raw);
|
||||
if (this.isSSH)
|
||||
this.stream.write(raw + "\n");
|
||||
else
|
||||
{
|
||||
else {
|
||||
this.process.stdin.write(raw + "\n");
|
||||
}
|
||||
}
|
||||
|
@ -1038,12 +1061,19 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
delete this.needOutput[sel];
|
||||
if (node && node.resultRecords && node.resultRecords.resultClass === "error") {
|
||||
if (!suppressFailure) {
|
||||
//this.log("stderr", `WARNING: Error executing command '${command}'`);
|
||||
resolve(node);
|
||||
} else
|
||||
this.log("stderr", `WARNING: Error executing command '${command}'`);
|
||||
if(command.includes("exec-finish") && node.resultRecords.results[0][1].includes("not meaningful in the outermost frame")){
|
||||
node.resultRecords.resultClass = "running";
|
||||
node.resultRecords.results = [];
|
||||
}
|
||||
// resolve(node);
|
||||
} else {
|
||||
this.log("stderr", `WARNING: reject '${command}'`);
|
||||
reject(new MIError(node.result("msg") || "Internal error", command));
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
resolve(node);
|
||||
}
|
||||
};
|
||||
this.sendRaw(sel + "-" + command);
|
||||
});
|
||||
|
@ -1058,15 +1088,15 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
return /^-|[^\w\d\/_\-\.]/g.test(text) ? ('"' + escape(text) + '"') : text;
|
||||
}
|
||||
|
||||
isRecord:boolean = false;
|
||||
currentThreadId:number = 0;
|
||||
isRecord: boolean = false;
|
||||
currentThreadId: number = 0;
|
||||
status: 'running' | 'stopped' | 'none' = 'none';
|
||||
cpuArch:string = '';
|
||||
cpuArch: string = '';
|
||||
prettyPrint: boolean = true;
|
||||
printCalls: boolean;
|
||||
debugOutput: boolean;
|
||||
features: string[];
|
||||
regNames:any[] = [];
|
||||
regNames: any[] = [];
|
||||
public procEnv: any;
|
||||
protected isSSH: boolean;
|
||||
protected sshReady: boolean;
|
||||
|
|
Loading…
Reference in New Issue