profile-memory-test.mjs•4.65 kB
import { spawn } from 'child_process';
import v8 from 'v8';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Create heap-snapshots directory
const snapshotDir = path.join(__dirname, 'heap-snapshots');
if (!fs.existsSync(snapshotDir)) {
fs.mkdirSync(snapshotDir, { recursive: true });
}
// Clean up old snapshots
fs.readdirSync(snapshotDir).forEach(file => {
if (file.endsWith('.heapsnapshot')) {
fs.unlinkSync(path.join(snapshotDir, file));
}
});
console.log('Starting memory profiling for unit tests...\n');
// Function to format bytes
function formatBytes(bytes) {
return (bytes / 1024 / 1024).toFixed(2) + ' MB';
}
// Track memory usage
const memoryUsage = [];
let snapshotCount = 0;
function recordMemoryUsage(label) {
const usage = process.memoryUsage();
const data = {
label,
timestamp: Date.now(),
heapUsed: usage.heapUsed,
heapTotal: usage.heapTotal,
external: usage.external,
arrayBuffers: usage.arrayBuffers,
rss: usage.rss
};
memoryUsage.push(data);
console.log(`[${label}] Memory: Heap ${formatBytes(usage.heapUsed)} / ${formatBytes(usage.heapTotal)}, RSS: ${formatBytes(usage.rss)}`);
// Take heap snapshot at key points
if (label.includes('test') || label === 'start' || label === 'end') {
const filename = `heap-${snapshotCount++}-${label.replace(/\s+/g, '-')}.heapsnapshot`;
const filepath = path.join(snapshotDir, filename);
v8.writeHeapSnapshot(filepath);
console.log(` → Heap snapshot saved: ${filename}`);
}
return data;
}
// Record initial state
recordMemoryUsage('start');
// Create a test runner that reports memory usage
const testCode = `
import { beforeEach, afterEach } from 'vitest';
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
let testCount = 0;
beforeEach(async () => {
testCount++;
// Report memory before each test
if (process.send) {
process.send({ type: 'memory', label: \`before-test-\${testCount}\` });
}
// Force garbage collection if available
if (global.gc) {
global.gc();
}
});
afterEach(async () => {
// Report memory after each test
if (process.send) {
process.send({ type: 'memory', label: \`after-test-\${testCount}\` });
}
});
// Import the actual test file
await import('./tests/unit/languageServer.test.ts');
`;
// Write the test runner
fs.writeFileSync(path.join(__dirname, 'memory-test-runner.mjs'), testCode);
// Run tests with memory monitoring
const child = spawn('pnpm', ['vitest', 'memory-test-runner.mjs', '--', '--run', '--reporter=verbose'], {
env: {
...process.env,
NODE_OPTIONS: '--expose-gc --max-old-space-size=16384',
},
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
});
// Listen for memory reports from child process
child.on('message', (msg) => {
if (msg.type === 'memory') {
recordMemoryUsage(msg.label);
}
});
// Monitor memory periodically
const monitor = setInterval(() => {
recordMemoryUsage('monitoring');
}, 2000);
child.on('close', (code) => {
clearInterval(monitor);
recordMemoryUsage('end');
console.log('\n=== Memory Usage Analysis ===\n');
// Find memory growth
const start = memoryUsage.find(m => m.label === 'start');
const end = memoryUsage.find(m => m.label === 'end');
if (start && end) {
const heapGrowth = end.heapUsed - start.heapUsed;
const rssGrowth = end.rss - start.rss;
console.log(`Heap growth: ${formatBytes(heapGrowth)}`);
console.log(`RSS growth: ${formatBytes(rssGrowth)}`);
}
// Find biggest memory users
const sorted = [...memoryUsage].sort((a, b) => b.heapUsed - a.heapUsed);
console.log('\nTop 5 memory usage points:');
sorted.slice(0, 5).forEach(m => {
console.log(` ${m.label}: ${formatBytes(m.heapUsed)}`);
});
// Save detailed report
const reportPath = path.join(snapshotDir, 'memory-report.json');
fs.writeFileSync(reportPath, JSON.stringify(memoryUsage, null, 2));
console.log(`\nDetailed report saved to: ${reportPath}`);
// Clean up
fs.unlinkSync(path.join(__dirname, 'memory-test-runner.mjs'));
console.log('\nTo analyze heap snapshots:');
console.log('1. Open Chrome DevTools');
console.log('2. Go to Memory tab');
console.log('3. Load snapshots from:', snapshotDir);
process.exit(code);
});