From 02e386e5cbbb265692bbec97a259918b7a41eda8 Mon Sep 17 00:00:00 2001 From: Madhav Goyal Date: Wed, 1 Apr 2026 13:36:00 +0530 Subject: [PATCH] WIP: Add additional checkbox for #1. Pausing the execution with `Draw` button when checkbox is checked works. Since, execution can be paused anywhere, the stackframe changes and variables are lost. --- media/panel.html | 5 +- media/panel.js | 13 ++++- src/memScopePanel.ts | 121 +++---------------------------------------- src/utils.ts | 120 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 116 deletions(-) create mode 100644 src/utils.ts 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