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
JavaScript
/**
* 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