il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
265 lines • 8.86 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.performanceMonitor = exports.DatabasePerformanceMonitor = void 0;
exports.measurePerformance = measurePerformance;
/**
* Performance monitor and cache for database operations
*/
class DatabasePerformanceMonitor {
constructor(maxMetrics = 1000, defaultCacheTtlMs = 300000, // 5 minutes
maxCacheSize = 500) {
this.metrics = [];
this.cacheMap = new Map();
this.maxMetrics = maxMetrics;
this.defaultCacheTtlMs = defaultCacheTtlMs;
this.maxCacheSize = maxCacheSize;
// Cleanup expired cache entries periodically
setInterval(() => this.cleanupCache(), 60000); // Every minute
}
/**
* Measure and record the performance of an operation
*/
async measureOperation(operationName, operation, metadata) {
const startTime = Date.now();
let success = false;
let error;
try {
const result = await operation();
success = true;
return result;
}
catch (err) {
error = err instanceof Error ? err.message : String(err);
throw err;
}
finally {
const duration = Date.now() - startTime;
this.recordMetric({
operationName,
duration,
timestamp: new Date(),
success,
error,
metadata
});
}
}
/**
* Record a performance metric
*/
recordMetric(metric) {
this.metrics.push(metric);
// Keep only the most recent metrics
if (this.metrics.length > this.maxMetrics) {
this.metrics = this.metrics.slice(-this.maxMetrics);
}
// Log slow operations
if (metric.duration > 5000) { // 5 seconds
console.warn(`Slow database operation detected: ${metric.operationName} took ${metric.duration}ms`);
}
}
/**
* Get performance statistics for a specific operation or all operations
*/
getStats(operationName) {
const relevantMetrics = operationName
? this.metrics.filter(m => m.operationName === operationName)
: this.metrics;
if (relevantMetrics.length === 0) {
return {
totalOperations: 0,
successfulOperations: 0,
failedOperations: 0,
averageDuration: 0,
minDuration: 0,
maxDuration: 0,
p50Duration: 0,
p95Duration: 0,
p99Duration: 0,
errorRate: 0,
operationsPerSecond: 0
};
}
const durations = relevantMetrics.map(m => m.duration).sort((a, b) => a - b);
const successfulOps = relevantMetrics.filter(m => m.success).length;
const totalOps = relevantMetrics.length;
// Calculate time span for operations per second
const timeSpanMs = relevantMetrics.length > 1
? relevantMetrics[relevantMetrics.length - 1].timestamp.getTime() - relevantMetrics[0].timestamp.getTime()
: 1000;
return {
totalOperations: totalOps,
successfulOperations: successfulOps,
failedOperations: totalOps - successfulOps,
averageDuration: durations.reduce((sum, d) => sum + d, 0) / durations.length,
minDuration: durations[0],
maxDuration: durations[durations.length - 1],
p50Duration: this.percentile(durations, 0.5),
p95Duration: this.percentile(durations, 0.95),
p99Duration: this.percentile(durations, 0.99),
errorRate: (totalOps - successfulOps) / totalOps,
operationsPerSecond: (totalOps / timeSpanMs) * 1000
};
}
/**
* Calculate percentile from sorted array
*/
percentile(sortedArray, p) {
if (sortedArray.length === 0)
return 0;
const index = Math.ceil(sortedArray.length * p) - 1;
return sortedArray[Math.max(0, Math.min(index, sortedArray.length - 1))];
}
/**
* Cache a value with optional TTL
*/
cache(key, value, ttlMs) {
// Remove oldest entries if cache is full
if (this.cacheMap.size >= this.maxCacheSize) {
const oldestKey = Array.from(this.cacheMap.entries())
.sort(([, a], [, b]) => a.lastAccessed.getTime() - b.lastAccessed.getTime())[0][0];
this.cacheMap.delete(oldestKey);
}
const expiresAt = Date.now() + (ttlMs || this.defaultCacheTtlMs);
this.cacheMap.set(key, {
value,
expiresAt,
accessCount: 0,
lastAccessed: new Date()
});
}
/**
* Get a cached value
*/
getCached(key) {
const entry = this.cacheMap.get(key);
if (!entry)
return null;
// Check if expired
if (Date.now() > entry.expiresAt) {
this.cacheMap.delete(key);
return null;
}
// Update access statistics
entry.accessCount++;
entry.lastAccessed = new Date();
return entry.value;
}
/**
* Execute operation with caching
*/
async withCache(key, operation, ttlMs) {
// Try to get from cache first
const cached = this.getCached(key);
if (cached !== null) {
return cached;
}
// Execute operation and cache result
const result = await operation();
this.cache(key, result, ttlMs);
return result;
}
/**
* Clear cache entries
*/
clearCache(pattern) {
if (!pattern) {
this.cacheMap.clear();
return;
}
const regex = new RegExp(pattern);
for (const [key] of this.cacheMap) {
if (regex.test(key)) {
this.cacheMap.delete(key);
}
}
}
/**
* Clean up expired cache entries
*/
cleanupCache() {
const now = Date.now();
for (const [key, entry] of this.cacheMap) {
if (now > entry.expiresAt) {
this.cacheMap.delete(key);
}
}
}
/**
* Get cache statistics
*/
getCacheStats() {
const entries = Array.from(this.cacheMap.entries()).map(([key, entry]) => ({
key,
accessCount: entry.accessCount,
lastAccessed: entry.lastAccessed,
expiresAt: new Date(entry.expiresAt)
}));
const totalAccesses = entries.reduce((sum, entry) => sum + entry.accessCount, 0);
const cacheHits = entries.filter(entry => entry.accessCount > 0).length;
return {
size: this.cacheMap.size,
maxSize: this.maxCacheSize,
hitRate: totalAccesses > 0 ? cacheHits / totalAccesses : 0,
entries
};
}
/**
* Get recent metrics
*/
getRecentMetrics(limit = 100) {
return this.metrics.slice(-limit);
}
/**
* Get metrics for a specific time range
*/
getMetricsInRange(startTime, endTime) {
return this.metrics.filter(m => m.timestamp >= startTime && m.timestamp <= endTime);
}
/**
* Export metrics as JSON
*/
exportMetrics() {
return JSON.stringify({
metrics: this.metrics,
stats: this.getStats(),
cacheStats: this.getCacheStats(),
exportedAt: new Date().toISOString()
}, null, 2);
}
/**
* Reset all metrics and cache
*/
reset() {
this.metrics = [];
this.cacheMap.clear();
}
}
exports.DatabasePerformanceMonitor = DatabasePerformanceMonitor;
/**
* Global performance monitor instance
*/
exports.performanceMonitor = new DatabasePerformanceMonitor();
/**
* Decorator for measuring method performance
*/
function measurePerformance(operationName) {
return function (target, propertyName, descriptor) {
if (!descriptor) {
// Handle case where descriptor is not provided
descriptor = Object.getOwnPropertyDescriptor(target, propertyName) || {
value: target[propertyName],
writable: true,
enumerable: true,
configurable: true
};
}
const method = descriptor.value;
const name = operationName || `${target.constructor.name}.${propertyName}`;
descriptor.value = async function (...args) {
return exports.performanceMonitor.measureOperation(name, () => method.apply(this, args), { className: target.constructor.name, methodName: propertyName });
};
return descriptor;
};
}
//# sourceMappingURL=performance-monitor.js.map