Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert skill for Node.js-specific profiling and optimization. Use V8 CPU profiler, analyze heap snapshots, configure clinic.js tools (Doctor, Flame, Bubbleprof), debug event loop blocking, analyze async hooks performance, and optimize V8 JIT compilation.
.claude/skills/a5c-ai-nodejs-profiling/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 1922% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 138% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 250% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 105% | 0% |
You are nodejs-profiling - a specialized skill for Node.js runtime profiling and optimization. This skill provides expert capabilities for analyzing Node.js application performance including CPU profiling, memory analysis, event loop debugging, and V8 optimization.
This skill enables AI-powered Node.js profiling including:
npm install -g clinicProfile CPU usage using V8's built-in profiler:
javascript// cpu-profile.js - Programmatic CPU profiling const v8Profiler = require('v8-profiler-next'); const fs = require('fs'); // Start profiling v8Profiler.setGenerateType(1); // Generate call tree v8Profiler.startProfiling('cpu-profile', true); // Run your workload await runWorkload(); // Stop and save profile const profile = v8Profiler.stopProfiling('cpu-profile'); const profileData = profile.export(); fs.writeFileSync('cpu-profile.cpuprofile', JSON.stringify(profileData)); profile.delete(); console.log('CPU profile saved to cpu-profile.cpuprofile');
bash# Using Node.js built-in profiler node --prof app.js node --prof-process isolate-0x*.log > processed.txt # Generate V8 log for analysis node --trace-opt --trace-deopt app.js 2>&1 | grep -E "(opt|deopt)" # Run with inspector for Chrome DevTools profiling node --inspect app.js # Then open chrome://inspect in Chrome
Capture and analyze heap snapshots:
javascript// heap-analysis.js const v8 = require('v8'); const fs = require('fs'); // Take heap snapshot function takeHeapSnapshot(filename) { const snapshotStream = v8.writeHeapSnapshot(filename); console.log(`Heap snapshot written to ${snapshotStream}`); return snapshotStream; } // Memory usage tracking function trackMemory() { const usage = process.memoryUsage(); return { heapUsed: `${(usage.heapUsed / 1024 / 1024).toFixed(2)} MB`, heapTotal: `${(usage.heapTotal / 1024 / 1024).toFixed(2)} MB`, external: `${(usage.external / 1024 / 1024).toFixed(2)} MB`, rss: `${(usage.rss / 1024 / 1024).toFixed(2)} MB`, arrayBuffers: `${(usage.arrayBuffers / 1024 / 1024).toFixed(2)} MB` }; } // Heap statistics function getHeapStats() { const stats = v8.getHeapStatistics(); return { totalHeapSize: `${(stats.total_heap_size / 1024 / 1024).toFixed(2)} MB`, usedHeapSize: `${(stats.used_heap_size / 1024 / 1024).toFixed(2)} MB`, heapSizeLimit: `${(stats.heap_size_limit / 1024 / 1024).toFixed(2)} MB`, mallocedMemory: `${(stats.malloced_memory / 1024 / 1024).toFixed(2)} MB`, peakMallocedMemory: `${(stats.peak_malloced_memory / 1024 / 1024).toFixed(2)} MB` }; } // Trigger garbage collection (requires --expose-gc flag) function forceGC() { if (global.gc) { global.gc(); console.log('Garbage collection triggered'); } else { console.warn('Run with --expose-gc to enable manual GC'); } }
Use clinic.js suite for comprehensive analysis:
bash# Clinic Doctor - Overall health check clinic doctor -- node app.js # Generates: .clinic/xxx.clinic-doctor # Clinic Flame - CPU flame graphs clinic flame -- node app.js # Generates: .clinic/xxx.clinic-flame # Clinic Bubbleprof - Async operations visualization clinic bubbleprof -- node app.js # Generates: .clinic/xxx.clinic-bubbleprof # Clinic Heap Profiler - Memory analysis clinic heapprofiler -- node app.js # Generates: .clinic/xxx.clinic-heapprofiler # Run with specific workload clinic flame --autocannon [ /api/users -- -c 10 -d 30 ] -- node app.js # Analyze specific endpoint clinic bubbleprof --autocannon [ /api/slow-endpoint -c 5 -d 60 ] -- node server.js
javascript// Using clinic programmatically const ClinicDoctor = require('@clinic/doctor'); const doctor = new ClinicDoctor(); doctor.collect(['node', 'app.js'], (err, filepath) => { if (err) throw err; doctor.visualize(filepath, filepath + '.html', (err) => { if (err) throw err; console.log(`Report: ${filepath}.html`); }); });
Debug event loop blocking and delays:
javascript// event-loop-monitor.js const { monitorEventLoopDelay } = require('perf_hooks'); // Create histogram for event loop delay const h = monitorEventLoopDelay({ resolution: 20 }); h.enable(); // Periodic reporting setInterval(() => { console.log('Event Loop Delay:'); console.log(` Min: ${h.min / 1e6} ms`); console.log(` Max: ${h.max / 1e6} ms`); console.log(` Mean: ${h.mean / 1e6} ms`); console.log(` P50: ${h.percentile(50) / 1e6} ms`); console.log(` P99: ${h.percentile(99) / 1e6} ms`); h.reset(); }, 5000); // Detect blocking operations const blocked = require('blocked-at'); blocked((time, stack, { type, resource }) => { console.warn(`Event loop blocked for ${time}ms`); console.warn(`Type: ${type}`); console.warn(`Stack:\n${stack.join('\n')}`); }, { threshold: 100, resourcesCap: 100 });
javascript// Async operation timing const async_hooks = require('async_hooks'); const { performance, PerformanceObserver } = require('perf_hooks'); // Track async operation durations const asyncTiming = new Map(); const hook = async_hooks.createHook({ init(asyncId, type, triggerAsyncId) { asyncTiming.set(asyncId, { type, start: performance.now(), triggerAsyncId }); }, destroy(asyncId) { const timing = asyncTiming.get(asyncId); if (timing) { const duration = performance.now() - timing.start; if (duration > 100) { // Log slow operations console.log(`Slow async: ${timing.type} took ${duration.toFixed(2)}ms`); } asyncTiming.delete(asyncId); } } }); hook.enable();
Generate flame graphs for CPU analysis:
bash# Using 0x npm install -g 0x 0x -o app.js # Opens flame graph in browser # Using perf and FlameGraph (Linux) perf record -F 99 -g -- node app.js perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > flame.svg # Using node --perf-basic-prof node --perf-basic-prof app.js & perf record -F 99 -p $! -g -- sleep 30 perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > flame.svg
Analyze V8 JIT optimization:
bash# Trace optimizations and deoptimizations node --trace-opt --trace-deopt app.js 2>&1 | tee opt.log # Analyze inline caches node --trace-ic app.js 2>&1 | tee ic.log # Check for hidden class transitions node --allow-natives-syntax -e " function Point(x, y) { this.x = x; this.y = y; } const p = new Point(1, 2); %DebugPrint(p); %HaveSameMap(new Point(1,2), p); " # Detailed V8 flags node --v8-options | grep -i "optimize"
javascript// Optimization hints (for debugging only) function optimizedFunction(a, b) { // This function should be optimized return a + b; } // Check if function is optimized (requires --allow-natives-syntax) // %OptimizeFunctionOnNextCall(optimizedFunction); // optimizedFunction(1, 2); // console.log(%GetOptimizationStatus(optimizedFunction));
Detect and diagnose memory leaks:
javascript// leak-detector.js const memwatch = require('@airbnb/node-memwatch'); // Detect leak trends memwatch.on('leak', (info) => { console.error('Memory leak detected:'); console.error(JSON.stringify(info, null, 2)); }); // Track heap diffs let lastHeapDiff = null; memwatch.on('stats', (stats) => { console.log('GC occurred:'); console.log(` Heap used: ${(stats.used_heap_size / 1024 / 1024).toFixed(2)} MB`); if (lastHeapDiff) { const diff = new memwatch.HeapDiff(); // ... run some code ... const changes = diff.end(); console.log('Heap changes:', JSON.stringify(changes.change, null, 2)); } }); // Programmatic heap diff function findLeaks() { const hd = new memwatch.HeapDiff(); // Run suspected leaky code suspectedLeakyFunction(); const diff = hd.end(); return diff.change.details.filter(d => d.size_bytes > 10000 && d['+'] > d['-'] ); }
Use built-in performance APIs:
javascript// performance-monitoring.js const { performance, PerformanceObserver, createHistogram } = require('perf_hooks'); // Measure specific operations performance.mark('operation-start'); await performOperation(); performance.mark('operation-end'); performance.measure('operation', 'operation-start', 'operation-end'); // Observe measurements const obs = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach((entry) => { console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`); }); }); obs.observe({ entryTypes: ['measure', 'function'] }); // Create histogram for repeated measurements const histogram = createHistogram(); function timedOperation() { const start = process.hrtime.bigint(); // ... operation ... const duration = Number(process.hrtime.bigint() - start); histogram.record(duration); } // Report histogram console.log({ min: histogram.min, max: histogram.max, mean: histogram.mean, p50: histogram.percentile(50), p99: histogram.percentile(99) });
This skill can leverage the following MCP servers:
| Server | Description | Use Case | |--------|-------------|----------| | clinic.js | Node.js profiling suite | Comprehensive analysis | | Sentry MCP | Error tracking | Performance correlation | | OpenTelemetry | Distributed tracing | Production profiling |
This skill integrates with the following processes:
cpu-profiling-investigation.js - CPU profiling workflowsmemory-profiling-analysis.js - Memory analysismemory-leak-detection.js - Leak detectionWhen executing operations, provide structured output:
json{ "operation": "profile-cpu", "status": "completed", "duration": "30s", "profile": { "samples": 15420, "topFunctions": [ { "name": "processRequest", "selfTime": "2340ms", "totalTime": "8920ms", "percentage": "28.5%", "file": "handlers.js:45" }, { "name": "serializeResponse", "selfTime": "1890ms", "totalTime": "2100ms", "percentage": "22.1%", "file": "serializer.js:12" } ], "eventLoopDelay": { "mean": "2.3ms", "p99": "15.8ms", "max": "45.2ms" } }, "recommendations": [ { "function": "processRequest", "issue": "High CPU time in JSON parsing", "suggestion": "Consider streaming JSON parser for large payloads" } ], "artifacts": ["cpu-profile.cpuprofile", "flame.svg"] }
| Error | Cause | Resolution | |-------|-------|------------| | Cannot take heap snapshot | OOM condition | Increase memory limit | | Profiler already started | Multiple profile sessions | Stop existing profiler | | Event loop blocked | Sync operation | Use async alternative | | High GC time | Memory pressure | Reduce allocations, increase heap |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 19,292 | 25,872 | +34% | 1 | 1 | 0% | 4,150 | 6,687 | +61% | 0 | 0 | — |
case-02 | fail→fail | 17,564 | 16,845 | -4% | 1 | 1 | 0% | 3,618 | 7,027 | +94% | 0 | 0 | — |
case-03 | fail→pass | 6,249 | 10,181 | +63% | 1 | 1 | 0% | 280 | 5,662 | +1922% | 0 | 0 | — |
case-04 | fail→pass | 12,874 | 12,684 | -1% | 1 | 1 | 0% | 2,411 | 5,742 | +138% | 0 | 0 | — |
case-05 | pass→pass | 9,226 | 7,393 | -20% | 1 | 1 | 0% | 1,393 | 4,730 | +240% | 0 | 0 | — |
case-06 | pass→pass | 9,344 | 11,597 | +24% | 1 | 1 | 0% | 1,452 | 5,557 | +283% | 0 | 0 | — |
case-07 | pass→pass | 8,750 | 12,481 | +43% | 1 | 1 | 0% | 1,881 | 5,845 | +211% | 0 | 0 | — |
case-08 | pass→pass | 9,916 | 7,723 | -22% | 1 | 1 | 0% | 1,709 | 5,082 | +197% | 0 | 0 | — |
case-09 | fail→pass | 8,745 | 5,297 | -39% | 1 | 1 | 0% | 1,361 | 4,763 | +250% | 0 | 0 | — |
case-10 | pass→pass | 11,978 | 14,362 | +20% | 1 | 1 | 0% | 2,461 | 6,563 | +167% | 0 | 0 | — |
case-11 | pass→pass | 14,897 | 13,264 | -11% | 1 | 1 | 0% | 2,632 | 6,334 | +141% | 0 | 0 | — |
case-12 | fail→pass | 16,406 | 13,190 | -20% | 1 | 1 | 0% | 3,077 | 6,320 | +105% | 0 | 0 | — |
case-13 | pass→pass | 10,437 | 7,231 | -31% | 1 | 1 | 0% | 1,825 | 4,923 | +170% | 0 | 0 | — |
case-14 | pass→pass | 10,289 | 9,747 | -5% | 1 | 1 | 0% | 1,671 | 5,420 | +224% | 0 | 0 | — |
case-15 | pass→pass | 3,268 | 4,603 | +41% | 1 | 1 | 0% | 574 | 4,611 | +703% | 0 | 0 | — |
case-16 | pass→pass | 6,823 | 7,376 | +8% | 1 | 1 | 0% | 1,213 | 5,108 | +321% | 0 | 0 | — |
case-17 | pass→pass | 15,197 | 22,789 | +50% | 1 | 1 | 0% | 3,039 | 6,681 | +120% | 0 | 0 | — |
case-18 | pass→pass | 8,139 | 8,229 | +1% | 1 | 1 | 0% | 1,514 | 5,162 | +241% | 0 | 0 | — |
case-19 | pass→pass | 19,694 | 21,742 | +10% | 1 | 1 | 0% | 2,924 | 7,460 | +155% | 0 | 0 | — |
case-20 | pass→pass | 13,798 | 13,903 | +1% | 1 | 1 | 0% | 2,337 | 6,341 | +171% | 0 | 0 | — |
case-21 | pass→pass | 12,546 | 10,900 | -13% | 1 | 1 | 0% | 1,999 | 5,778 | +189% | 0 | 0 | — |
case-22 | pass→pass | 11,728 | 9,442 | -19% | 1 | 1 | 0% | 2,051 | 5,770 | +181% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +23 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.