diff --git a/media/panel.html b/media/panel.html
index e29ed23..6b487db 100644
--- a/media/panel.html
+++ b/media/panel.html
@@ -108,9 +108,10 @@
Memscope
-
+
+
-
+
diff --git a/media/panel.js b/media/panel.js
index 3b2dfa5..4245189 100644
--- a/media/panel.js
+++ b/media/panel.js
@@ -116,8 +116,9 @@ document.querySelectorAll('.datatype').forEach(button => {
selectedDatatype = button.getAttribute('data-type');
selectedTypeSize = parseInt(button.getAttribute('data-size'), 10);
- if (document.getElementById('live').checked)
+ if (document.getElementById('live').checked) {
fetchImage();
+ }
});
});
@@ -134,6 +135,16 @@ function fetchImage() {
});
}
+function requestDraw() {
+ const pause = document.getElementById("pause").checked;
+ if (pause) {
+ vscode.postMessage({
+ command: 'pause',
+ });
+ }
+ fetchImage();
+}
+
function clear() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
diff --git a/src/memScopePanel.ts b/src/memScopePanel.ts
index ce81e35..0d2ea09 100644
--- a/src/memScopePanel.ts
+++ b/src/memScopePanel.ts
@@ -1,14 +1,13 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
+import { handleFetch, handlePause } from './utils';
export class MemScopePanel {
public static currentPanel: MemScopePanel | undefined;
private readonly panel: vscode.WebviewPanel;
private readonly extensionUri: vscode.Uri;
private disposables: vscode.Disposable[] = [];
- private currentRequestId: number = 0;
- private abortController: AbortController | null = null;
public static createOrShow(context: vscode.ExtensionContext) {
const column = vscode.ViewColumn.Beside;
@@ -47,117 +46,13 @@ export class MemScopePanel {
}
private async handleMessage(msg: any) {
- if (msg.command === 'fetch') {
- // Cancel previous request
- if (this.abortController) {
- this.abortController.abort();
- }
-
- this.abortController = new AbortController();
- const signal = this.abortController.signal;
- const isCancelled = () => signal.aborted;
-
- try {
- const session = vscode.debug.activeDebugSession;
- if (!session) {
- throw new Error("No active debug session!");
- }
-
- if (isCancelled()) {
- return;
- }
-
- const threads = await session.customRequest('threads');
- const threadId = threads.threads[0].id;
-
- if (isCancelled()) {
- return;
- }
-
- const stack = await session.customRequest('stackTrace', {
- threadId,
- startFrame: 0,
- levels: 1
- });
- const frameId = stack.stackFrames[0].id;
-
- if (isCancelled()) {
- return;
- }
-
- const getValue = async (expr: string) => {
- const res = await session.customRequest('evaluate', {
- expression: expr,
- frameId,
- context: 'watch'
- });
- return res.result;
- };
-
- const getPtr = async (expr: string) => {
- const res = await session.customRequest('evaluate', {
- expression: expr,
- frameId,
- context: 'watch'
- });
- return res.memoryReference;
- };
-
- const width = parseInt(await getValue(msg.widthExpr));
- const height = parseInt(await getValue(msg.heightExpr));
- const channels = parseInt(await getValue(msg.channels));
- const datatype = msg.datatype || 'uint8';
- const typeSize = msg.typeSize || 1;
- const count = width * height * channels * typeSize;
-
- if (isNaN(count) || count <= 0) {
- throw new Error("Invalid image dimensions");
- }
-
- if (isCancelled()) {
- return;
- }
-
- const rowsPerChunk = 100;
- const bytesPerRow = width * channels * typeSize;
- const maxChunkSize = bytesPerRow * rowsPerChunk;
-
- for (let i = 0; i < count; i += maxChunkSize) {
- const currentReadSize = Math.min(maxChunkSize, count - i);
-
- const elementOffset = i / typeSize;
- const pointerStr = (await getPtr(`${msg.pointerExpr} + ${elementOffset}`) || '0');
-
- if (isCancelled()) { return; }
-
- const memory = await session.customRequest('readMemory', {
- memoryReference: pointerStr,
- count: currentReadSize
- });
-
- const uint8Array = new Uint8Array(Buffer.from(memory.data, 'base64'));
-
- this.panel.webview.postMessage({
- command: 'render',
- memory: uint8Array.buffer,
- width,
- height,
- channels,
- datatype,
- typeSize,
- // StartRow tells the webview where to begin drawing this block
- startRow: Math.floor(i / bytesPerRow),
- totalChunks: Math.ceil(count / maxChunkSize),
- requestID: msg.requestID
- });
- }
-
- } catch (err: any) {
- if (!signal.aborted) {
- this.panel.webview.postMessage({ command: 'error', message: err.message || String(err), requestID: msg.requestID });
- }
- }
- this.abortController = null;
+ switch (msg.command) {
+ case 'fetch':
+ handleFetch(msg, this.panel);
+ break;
+ case 'pause':
+ handlePause(msg, this.panel);
+ break;
}
}
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..80cb972
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,120 @@
+import vscode from 'vscode';
+
+let abortController: AbortController | null = null;
+
+export async function handleFetch(msg: any, panel: vscode.WebviewPanel) {
+ if (abortController) {
+ abortController.abort();
+ }
+
+ abortController = new AbortController();
+ const signal = abortController.signal;
+ const isCancelled = () => signal.aborted;
+
+ try {
+ const session = vscode.debug.activeDebugSession;
+ if (!session) {
+ throw new Error("No active debug session!");
+ }
+
+ if (isCancelled()) {
+ return;
+ }
+
+ const threads = await session.customRequest('threads');
+ const threadId = threads.threads[0].id;
+
+ if (isCancelled()) {
+ return;
+ }
+
+ const stack = await session.customRequest('stackTrace', {
+ threadId,
+ startFrame: 0,
+ levels: 1
+ });
+ const frameId = stack.stackFrames[0].id;
+
+ if (isCancelled()) {
+ return;
+ }
+
+ const getValue = async (expr: string) => {
+ const res = await session.customRequest('evaluate', {
+ expression: expr,
+ frameId,
+ context: 'watch'
+ });
+ return res.result;
+ };
+
+ const getPtr = async (expr: string) => {
+ const res = await session.customRequest('evaluate', {
+ expression: expr,
+ frameId,
+ context: 'watch'
+ });
+ return res.memoryReference;
+ };
+
+ const width = parseInt(await getValue(msg.widthExpr));
+ const height = parseInt(await getValue(msg.heightExpr));
+ const channels = parseInt(await getValue(msg.channels));
+ const datatype = msg.datatype || 'uint8';
+ const typeSize = msg.typeSize || 1;
+ const count = width * height * channels * typeSize;
+
+ if (isNaN(count) || count <= 0) {
+ throw new Error("Invalid image dimensions");
+ }
+
+ if (isCancelled()) {
+ return;
+ }
+
+ const rowsPerChunk = 100;
+ const bytesPerRow = width * channels * typeSize;
+ const maxChunkSize = bytesPerRow * rowsPerChunk;
+
+ for (let i = 0; i < count; i += maxChunkSize) {
+ const currentReadSize = Math.min(maxChunkSize, count - i);
+
+ const elementOffset = i / typeSize;
+ const pointerStr = (await getPtr(`${msg.pointerExpr} + ${elementOffset}`) || '0');
+
+ if (isCancelled()) { return; }
+
+ const memory = await session.customRequest('readMemory', {
+ memoryReference: pointerStr,
+ count: currentReadSize
+ });
+
+ const uint8Array = new Uint8Array(Buffer.from(memory.data, 'base64'));
+
+ panel.webview.postMessage({
+ command: 'render',
+ memory: uint8Array.buffer,
+ width,
+ height,
+ channels,
+ datatype,
+ typeSize,
+ // StartRow tells the webview where to begin drawing this block
+ startRow: Math.floor(i / bytesPerRow),
+ totalChunks: Math.ceil(count / maxChunkSize),
+ requestID: msg.requestID
+ });
+ }
+
+ } catch (err: any) {
+ if (!signal.aborted) {
+ panel.webview.postMessage({ command: 'error', message: err.message || String(err), requestID: msg.requestID });
+ }
+ }
+ abortController = null;
+}
+
+export async function handlePause(msg: any, panel: vscode.WebviewPanel) {
+ const session = vscode.debug.activeDebugSession;
+ await session?.customRequest('pause');
+}
\ No newline at end of file