UNPKG

ai-debug-local-mcp

Version:

๐ŸŽฏ ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

287 lines โ€ข 10.6 kB
/** * Resource Leak Detector * Identifies and prevents common resource leaks that cause crashes */ import { EventEmitter } from 'events'; export class ResourceLeakDetector extends EventEmitter { static instance; monitoringInterval = null; snapshots = []; maxSnapshots = 100; thresholds = { memoryGrowthMB: 100, // 100MB growth without GC activeHandles: 1000, // Max active handles eventListeners: 500, // Max event listeners timers: 100, // Max active timers consecutiveGrowth: 5 // Consecutive growth cycles }; static getInstance() { if (!ResourceLeakDetector.instance) { ResourceLeakDetector.instance = new ResourceLeakDetector(); } return ResourceLeakDetector.instance; } /** * Start leak detection monitoring */ startMonitoring() { if (this.monitoringInterval) { return; // Already monitoring } console.log('๐Ÿ” Starting resource leak detection...'); // Monitor every 30 seconds this.monitoringInterval = setInterval(() => { try { this.takeSnapshot(); this.analyzeLeaks(); } catch (error) { console.error('โš ๏ธ Leak detection error:', error); } }, 30 * 1000); // Take initial snapshot this.takeSnapshot(); } /** * Stop monitoring */ stopMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = null; console.log('๐Ÿ›‘ Stopped resource leak detection'); } } /** * Take a resource usage snapshot */ takeSnapshot() { const snapshot = { timestamp: Date.now(), memoryUsage: process.memoryUsage(), activeHandles: this.getActiveHandles(), activeRequests: this.getActiveRequests(), eventListenerCount: this.getEventListenerCount(), timerCount: this.getTimerCount() }; this.snapshots.push(snapshot); // Keep only recent snapshots if (this.snapshots.length > this.maxSnapshots) { this.snapshots.shift(); } return snapshot; } /** * Get active handle count */ getActiveHandles() { return process._getActiveHandles().length; } /** * Get active request count */ getActiveRequests() { return process._getActiveRequests().length; } /** * Estimate event listener count */ getEventListenerCount() { let count = 0; // Check process event listeners if (process.listenerCount) { const events = ['exit', 'SIGINT', 'SIGTERM', 'uncaughtException', 'unhandledRejection', 'warning']; count += events.reduce((sum, event) => sum + process.listenerCount(event), 0); } return count; } /** * Get active timer count (approximate) */ getTimerCount() { // This is an approximation - Node.js doesn't expose exact timer counts return process._getActiveHandles().filter((handle) => handle.constructor && handle.constructor.name === 'Timer').length; } /** * Analyze for potential leaks */ analyzeLeaks() { if (this.snapshots.length < 2) { return []; } const detections = []; const current = this.snapshots[this.snapshots.length - 1]; const previous = this.snapshots[this.snapshots.length - 2]; // Memory leak detection const memoryLeak = this.detectMemoryLeak(); if (memoryLeak) { detections.push(memoryLeak); } // Handle leak detection if (current.activeHandles > this.thresholds.activeHandles) { detections.push({ type: 'handles', severity: current.activeHandles > this.thresholds.activeHandles * 2 ? 'critical' : 'high', count: current.activeHandles, threshold: this.thresholds.activeHandles, description: `Excessive active handles detected (${current.activeHandles})`, recommendations: [ 'Check for unclosed file descriptors', 'Verify HTTP connections are properly closed', 'Look for unclosed database connections' ] }); } // Event listener leak detection if (current.eventListenerCount > this.thresholds.eventListeners) { detections.push({ type: 'eventListeners', severity: current.eventListenerCount > this.thresholds.eventListeners * 2 ? 'critical' : 'high', count: current.eventListenerCount, threshold: this.thresholds.eventListeners, description: `Excessive event listeners detected (${current.eventListenerCount})`, recommendations: [ 'Remove unused event listeners', 'Use removeAllListeners() for cleanup', 'Check for circular references in event handlers' ] }); } // Timer leak detection if (current.timerCount > this.thresholds.timers) { detections.push({ type: 'timers', severity: current.timerCount > this.thresholds.timers * 2 ? 'critical' : 'medium', count: current.timerCount, threshold: this.thresholds.timers, description: `Excessive active timers detected (${current.timerCount})`, recommendations: [ 'Clear unused intervals and timeouts', 'Check for recursive setTimeout patterns', 'Verify proper cleanup on component destruction' ] }); } // Emit detections detections.forEach(detection => { this.emit('leakDetected', detection); if (detection.severity === 'critical' || detection.severity === 'high') { this.logLeakDetection(detection); } }); return detections; } /** * Detect memory leaks through growth pattern analysis */ detectMemoryLeak() { if (this.snapshots.length < this.thresholds.consecutiveGrowth) { return null; } const recent = this.snapshots.slice(-this.thresholds.consecutiveGrowth); const first = recent[0]; const last = recent[recent.length - 1]; const heapGrowth = (last.memoryUsage.heapUsed - first.memoryUsage.heapUsed) / (1024 * 1024); // MB const rssGrowth = (last.memoryUsage.rss - first.memoryUsage.rss) / (1024 * 1024); // MB // Check for consistent growth pattern let consecutiveGrowth = 0; for (let i = 1; i < recent.length; i++) { if (recent[i].memoryUsage.heapUsed > recent[i - 1].memoryUsage.heapUsed) { consecutiveGrowth++; } } const isLeaking = consecutiveGrowth >= this.thresholds.consecutiveGrowth - 1 && heapGrowth > this.thresholds.memoryGrowthMB; if (isLeaking) { let severity = 'medium'; if (heapGrowth > 500) severity = 'critical'; else if (heapGrowth > 200) severity = 'high'; return { type: 'memory', severity, count: Math.round(heapGrowth), threshold: this.thresholds.memoryGrowthMB, description: `Memory leak detected: ${heapGrowth.toFixed(1)}MB heap growth over ${this.thresholds.consecutiveGrowth} cycles`, recommendations: [ 'Force garbage collection', 'Check for circular references', 'Review large object retention', 'Clear caches and buffers', 'Investigate closure memory retention' ] }; } return null; } /** * Log leak detection */ logLeakDetection(detection) { const icon = detection.severity === 'critical' ? '๐Ÿšจ' : detection.severity === 'high' ? 'โš ๏ธ' : '๐Ÿ”'; console.warn(`${icon} ${detection.type.toUpperCase()} LEAK DETECTED (${detection.severity})`); console.warn(` ${detection.description}`); console.warn(` Recommendations:`); detection.recommendations.forEach(rec => console.warn(` - ${rec}`)); } /** * Get current resource status */ getCurrentStatus() { return this.snapshots.length > 0 ? this.snapshots[this.snapshots.length - 1] : null; } /** * Get resource history */ getResourceHistory() { return [...this.snapshots]; } /** * Force immediate leak analysis */ performLeakAnalysis() { this.takeSnapshot(); return this.analyzeLeaks(); } /** * Update detection thresholds */ updateThresholds(thresholds) { this.thresholds = { ...this.thresholds, ...thresholds }; console.log('๐Ÿ”ง Updated leak detection thresholds:', this.thresholds); } /** * Attempt automatic leak mitigation */ attemptLeakMitigation(detection) { try { switch (detection.type) { case 'memory': if (global.gc) { console.warn('๐Ÿ—‘๏ธ Forcing garbage collection for memory leak...'); global.gc(); return true; } break; case 'eventListeners': console.warn('๐Ÿงน Clearing excessive event listeners...'); // Remove warning listeners that might accumulate process.removeAllListeners('warning'); return true; case 'timers': console.warn('โฑ๏ธ Cannot automatically clear timers - manual intervention required'); break; case 'handles': console.warn('๐Ÿ”Œ Cannot automatically close handles - manual intervention required'); break; } } catch (error) { console.error('โŒ Failed to mitigate leak:', error); } return false; } } //# sourceMappingURL=resource-leak-detector.js.map