diff --git a/openhands/runtime/utils/runtime_templates/Dockerfile.j2 b/openhands/runtime/utils/runtime_templates/Dockerfile.j2 index c0d915f75e..233e292097 100644 --- a/openhands/runtime/utils/runtime_templates/Dockerfile.j2 +++ b/openhands/runtime/utils/runtime_templates/Dockerfile.j2 @@ -64,11 +64,17 @@ RUN if [ -z "${RELEASE_TAG}" ]; then \ if [ -d "${OPENVSCODE_SERVER_ROOT}" ]; then rm -rf "${OPENVSCODE_SERVER_ROOT}"; fi && \ mv ${RELEASE_TAG}-linux-${arch} ${OPENVSCODE_SERVER_ROOT} && \ cp ${OPENVSCODE_SERVER_ROOT}/bin/remote-cli/openvscode-server ${OPENVSCODE_SERVER_ROOT}/bin/remote-cli/code && \ - rm -f ${RELEASE_TAG}-linux-${arch}.tar.gz && \ - # Install our custom extension - mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world && \ + rm -f ${RELEASE_TAG}-linux-${arch}.tar.gz + +{% endmacro %} + +{% macro install_vscode_extensions() %} +# Install our custom extension +RUN mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world && \ cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/hello-world/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world/ +RUN mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-memory-monitor && \ + cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/memory-monitor/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-memory-monitor/ {% endmacro %} {% macro install_dependencies() %} @@ -147,6 +153,7 @@ RUN chmod a+rwx /openhands/code/openhands/__init__.py # ================================================================ {% if build_from_versioned %} {{ install_dependencies() }} +{{ install_vscode_extensions() }} {% endif %} # Install extra dependencies if specified diff --git a/openhands/runtime/utils/vscode-extensions/memory-monitor/README.md b/openhands/runtime/utils/vscode-extensions/memory-monitor/README.md new file mode 100644 index 0000000000..d66b746696 --- /dev/null +++ b/openhands/runtime/utils/vscode-extensions/memory-monitor/README.md @@ -0,0 +1,48 @@ +# OpenHands Memory Monitor + +A VSCode extension for monitoring system and process memory usage in real-time. + +## Features + +- **Real-time Memory Monitoring**: Displays current memory usage in the status bar +- **Detailed Memory Information**: View detailed memory statistics in a graphical interface +- **Process Monitoring**: See top processes by memory usage +- **Memory Usage History**: Track memory usage over time with interactive charts +- **Cross-Platform Support**: Works on Windows, macOS, and Linux + +## Usage + +The extension automatically starts monitoring memory usage when VSCode is launched. You can interact with it in the following ways: + +### Status Bar Indicator + +A memory usage indicator is displayed in the status bar showing the current system memory usage percentage. Click on this indicator to open the detailed memory view. + +### Commands + +The following commands are available in the Command Palette (`Ctrl+Shift+P` or `Cmd+Shift+P`): + +- **Start Memory Monitor**: Start monitoring memory usage +- **Stop Memory Monitor**: Stop monitoring memory usage +- **Show Memory Details**: Open the detailed memory view + +## Detailed Memory View + +The detailed memory view provides comprehensive information about: + +1. **System Memory**: Total, used, and free memory +2. **Process Memory**: Memory usage of the VSCode extension host process +3. **Memory History**: Chart showing memory usage over time +4. **Top Processes**: List of processes using the most memory + +## Development + +This extension is part of the OpenHands project. To modify or extend it: + +1. Make changes to the source files +2. Test the extension in a development VSCode instance +3. Package the extension for distribution + +## License + +This extension is licensed under the MIT license. \ No newline at end of file diff --git a/openhands/runtime/utils/vscode-extensions/memory-monitor/extension.js b/openhands/runtime/utils/vscode-extensions/memory-monitor/extension.js new file mode 100644 index 0000000000..a0ba135c7c --- /dev/null +++ b/openhands/runtime/utils/vscode-extensions/memory-monitor/extension.js @@ -0,0 +1,42 @@ +const vscode = require('vscode'); +const MemoryMonitor = require('./memory_monitor'); + +function activate(context) { + // Create memory monitor instance + const memoryMonitor = new MemoryMonitor(); + + // Store the context in the memory monitor + memoryMonitor.context = context; + + // Register memory monitor start command + let startMonitorCommand = vscode.commands.registerCommand('openhands-memory-monitor.startMemoryMonitor', function () { + memoryMonitor.start(); + }); + + // Register memory monitor stop command + let stopMonitorCommand = vscode.commands.registerCommand('openhands-memory-monitor.stopMemoryMonitor', function () { + memoryMonitor.stop(); + }); + + // Register memory details command + let showMemoryDetailsCommand = vscode.commands.registerCommand('openhands-memory-monitor.showMemoryDetails', function () { + memoryMonitor.showDetails(); + }); + + // Add all commands to subscriptions + context.subscriptions.push(startMonitorCommand); + context.subscriptions.push(stopMonitorCommand); + context.subscriptions.push(showMemoryDetailsCommand); + + // Start memory monitoring by default + memoryMonitor.start(); +} + +function deactivate() { + // Clean up resources if needed +} + +module.exports = { + activate, + deactivate +} \ No newline at end of file diff --git a/openhands/runtime/utils/vscode-extensions/memory-monitor/memory_monitor.js b/openhands/runtime/utils/vscode-extensions/memory-monitor/memory_monitor.js new file mode 100644 index 0000000000..b8ebe21546 --- /dev/null +++ b/openhands/runtime/utils/vscode-extensions/memory-monitor/memory_monitor.js @@ -0,0 +1,343 @@ +const os = require('os'); +const vscode = require('vscode'); +const ProcessMonitor = require('./process_monitor'); + +class MemoryMonitor { + constructor() { + this.isMonitoring = false; + this.intervalId = null; + this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); + this.statusBarItem.command = 'openhands-memory-monitor.showMemoryDetails'; + this.processMonitor = new ProcessMonitor(); + this.memoryHistory = []; + this.maxHistoryLength = 60; // Keep 5 minutes of data with 5-second intervals + this.context = null; // Will be set in activate + } + + start(interval = 5000) { + if (this.isMonitoring) { + return; + } + + this.isMonitoring = true; + this.statusBarItem.show(); + + // Initial update + this.updateMemoryInfo(); + + // Set interval for updates + this.intervalId = setInterval(() => { + this.updateMemoryInfo(); + }, interval); + + vscode.window.showInformationMessage('Memory monitoring started'); + } + + stop() { + if (!this.isMonitoring) { + return; + } + + this.isMonitoring = false; + clearInterval(this.intervalId); + this.statusBarItem.hide(); + + vscode.window.showInformationMessage('Memory monitoring stopped'); + } + + updateMemoryInfo() { + const totalMem = os.totalmem(); + const freeMem = os.freemem(); + const usedMem = totalMem - freeMem; + + // Calculate memory usage percentage + const memUsagePercent = Math.round((usedMem / totalMem) * 100); + + // Format memory values to MB + const usedMemMB = Math.round(usedMem / (1024 * 1024)); + const totalMemMB = Math.round(totalMem / (1024 * 1024)); + + // Update status bar + this.statusBarItem.text = `$(pulse) Mem: ${memUsagePercent}%`; + this.statusBarItem.tooltip = `Memory Usage: ${usedMemMB}MB / ${totalMemMB}MB`; + + // Store memory data in history + this.memoryHistory.push({ + timestamp: new Date(), + usedMemMB, + totalMemMB, + memUsagePercent, + processMemory: process.memoryUsage() + }); + + // Limit history length + if (this.memoryHistory.length > this.maxHistoryLength) { + this.memoryHistory.shift(); + } + } + + showDetails() { + // Create and show a webview panel with detailed memory information + const panel = vscode.window.createWebviewPanel( + 'memoryMonitor', + 'Memory Monitor', + vscode.ViewColumn.One, + { + enableScripts: true + } + ); + + // Set up message handler for real-time updates + panel.webview.onDidReceiveMessage( + message => { + if (message.command === 'requestUpdate') { + this.updateWebviewContent(panel); + } + }, + undefined, + this.context ? this.context.subscriptions : [] + ); + + // Initial update + this.updateWebviewContent(panel); + + // Handle panel disposal + panel.onDidDispose(() => { + // Clean up any resources if needed + }, null, this.context ? this.context.subscriptions : []); + } + + updateWebviewContent(panel) { + // Get system memory info + const totalMem = os.totalmem(); + const freeMem = os.freemem(); + const usedMem = totalMem - freeMem; + + // Format memory values + const usedMemMB = Math.round(usedMem / (1024 * 1024)); + const freeMemMB = Math.round(freeMem / (1024 * 1024)); + const totalMemMB = Math.round(totalMem / (1024 * 1024)); + + // Get process memory usage + const processMemory = process.memoryUsage(); + const rss = Math.round(processMemory.rss / (1024 * 1024)); + const heapTotal = Math.round(processMemory.heapTotal / (1024 * 1024)); + const heapUsed = Math.round(processMemory.heapUsed / (1024 * 1024)); + + // Get process information + this.processMonitor.getProcessInfo((error, processInfo) => { + if (error) { + console.error('Error getting process info:', error); + return; + } + + // Create HTML content for the webview + const htmlContent = this.generateHtmlReport( + usedMemMB, freeMemMB, totalMemMB, + rss, heapTotal, heapUsed, + processInfo + ); + + // Set the webview's HTML content + panel.webview.html = htmlContent; + }); + } + + generateHtmlReport(usedMemMB, freeMemMB, totalMemMB, rss, heapTotal, heapUsed, processInfo) { + // Create memory usage history data for chart + const memoryLabels = this.memoryHistory.map((entry, index) => index); + const memoryData = this.memoryHistory.map(entry => entry.memUsagePercent); + const heapData = this.memoryHistory.map(entry => + Math.round(entry.processMemory.heapUsed / (1024 * 1024)) + ); + + // Format process info table + let processTable = ''; + if (processInfo && processInfo.processes) { + processTable = ` +
| PID | +Memory % | +CPU % | +Command | +
|---|---|---|---|
| ${proc.pid} | +${proc.memPercent}% | +${proc.cpuPercent || 'N/A'} | +${proc.cmd} | +