1、解决堆栈切换时main函数变量不显示问题

This commit is contained in:
xuhong 2024-06-18 11:24:37 +08:00
parent b331b94217
commit ec11595591
4 changed files with 192 additions and 160 deletions

View File

@ -41,6 +41,11 @@ export interface Variable {
raw?: any;
}
export interface RegisterValue {
index: number;
value: string;
}
export interface SSHArguments {
forwardX11: boolean;
host: string;

View File

@ -32,10 +32,6 @@ export function isExpandable(value: string): number {
else return 0;
}
import * as fs from "fs";
import * as os from "os";
function isJSON(str) {
var jList = [];
try {
@ -50,9 +46,7 @@ import * as os from "os";
}
}
export function expandValue(variableCreate: Function, value: string, root: string = "", extra: any = undefined): any {
let jsonVar = false;
if(value.indexOf("=")!=-1){
jsonVar = true;
@ -64,7 +58,7 @@ export function expandValue(variableCreate: Function, value: string, root: strin
let stringEnd = 1;
let inString = true;
const charStr = value[0];
let remaining = value.substr(1);
let remaining = value.substring(1);
let escaped = false;
while (inString) {
if (escaped)
@ -77,13 +71,15 @@ export function expandValue(variableCreate: Function, value: string, root: strin
remaining = remaining.substr(1);
stringEnd++;
}
const str = value.substr(0, stringEnd).trim();
value = value.substr(stringEnd).trim();
const str = value.substring(0, stringEnd).trim();
value = value.substring(stringEnd).trim();
return str;
};
const stack = [root];
let parseValue, parseCommaResult, parseCommaValue, parseResult, createValue;
// let parseValue, parseCommaResult, parseCommaValue, parseResult, createValue;
let parseValue: () => any, parseCommaResult: (pushToStack: boolean) => any, parseCommaValue: () => any, parseResult: (pushToStack: boolean) => any, createValue: (name: string, val: any) => any;
let variable = "";
const getNamespace = (variable) => {
@ -99,15 +95,15 @@ export function expandValue(variableCreate: Function, value: string, root: strin
if (namespace) {
while (name.startsWith("*")) {
prefix += "*";
name = name.substr(1);
name = name.substring(1);
}
namespace = namespace + pointerCombineChar + name;
} else
if (root && root.startsWith("*"))
namespace = name.substr(1);
else {
namespace = name;
}
} else if (root && root.startsWith("*")){
namespace = name.substring(1);
}
else {
namespace = name;
}
}
}
});
@ -121,19 +117,19 @@ export function expandValue(variableCreate: Function, value: string, root: strin
if (value[0] != '{')
return undefined;
const oldContent = value;
value = value.substr(1).trim();
value = value.substring(1).trim();
if (value[0] == '}') {
value = value.substr(1).trim();
value = value.substring(1).trim();
return [];
}
if (value[0] == '<') {
value = value.substr(1).trim();
value = value.substring(1).trim();
}
if (value.startsWith("...")) {
value = value.substr(3).trim();
value = value.substring(3).trim();
if (value[0] == '}') {
value = value.substr(1).trim();
value = value.substring(1).trim();
return <any> "<...>";
}
}
@ -153,7 +149,7 @@ export function expandValue(variableCreate: Function, value: string, root: strin
values.push(createValue("[0]", val));
const remaining = value;
let i = 0;
while (true) {
for (;;) {
stack.push("[" + (++i) + "]");
if (!(val = parseCommaValue())) {
stack.pop();
@ -162,7 +158,7 @@ export function expandValue(variableCreate: Function, value: string, root: strin
stack.pop();
values.push(createValue("[" + i + "]", val));
}
value = value.substr(1).trim(); // }
value = value.substring(1).trim(); // }
return values;
}
@ -172,13 +168,13 @@ export function expandValue(variableCreate: Function, value: string, root: strin
results.push(result);
while (result = parseCommaResult(true))
results.push(result);
value = value.substr(1).trim(); // }
value = value.substring(1).trim(); // }
return results;
}
else if(nullTupleRegex.exec(value)){
var match=nullTupleRegex.exec(value);
value = value.substr(match[1].length).trim();
value = value.substring(match[1].length).trim();
const values = [];
stack.push("[0]");
let val = match[1];
@ -201,38 +197,38 @@ export function expandValue(variableCreate: Function, value: string, root: strin
primitive = undefined;
else if (value.startsWith("true")) {
primitive = "true";
value = value.substr(4).trim();
value = value.substring(4).trim();
} else if (value.startsWith("false")) {
primitive = "false";
value = value.substr(5).trim();
value = value.substring(5).trim();
} else if (match = nullpointerRegex.exec(value)) {
primitive = "<nullptr>";
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else if (match = referenceStringRegex.exec(value)) {
value = value.substr(match[1].length).trim();
value = value.substring(match[1].length).trim();
primitive = parseCString();
} else if (match = referenceRegexQt.exec(value)) {
primitive = "*" + match[0];
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else if (match = referenceRegex.exec(value)) {
primitive = "*" + match[0];
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else if (match = cppReferenceRegex.exec(value)) {
primitive = match[0];
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else if (match = charRegex.exec(value)) {
primitive = match[1];
value = value.substr(match[0].length - 1);
value = value.substring(match[0].length - 1);
primitive += " " + parseCString();
} else if (match = numberRegex.exec(value)) {
primitive = match[0];
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else if (match = variableRegex.exec(value)) {
primitive = match[0];
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else if (match = errorRegex.exec(value)) {
primitive = match[0];
value = value.substr(match[0].length).trim();
value = value.substring(match[0].length).trim();
} else {
primitive = value;
}
@ -274,7 +270,7 @@ export function expandValue(variableCreate: Function, value: string, root: strin
{
return undefined;
}
value = value.substr(variableMatch[0].length).trim();
value = value.substring(variableMatch[0].length).trim();
var name = variable = variableMatch[1];
if (name.charAt(name.length - 1) === ">") {
@ -288,7 +284,6 @@ export function expandValue(variableCreate: Function, value: string, root: strin
if (pushToStack){
stack.pop();
}
return createValue(name, val);
};
@ -364,7 +359,5 @@ export function expandValue(variableCreate: Function, value: string, root: strin
value = value.trim();
var retval = parseValue()
return retval;
return parseValue();
}

View File

@ -1,4 +1,4 @@
import { Breakpoint, OurInstructionBreakpoint, IBackend, Thread, Stack, SSHArguments, Variable, VariableObject, MIError } from "../backend";
import { Breakpoint, OurInstructionBreakpoint, IBackend, Thread, Stack, SSHArguments, Variable, RegisterValue, VariableObject, MIError } from "../backend";
import * as ChildProcess from "child_process";
import { EventEmitter } from "events";
import { parseMI, MINode } from '../mi_parse';
@ -13,7 +13,6 @@ import * as os from 'os';
export function escape(str: string) {
return str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
}
const nonOutput = /^(?:\d*|undefined)[\*\+\=]|[\~\@\&\^]/;
const gdbMatch = /(?:\d*|undefined)\(gdb\)/;
const numRegex = /\d+/;
@ -931,43 +930,54 @@ export class MI2 extends EventEmitter implements IBackend {
async getRegisters(): Promise<any[]> {
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.status == 'stopped' && this.currentThreadId) {
await this.sendCommand(`thread-select ${this.currentThreadId}`);
}
const result = await this.sendCommand(`data-list-register-values x`);
const regValues = result.result("register-values");
if (trace)
this.log("stderr", `getRegisters: ${this.regNames.length} ${regValues.length}`);
var ret = [];
let reg_index = 0;
for (let name_index = 0; name_index < this.regNames.length; name_index++) {
const name = this.regNames[name_index];
if (!name || name == '') continue;
const reg = regValues[reg_index];
const value = MINode.valueOf(reg, "value");
const type = MINode.valueOf(reg, "type");
// Getting register names and values are separate GDB commands.
// We first retrieve the register names and then the values.
// The register names should never change, so we could cache and reuse them,
// but for now we just retrieve them every time to keep it simple.
const names = await this.getRegisterNames();
const values = await this.getRegisterValues();
const ret: Variable[] = [];
for (const val of values) {
const key = names[val.index];
const value = val.value;
const type = "string";
ret.push({
name: name,
evaluateName: '$' + name, //修复寄存器无法watch
value: value,
type: type,
variablesReference: 0
name: key,
valueStr: value,
type: type
});
reg_index++;
}
return ret;
}
async getRegisterNames(): Promise<string[]> {
if (trace)
this.log("stderr", "getRegisterNames");
const result = await this.sendCommand("data-list-register-names");
const names = result.result('register-names');
if (!Array.isArray(names)) {
throw new Error('Failed to retrieve register names.');
}
return names.map(name => name.toString());
}
async getRegisterValues(): Promise<RegisterValue[]> {
if (trace)
this.log("stderr", "getRegisterValues");
const result = await this.sendCommand("data-list-register-values N");
const nodes = result.result('register-values');
if (!Array.isArray(nodes)) {
throw new Error('Failed to retrieve register values.');
}
const ret: RegisterValue[] = nodes.map(node => {
const index = parseInt(MINode.valueOf(node, "number"));
const value = MINode.valueOf(node, "value");
return { index: index, value: value };
});
return ret;
}
async getStackVariables(thread: number, frame: number): Promise<Variable[]> {
if (trace)
this.log("stderr", "getStackVariables");
@ -1043,10 +1053,14 @@ export class MI2 extends EventEmitter implements IBackend {
return await this.sendCommand(command, suppressFailure);
}
async varCreate(expression: string, name: string = "-", frame: string = "@"): Promise<VariableObject> {
async varCreate(threadId: number, frameLevel: number, expression: string, name: string = "-", frame: string = "@"): Promise<VariableObject> {
if (trace)
this.log("stderr", "varCreate");
const res = await this.sendCommand(`var-create ${this.quote(name)} ${frame} "${expression}"`);
let miCommand = "var-create ";
if (threadId != 0) {
miCommand += `--thread ${threadId} --frame ${frameLevel}`;
}
const res = await this.sendCommand(`${miCommand} ${this.quote(name)} ${frame} "${expression}"`);
return new VariableObject(res.result(""));
}

View File

@ -22,10 +22,21 @@ export enum RunCommand { CONTINUE, RUN, NONE }
const STACK_HANDLES_START = 1000;
const VAR_HANDLES_START = 512 * 256 + 1000;
const VARIABLES_TAG_REGISTER = 0xab;
const logger = require('./backend/log');
class VariableScope {
constructor(public readonly name: string, public readonly threadId: number, public readonly level: number) {
}
public static variableName(handle: number, name: string): string {
return `var_${handle}_${name}`;
}
}
export class MI2DebugSession extends DebugSession {
protected variableHandles = new Handles<string | VariableObject | ExtendedVariable>(VAR_HANDLES_START);
protected variableHandles = new Handles<VariableScope | string | VariableObject | ExtendedVariable>();
protected variableHandlesReverse: { [id: string]: number } = {};
protected scopeHandlesReverse: { [key: string]: number } = {};
protected useVarObjects: boolean;
protected quit: boolean;
protected attached: boolean;
@ -185,8 +196,10 @@ export class MI2DebugSession extends DebugSession {
try {
if (this.useVarObjects) {
let name = args.name;
if (args.variablesReference >= VAR_HANDLES_START) {
const parent = this.variableHandles.get(args.variablesReference) as VariableObject;
const parent = this.variableHandles.get(args.variablesReference);
if (parent instanceof VariableScope) {
name = VariableScope.variableName(args.variablesReference, name);
} else if (parent instanceof VariableObject) {
name = `${parent.name}.${name}`;
}
@ -466,8 +479,24 @@ export class MI2DebugSession extends DebugSession {
protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
const scopes = new Array<Scope>();
scopes.push(new Scope("Local", STACK_HANDLES_START + (parseInt(args.frameId as any) || 0), false));
scopes.push(new Scope("Registers", VARIABLES_TAG_REGISTER, false));
const [threadId, level] = this.frameIdToThreadAndLevel(args.frameId);
const createScope = (scopeName: string, expensive: boolean): Scope => {
const key: string = scopeName + ":" + threadId + ":" + level;
let handle: number;
if (this.scopeHandlesReverse.hasOwnProperty(key)) {
handle = this.scopeHandlesReverse[key];
} else {
handle = this.variableHandles.create(new VariableScope(scopeName, threadId, level));
this.scopeHandlesReverse[key] = handle;
}
return new Scope(scopeName, handle, expensive);
};
scopes.push(createScope("Locals", false));
scopes.push(createScope("Registers", false));
response.body = {
scopes: scopes
};
@ -476,25 +505,10 @@ export class MI2DebugSession extends DebugSession {
protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise<void> {
const variables: DebugProtocol.Variable[] = [];
let id: number | string | VariableObject | ExtendedVariable;
if (args.variablesReference == VARIABLES_TAG_REGISTER) {
var results = await this.miDebugger.getRegisters();
response.body = {
variables: results
};
this.sendResponse(response);
return;
}
if (args.variablesReference < VAR_HANDLES_START) {
id = args.variablesReference - STACK_HANDLES_START;
} else {
id = this.variableHandles.get(args.variablesReference);
}
const createVariable = (arg, options?) => {
const id: VariableScope | string | VariableObject | ExtendedVariable = this.variableHandles.get(args.variablesReference);
const createVariable = (arg: string | VariableObject, options?: any) => {
if (options)
return this.variableHandles.create(new ExtendedVariable(arg, options));
return this.variableHandles.create(new ExtendedVariable(typeof arg === 'string' ? arg : arg.name, options));
else
return this.variableHandles.create(arg);
};
@ -510,70 +524,77 @@ export class MI2DebugSession extends DebugSession {
return varObj.isCompound() ? id : 0;
};
if (typeof id == "number") {
let stack: Variable[];
logger.debug(`variablesRequest: typeof id:${typeof id}`);
if (id instanceof VariableScope) {
try {
const [threadId, level] = this.frameIdToThreadAndLevel(id);
stack = await this.miDebugger.getStackVariables(threadId, level);
for (const variable of stack) {
if (this.useVarObjects) {
try {
const varObjName = `var_${id}_${variable.name}`;
let varObj: VariableObject;
if (id.name == "Registers") {
const registers = await this.miDebugger.getRegisters();
for (const reg of registers) {
variables.push({
name: reg.name,
value: reg.valueStr,
variablesReference: 0
});
}
} else {
const stack: Variable[] = await this.miDebugger.getStackVariables(id.threadId, id.level);
for (const variable of stack) {
if (this.useVarObjects) {
try {
const changes = await this.miDebugger.varUpdate(varObjName);
const changelist = changes.result("changelist");
changelist.forEach((change) => {
const name = MINode.valueOf(change, "name");
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change);
});
const varId = this.variableHandlesReverse[varObjName];
varObj = this.variableHandles.get(varId) as any;
} catch (err) {
if (err instanceof MIError && err.message == "Variable object not found") {
varObj = await this.miDebugger.varCreate(variable.name, varObjName);
const varId = findOrCreateVariable(varObj);
varObj.exp = variable.name;
varObj.id = varId;
} else {
throw err;
const varObjName = VariableScope.variableName(args.variablesReference, variable.name);
let varObj: VariableObject;
try {
const changes = await this.miDebugger.varUpdate(varObjName);
const changelist = changes.result("changelist");
changelist.forEach((change: any) => {
const name = MINode.valueOf(change, "name");
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change);
});
const varId = this.variableHandlesReverse[varObjName];
varObj = this.variableHandles.get(varId) as any;
} catch (err) {
if (err instanceof MIError && (err.message == "Variable object not found" || err.message.endsWith("does not exist"))) {
varObj = await this.miDebugger.varCreate(id.threadId, id.level, variable.name, varObjName);
const varId = findOrCreateVariable(varObj);
varObj.exp = variable.name;
varObj.id = varId;
} else {
throw err;
}
}
variables.push(varObj.toProtocolVariable());
} catch (err) {
variables.push({
name: variable.name,
value: `<${err}>`,
variablesReference: 0
});
}
variables.push(varObj.toProtocolVariable());
} catch (err) {
variables.push({
name: variable.name,
value: `<${err}>`,
variablesReference: 0
});
} else {
if (variable.valueStr !== undefined) {
let expanded = expandValue(createVariable, `{${variable.name}=${variable.valueStr})`, "", variable.raw);
if (expanded) {
if (typeof expanded[0] == "string")
expanded = [
{
name: "<value>",
value: prettyStringArray(expanded),
variablesReference: 0
}
];
variables.push(expanded[0]);
}
} else
variables.push({
name: variable.name,
type: variable.type,
value: "<unknown>",
variablesReference: createVariable(variable.name)
});
}
} else {
if (variable.valueStr !== undefined) {
let expanded = expandValue(createVariable, `{${variable.name}=${variable.valueStr})`, "", variable.raw);
if (expanded) {
if (typeof expanded[0] == "string")
expanded = [
{
name: "<value>",
value: prettyStringArray(expanded),
variablesReference: 0
}
];
variables.push(expanded[0]);
}
} else
variables.push({
name: variable.name,
evaluateName: variable.name,
type: variable.type,
value: variable.type,
variablesReference: createVariable(variable.name)
});
}
}
response.body = {
@ -581,8 +602,7 @@ export class MI2DebugSession extends DebugSession {
};
this.sendResponse(response);
} catch (err) {
this.sendResponse(response);
//this.sendErrorResponse(response, 1, `Could not expand variable1: ${err}`);
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
}
} else if (typeof id == "string") {
// Variable members