diff --git a/package.json b/package.json index c74ff8b..fde1185 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,16 @@ "description": "Additional arguments to pass to GDB", "default": [] }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, "printCalls": { "type": "boolean", "description": "Prints all GDB calls to the console", @@ -192,6 +202,16 @@ "description": "If true this will connect to a gdbserver instead of attaching to a PID", "default": false }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, "printCalls": { "type": "boolean", "description": "Prints all GDB calls to the console", @@ -466,6 +486,16 @@ "description": "Additional arguments to pass to LLDB", "default": [] }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, "printCalls": { "type": "boolean", "description": "Prints all lldb calls to the console", @@ -552,6 +582,16 @@ "type": "string", "description": "PID of running program or program name" }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, "printCalls": { "type": "boolean", "description": "Prints all LLDB calls to the console", @@ -713,6 +753,16 @@ "description": "Additional arguments to pass to mago", "default": [] }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, "printCalls": { "type": "boolean", "description": "Prints all mago calls to the console", @@ -739,6 +789,16 @@ "type": "string", "description": "PID of running program or program name" }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, "printCalls": { "type": "boolean", "description": "Prints all mago calls to the console", @@ -834,4 +894,4 @@ "@types/node": "^7.0.5", "@types/mocha": "^2.2.39" } -} \ No newline at end of file +} diff --git a/src/backend/backend.ts b/src/backend/backend.ts index ada4232..d85cd26 100644 --- a/src/backend/backend.ts +++ b/src/backend/backend.ts @@ -1,6 +1,8 @@ import { MINode } from "./mi_parse"; import { DebugProtocol } from "vscode-debugprotocol/lib/debugProtocol"; +export type ValuesFormattingMode = "disabled" | "parseText" | "prettyPrinters"; + export interface Breakpoint { file?: string; line?: number; diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts index f5f28f1..9a89594 100644 --- a/src/backend/mi2/mi2.ts +++ b/src/backend/mi2/mi2.ts @@ -196,9 +196,9 @@ export class MI2 extends EventEmitter implements IBackend { ]; if (!attach) cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")); + if (this.prettyPrint) + cmds.push(this.sendCommand("enable-pretty-printing")); - // TODO: add extension parameter for enabling/disabling pretty printers - cmds.push(this.sendCommand("enable-pretty-printing")); return cmds; } @@ -668,7 +668,7 @@ export class MI2 extends EventEmitter implements IBackend { return new VariableObject(res.result("")); } - async varEvalExpression(name: string): Promise < MINode > { + async varEvalExpression(name: string): Promise { if (trace) this.log("stderr", "varEvalExpression"); return this.sendCommand(`var-evaluate-expression ${name}`); @@ -746,6 +746,7 @@ export class MI2 extends EventEmitter implements IBackend { return this.isSSH ? this.sshReady : !!this.process; } + prettyPrint: boolean = true; printCalls: boolean; debugOutput: boolean; public procEnv: any; diff --git a/src/gdb.ts b/src/gdb.ts index 2bc2812..d4d0682 100644 --- a/src/gdb.ts +++ b/src/gdb.ts @@ -2,7 +2,7 @@ import { MI2DebugSession } from './mibase'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; import { MI2 } from "./backend/mi2/mi2"; -import { SSHArguments } from './backend/backend'; +import { SSHArguments, ValuesFormattingMode } from './backend/backend'; export interface LaunchRequestArguments { cwd: string; @@ -14,6 +14,7 @@ export interface LaunchRequestArguments { terminal: string; autorun: string[]; ssh: SSHArguments; + valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } @@ -28,6 +29,7 @@ export interface AttachRequestArguments { remote: boolean; autorun: string[]; ssh: SSHArguments; + valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } @@ -54,6 +56,7 @@ class GDBDebugSession extends MI2DebugSession { this.started = false; this.crashed = false; this.debugReady = false; + this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; if (args.ssh !== undefined) { @@ -121,6 +124,7 @@ class GDBDebugSession extends MI2DebugSession { this.needContinue = true; this.isSSH = false; this.debugReady = false; + this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; if (args.ssh !== undefined) { @@ -177,4 +181,4 @@ class GDBDebugSession extends MI2DebugSession { } } -DebugSession.run(GDBDebugSession); \ No newline at end of file +DebugSession.run(GDBDebugSession); diff --git a/src/lldb.ts b/src/lldb.ts index f634c0f..28a92e8 100644 --- a/src/lldb.ts +++ b/src/lldb.ts @@ -2,7 +2,7 @@ import { MI2DebugSession } from './mibase'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; import { MI2_LLDB } from "./backend/mi2/mi2lldb"; -import { SSHArguments } from './backend/backend'; +import { SSHArguments, ValuesFormattingMode } from './backend/backend'; export interface LaunchRequestArguments { cwd: string; @@ -13,6 +13,7 @@ export interface LaunchRequestArguments { arguments: string; autorun: string[]; ssh: SSHArguments; + valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } @@ -25,6 +26,7 @@ export interface AttachRequestArguments { debugger_args: string[]; executable: string; autorun: string[]; + valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } @@ -49,6 +51,7 @@ class LLDBDebugSession extends MI2DebugSession { this.started = false; this.crashed = false; this.debugReady = false; + this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; if (args.ssh !== undefined) { @@ -108,6 +111,7 @@ class LLDBDebugSession extends MI2DebugSession { this.needContinue = true; this.isSSH = false; this.debugReady = false; + this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => { @@ -120,4 +124,4 @@ class LLDBDebugSession extends MI2DebugSession { } } -DebugSession.run(LLDBDebugSession); \ No newline at end of file +DebugSession.run(LLDBDebugSession); diff --git a/src/mago.ts b/src/mago.ts index bd6d166..e06c3e1 100644 --- a/src/mago.ts +++ b/src/mago.ts @@ -2,7 +2,7 @@ import { MI2DebugSession } from './mibase'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; import { MI2_Mago } from "./backend/mi2/mi2mago"; -import { SSHArguments } from './backend/backend'; +import { SSHArguments, ValuesFormattingMode } from './backend/backend'; export interface LaunchRequestArguments { cwd: string; @@ -12,6 +12,7 @@ export interface LaunchRequestArguments { debugger_args: string[]; arguments: string; autorun: string[]; + valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } @@ -24,6 +25,7 @@ export interface AttachRequestArguments { debugger_args: string[]; executable: string; autorun: string[]; + valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } @@ -56,6 +58,7 @@ class MagoDebugSession extends MI2DebugSession { this.started = false; this.crashed = false; this.debugReady = false; + this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.load(args.cwd, args.target, args.arguments, undefined).then(() => { @@ -83,6 +86,7 @@ class MagoDebugSession extends MI2DebugSession { this.needContinue = true; this.isSSH = false; this.debugReady = false; + this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => { @@ -95,4 +99,4 @@ class MagoDebugSession extends MI2DebugSession { } } -DebugSession.run(MagoDebugSession); \ No newline at end of file +DebugSession.run(MagoDebugSession); diff --git a/src/mibase.ts b/src/mibase.ts index 92cacca..aaeef58 100644 --- a/src/mibase.ts +++ b/src/mibase.ts @@ -1,6 +1,6 @@ import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; -import { Breakpoint, IBackend, Variable, VariableObject } from './backend/backend'; +import { Breakpoint, IBackend, Variable, VariableObject, ValuesFormattingMode } from './backend/backend'; import { MINode } from './backend/mi_parse'; import { expandValue, isExpandable } from './backend/gdb_expansion'; import { MI2 } from './backend/mi2/mi2'; @@ -24,6 +24,7 @@ const VAR_HANDLES_START = 2000; export class MI2DebugSession extends DebugSession { protected variableHandles = new Handles(VAR_HANDLES_START); protected variableHandlesReverse: { [id: string]: number } = {}; + protected useVarObjects: boolean; protected quit: boolean; protected attached: boolean; protected needContinue: boolean; @@ -82,6 +83,23 @@ export class MI2DebugSession extends DebugSession { } } + protected setValuesFormattingMode(mode: ValuesFormattingMode) { + switch (mode) { + case "disabled": + this.useVarObjects = true; + this.miDebugger.prettyPrint = false; + break; + case "prettyPrinters": + this.useVarObjects = true; + this.miDebugger.prettyPrint = true; + break; + case "parseText": + default: + this.useVarObjects = false; + this.miDebugger.prettyPrint = false; + } + } + protected handleMsg(type: string, msg: string) { if (type == "target") type = "stdout"; @@ -132,16 +150,24 @@ export class MI2DebugSession extends DebugSession { protected async setVariableRequest(response: DebugProtocol.SetVariableResponse, args: DebugProtocol.SetVariableArguments): Promise { try { - let name = args.name; - if (args.variablesReference >= VAR_HANDLES_START) { - const parent = this.variableHandles.get(args.variablesReference) as VariableObject; - name = `${parent.name}.${name}`; - } + if (this.useVarObjects) { + let name = args.name; + if (args.variablesReference >= VAR_HANDLES_START) { + const parent = this.variableHandles.get(args.variablesReference) as VariableObject; + name = `${parent.name}.${name}`; + } - let res = await this.miDebugger.varAssign(name, args.value); - response.body = { - value: res.result("value") - }; + let res = await this.miDebugger.varAssign(name, args.value); + response.body = { + value: res.result("value") + }; + } + else { + await this.miDebugger.changeVariable(args.name, args.value); + response.body = { + value: args.value + }; + } this.sendResponse(response); } catch (err) { @@ -307,34 +333,58 @@ export class MI2DebugSession extends DebugSession { try { stack = await this.miDebugger.getStackVariables(this.threadID, id); for (const variable of stack) { - try { - let varObj: VariableObject; + if (this.useVarObjects) { try { - const changes = await this.miDebugger.varUpdate(variable.name); - const changelist = changes.result("changelist"); - changelist.forEach((change) => { - const name = MINode.valueOf(change, "name"); - const vId = this.variableHandlesReverse[variable.name]; - const v = this.variableHandles.get(vId) as any; - v.applyChanges(change); - }); - const varId = this.variableHandlesReverse[variable.name]; - varObj = this.variableHandles.get(varId) as any; + let varObj: VariableObject; + try { + const changes = await this.miDebugger.varUpdate(variable.name); + const changelist = changes.result("changelist"); + changelist.forEach((change) => { + const name = MINode.valueOf(change, "name"); + const vId = this.variableHandlesReverse[variable.name]; + const v = this.variableHandles.get(vId) as any; + v.applyChanges(change); + }); + const varId = this.variableHandlesReverse[variable.name]; + varObj = this.variableHandles.get(varId) as any; + } + catch (err) { + varObj = await this.miDebugger.varCreate(variable.name, variable.name); + const varId = findOrCreateVariable(varObj); + varObj.exp = variable.name; + varObj.id = varId; + } + variables.push(varObj.toProtocolVariable()); } catch (err) { - varObj = await this.miDebugger.varCreate(variable.name, variable.name); - const varId = findOrCreateVariable(varObj); - varObj.exp = variable.name; - varObj.id = varId; + 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: prettyStringArray(expanded), + variablesReference: 0 + } + ]; + variables.push(expanded[0]); + } + } else + variables.push({ + name: variable.name, + type: variable.type, + value: "", + variablesReference: createVariable(variable.name) + }); } } response.body = { @@ -346,6 +396,39 @@ export class MI2DebugSession extends DebugSession { this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`); } } + else if (typeof id == "string") { + // Variable members + let variable; + try { + variable = await this.miDebugger.evalExpression(JSON.stringify(id)); + try { + let expanded = expandValue(createVariable, variable.result("value"), id, variable); + if (!expanded) { + this.sendErrorResponse(response, 2, `Could not expand variable`); + } + else { + if (typeof expanded[0] == "string") + expanded = [ + { + name: "", + value: prettyStringArray(expanded), + variablesReference: 0 + } + ]; + response.body = { + variables: expanded + }; + this.sendResponse(response); + } + } + catch (e) { + this.sendErrorResponse(response, 2, `Could not expand variable: ${e}`); + } + } + catch (err) { + this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`); + } + } else if (typeof id == "object") { if (id instanceof VariableObject) { // Variable members