Added examining memory locations (fix #64)
also added a way to run mi commands over unix domain sockets so other applications can communicate with code-debug
This commit is contained in:
parent
e78733308e
commit
87ff8b8644
5 changed files with 184 additions and 7 deletions
19
package.json
19
package.json
|
|
@ -6,8 +6,12 @@
|
||||||
"publisher": "webfreak",
|
"publisher": "webfreak",
|
||||||
"icon": "images/icon-plain.svg",
|
"icon": "images/icon-plain.svg",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.3.0"
|
"vscode": "^0.10.10"
|
||||||
},
|
},
|
||||||
|
"main": "./out/src/frontend/extension",
|
||||||
|
"activationEvents": [
|
||||||
|
"onCommand:code-debug.examineMemoryLocation"
|
||||||
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"Debuggers"
|
"Debuggers"
|
||||||
],
|
],
|
||||||
|
|
@ -16,6 +20,12 @@
|
||||||
"url": "https://github.com/WebFreak001/code-debug.git"
|
"url": "https://github.com/WebFreak001/code-debug.git"
|
||||||
},
|
},
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "code-debug.examineMemoryLocation",
|
||||||
|
"title": "Code-Debug: Examine memory location"
|
||||||
|
}
|
||||||
|
],
|
||||||
"debuggers": [
|
"debuggers": [
|
||||||
{
|
{
|
||||||
"type": "gdb",
|
"type": "gdb",
|
||||||
|
|
@ -347,7 +357,7 @@
|
||||||
},
|
},
|
||||||
"autorun": {
|
"autorun": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "LLDB commands to run when starting to debug",
|
"description": "lldb commands to run when starting to debug",
|
||||||
"default": []
|
"default": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -439,7 +449,7 @@
|
||||||
},
|
},
|
||||||
"autorun": {
|
"autorun": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Mago commands to run when starting to debug",
|
"description": "mago commands to run when starting to debug",
|
||||||
"default": []
|
"default": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -460,7 +470,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "node ./node_modules/vscode/bin/compile",
|
"vscode:prepublish": "node ./node_modules/vscode/bin/compile",
|
||||||
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
|
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
|
||||||
"test": "node ./node_modules/vscode/bin/test"
|
"test": "node ./node_modules/vscode/bin/test",
|
||||||
|
"postinstall": "node ./node_modules/vscode/bin/install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vscode-debugadapter": "^1.10.0",
|
"vscode-debugadapter": "^1.10.0",
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,5 @@ export interface IBackend {
|
||||||
evalExpression(name: string): Thenable<any>;
|
evalExpression(name: string): Thenable<any>;
|
||||||
isReady(): boolean;
|
isReady(): boolean;
|
||||||
changeVariable(name: string, rawValue: string): Thenable<any>;
|
changeVariable(name: string, rawValue: string): Thenable<any>;
|
||||||
|
examineMemory(from: number, to: number): Thenable<any>;
|
||||||
}
|
}
|
||||||
|
|
@ -556,6 +556,14 @@ export class MI2 extends EventEmitter implements IBackend {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
examineMemory(from: number, length: number): Thenable<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.sendCommand("data-read-memory-bytes 0x" + from.toString(16) + " " + length).then((result) => {
|
||||||
|
resolve(result.result("memory[0].contents"));
|
||||||
|
}, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
evalExpression(name: string): Thenable<any> {
|
evalExpression(name: string): Thenable<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.sendCommand("data-evaluate-expression " + name).then((result) => {
|
this.sendCommand("data-evaluate-expression " + name).then((result) => {
|
||||||
|
|
|
||||||
125
src/frontend/extension.ts
Normal file
125
src/frontend/extension.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import * as net from "net";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import * as os from "os";
|
||||||
|
|
||||||
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
|
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider("debugmemory", new MemoryContentProvider()));
|
||||||
|
context.subscriptions.push(vscode.commands.registerCommand("code-debug.examineMemoryLocation", examineMemory));
|
||||||
|
}
|
||||||
|
|
||||||
|
var memoryLocationRegex = /^0x[0-9a-f]+$/;
|
||||||
|
|
||||||
|
function getMemoryRange(range: string) {
|
||||||
|
if (!range)
|
||||||
|
return undefined;
|
||||||
|
range = range.replace(/\s+/g, "").toLowerCase();
|
||||||
|
var index;
|
||||||
|
if ((index = range.indexOf("+")) != -1) {
|
||||||
|
var from = range.substr(0, index);
|
||||||
|
var length = range.substr(index + 1);
|
||||||
|
if (!memoryLocationRegex.exec(from))
|
||||||
|
return undefined;
|
||||||
|
if (memoryLocationRegex.exec(length))
|
||||||
|
length = parseInt(length.substr(2), 16).toString();
|
||||||
|
return "from=" + encodeURIComponent(from) + "&length=" + encodeURIComponent(length);
|
||||||
|
}
|
||||||
|
else if ((index = range.indexOf("-")) != -1) {
|
||||||
|
var from = range.substr(0, index);
|
||||||
|
var to = range.substr(index + 1);
|
||||||
|
if (!memoryLocationRegex.exec(from))
|
||||||
|
return undefined;
|
||||||
|
if (!memoryLocationRegex.exec(to))
|
||||||
|
return undefined;
|
||||||
|
return "from=" + encodeURIComponent(from) + "&to=" + encodeURIComponent(to);
|
||||||
|
}
|
||||||
|
else if (memoryLocationRegex.exec(range))
|
||||||
|
return "at=" + encodeURIComponent(range);
|
||||||
|
else return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function examineMemory() {
|
||||||
|
let socketlists = path.join(os.tmpdir(), "code-debug-sockets");
|
||||||
|
if (!fs.existsSync(socketlists))
|
||||||
|
return vscode.window.showErrorMessage("No debugging sessions available");
|
||||||
|
fs.readdir(socketlists, (err, files) => {
|
||||||
|
if (err)
|
||||||
|
return vscode.window.showErrorMessage("No debugging sessions available");
|
||||||
|
var pickedFile = (file) => {
|
||||||
|
vscode.window.showInputBox({ placeHolder: "Memory Location or Range", validateInput: range => getMemoryRange(range) === undefined ? "Range must either be in format 0xF00-0xF01, 0xF100+32 or 0xABC154" : "" }).then(range => {
|
||||||
|
vscode.commands.executeCommand("vscode.previewHtml", vscode.Uri.parse("debugmemory://" + file + "#" + getMemoryRange(range)));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (files.length == 1)
|
||||||
|
pickedFile(files[0]);
|
||||||
|
else if (files.length > 0)
|
||||||
|
vscode.window.showQuickPick(files, { placeHolder: "Running debugging instance" }).then(file => pickedFile(file));
|
||||||
|
else
|
||||||
|
vscode.window.showErrorMessage("No debugging sessions available");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemoryContentProvider implements vscode.TextDocumentContentProvider {
|
||||||
|
provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): Thenable<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var conn = net.connect(path.join(os.tmpdir(), "code-debug-sockets", uri.authority));
|
||||||
|
var from, to;
|
||||||
|
var highlightAt = -1;
|
||||||
|
var splits = uri.fragment.split("&");
|
||||||
|
if (splits[0].split("=")[0] == "at") {
|
||||||
|
var loc = parseInt(splits[0].split("=")[1].substr(2), 16);
|
||||||
|
highlightAt = 64;
|
||||||
|
from = Math.max(loc - 64, 0);
|
||||||
|
to = Math.max(loc + 768, 0);
|
||||||
|
}
|
||||||
|
else if (splits[0].split("=")[0] == "from") {
|
||||||
|
from = parseInt(splits[0].split("=")[1].substr(2), 16);
|
||||||
|
if (splits[1].split("=")[0] == "to") {
|
||||||
|
to = parseInt(splits[1].split("=")[1].substr(2), 16);
|
||||||
|
}
|
||||||
|
else if (splits[1].split("=")[0] == "length") {
|
||||||
|
to = from + parseInt(splits[1].split("=")[1]);
|
||||||
|
}
|
||||||
|
else return reject("Invalid Range");
|
||||||
|
}
|
||||||
|
else return reject("Invalid Range");
|
||||||
|
if (to < from)
|
||||||
|
return reject("Negative Range");
|
||||||
|
conn.write("examineMemory " + JSON.stringify([from, to - from + 1]));
|
||||||
|
conn.once("data", data => {
|
||||||
|
var formattedCode = "";
|
||||||
|
var hexString = data.toString();
|
||||||
|
var x = 0;
|
||||||
|
var asciiLine = "";
|
||||||
|
var byteNo = 0;
|
||||||
|
for (var i = 0; i < hexString.length; i += 2) {
|
||||||
|
var digit = hexString.substr(i, 2);
|
||||||
|
var digitNum = parseInt(digit, 16);
|
||||||
|
if (digitNum >= 32 && digitNum <= 126)
|
||||||
|
asciiLine += String.fromCharCode(digitNum);
|
||||||
|
else
|
||||||
|
asciiLine += ".";
|
||||||
|
if (highlightAt == byteNo) {
|
||||||
|
formattedCode += "<b>" + digit + "</b> ";
|
||||||
|
} else
|
||||||
|
formattedCode += digit + " ";
|
||||||
|
if (++x > 16) {
|
||||||
|
formattedCode += asciiLine + "\n";
|
||||||
|
x = 0;
|
||||||
|
asciiLine = "";
|
||||||
|
}
|
||||||
|
byteNo++;
|
||||||
|
}
|
||||||
|
if (x > 0) {
|
||||||
|
for (var i = 0; i <= 16 - x; i++) {
|
||||||
|
formattedCode += " ";
|
||||||
|
}
|
||||||
|
formattedCode += asciiLine;
|
||||||
|
}
|
||||||
|
resolve("<h2>Memory Range from 0x" + from.toString(16) + " to 0x" + to.toString(16) + "</h2><code><pre>" + formattedCode + "</pre></code>");
|
||||||
|
conn.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,9 @@ import { expandValue, isExpandable } from './backend/gdb_expansion';
|
||||||
import { MI2 } from './backend/mi2/mi2';
|
import { MI2 } from './backend/mi2/mi2';
|
||||||
import { posix } from "path";
|
import { posix } from "path";
|
||||||
import * as systemPath from "path";
|
import * as systemPath from "path";
|
||||||
|
import * as net from "net";
|
||||||
|
import * as os from "os";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
let resolve = posix.resolve;
|
let resolve = posix.resolve;
|
||||||
let relative = posix.relative;
|
let relative = posix.relative;
|
||||||
|
|
@ -23,6 +26,7 @@ export class MI2DebugSession extends DebugSession {
|
||||||
protected debugReady: boolean;
|
protected debugReady: boolean;
|
||||||
protected miDebugger: MI2;
|
protected miDebugger: MI2;
|
||||||
protected threadID: number = 1;
|
protected threadID: number = 1;
|
||||||
|
protected commandServer: net.Server;
|
||||||
|
|
||||||
public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false, threadID: number = 1) {
|
public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false, threadID: number = 1) {
|
||||||
super(debuggerLinesStartAt1, isServer);
|
super(debuggerLinesStartAt1, isServer);
|
||||||
|
|
@ -39,6 +43,31 @@ export class MI2DebugSession extends DebugSession {
|
||||||
this.miDebugger.on("step-out-end", this.handleBreak.bind(this));
|
this.miDebugger.on("step-out-end", this.handleBreak.bind(this));
|
||||||
this.miDebugger.on("signal-stop", this.handlePause.bind(this));
|
this.miDebugger.on("signal-stop", this.handlePause.bind(this));
|
||||||
this.sendEvent(new InitializedEvent());
|
this.sendEvent(new InitializedEvent());
|
||||||
|
try {
|
||||||
|
this.commandServer = net.createServer(c => {
|
||||||
|
c.on("data", data => {
|
||||||
|
var rawCmd = data.toString();
|
||||||
|
var spaceIndex = rawCmd.indexOf(" ");
|
||||||
|
var func = rawCmd;
|
||||||
|
var args = [];
|
||||||
|
if (spaceIndex != -1) {
|
||||||
|
func = rawCmd.substr(0, spaceIndex);
|
||||||
|
args = JSON.parse(rawCmd.substr(spaceIndex + 1));
|
||||||
|
}
|
||||||
|
Promise.resolve(this.miDebugger[func].apply(this.miDebugger, args)).then(data => {
|
||||||
|
c.write(data.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.commandServer.on("error", err => {
|
||||||
|
this.handleMsg("stderr", "Code-Debug Utility Command Server: Error in command socket " + err.toString());
|
||||||
|
});
|
||||||
|
if (!fs.existsSync(systemPath.join(os.tmpdir(), "code-debug-sockets")))
|
||||||
|
fs.mkdirSync(systemPath.join(os.tmpdir(), "code-debug-sockets"));
|
||||||
|
this.commandServer.listen(systemPath.join(os.tmpdir(), "code-debug-sockets", "Debug-Instance-" + Math.floor(Math.random() * 36 * 36 * 36 * 36).toString(36)));
|
||||||
|
} catch (e) {
|
||||||
|
this.handleMsg("stderr", "Code-Debug Utility Command Server: Failed to start " + e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleMsg(type: string, msg: string) {
|
protected handleMsg(type: string, msg: string) {
|
||||||
|
|
@ -78,6 +107,8 @@ export class MI2DebugSession extends DebugSession {
|
||||||
this.miDebugger.detach();
|
this.miDebugger.detach();
|
||||||
else
|
else
|
||||||
this.miDebugger.stop();
|
this.miDebugger.stop();
|
||||||
|
this.commandServer.close();
|
||||||
|
this.commandServer = undefined;
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +119,7 @@ export class MI2DebugSession extends DebugSession {
|
||||||
};
|
};
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
}, err => {
|
}, err => {
|
||||||
this.sendErrorResponse(response, 11, `Could not continue: ${err}`);
|
this.sendErrorResponse(response, 11, `Could not continue: ${err}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,6 +210,8 @@ export class MI2DebugSession extends DebugSession {
|
||||||
stackFrames: ret
|
stackFrames: ret
|
||||||
};
|
};
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
|
}, err => {
|
||||||
|
this.sendErrorResponse(response, 12, `Failed to get Stack Trace: ${err.toString()}`)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,8 +252,7 @@ export class MI2DebugSession extends DebugSession {
|
||||||
stack.forEach(variable => {
|
stack.forEach(variable => {
|
||||||
if (variable.valueStr !== undefined) {
|
if (variable.valueStr !== undefined) {
|
||||||
let expanded = expandValue(createVariable, "{" + variable.name + "=" + variable.valueStr + ")");
|
let expanded = expandValue(createVariable, "{" + variable.name + "=" + variable.valueStr + ")");
|
||||||
if (expanded)
|
if (expanded) {
|
||||||
{
|
|
||||||
if (typeof expanded[0] == "string")
|
if (typeof expanded[0] == "string")
|
||||||
expanded = [
|
expanded = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue