Skip to content

Commit 50a981e

Browse files
authored
Merge pull request #13786 from guardian/perf-gc-report
Add a script to summarise garbage collection reports using `node --trace-gc`
2 parents 9feea7d + b67e986 commit 50a981e

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* eslint-disable -- perf script */
2+
const fs = require('node:fs');
3+
const readline = require('node:readline');
4+
5+
const logFilePath = process.argv[2];
6+
if (!logFilePath) {
7+
console.error('Usage: node gc-summary.js <trace-gc-log-file>');
8+
process.exit(1);
9+
}
10+
11+
// Global stats
12+
let totalGCCount = 0;
13+
let totalPauseTime = 0;
14+
let maxPauseTime = 0;
15+
16+
// Per-type stats
17+
const gcStatsByType = {};
18+
19+
const lineReader = readline.createInterface({
20+
input: fs.createReadStream(logFilePath),
21+
crlfDelay: Infinity,
22+
});
23+
24+
lineReader.on('line', (line) => {
25+
// Match GC type: e.g., "Scavenge", "Mark-Compact (reduce)", etc.
26+
const typeMatch = line.match(/:\s*(\w+(?:-\w+)?(?:\s*\(\w+\))?)/);
27+
const pauseMatch =
28+
line.match(/([0-9]+\.[0-9]+)\s*\/\s*[0-9.]+\s*ms/) ||
29+
line.match(/([0-9]+\.[0-9]+)\s*ms/);
30+
31+
if (pauseMatch && typeMatch) {
32+
const pause = parseFloat(pauseMatch[1]);
33+
const type = typeMatch[1].trim();
34+
35+
totalGCCount++;
36+
totalPauseTime += pause;
37+
if (pause > maxPauseTime) maxPauseTime = pause;
38+
39+
if (!gcStatsByType[type]) {
40+
gcStatsByType[type] = {
41+
count: 0,
42+
totalPause: 0,
43+
maxPause: 0,
44+
};
45+
}
46+
47+
gcStatsByType[type].count++;
48+
gcStatsByType[type].totalPause += pause;
49+
if (pause > gcStatsByType[type].maxPause) {
50+
gcStatsByType[type].maxPause = pause;
51+
}
52+
}
53+
});
54+
55+
lineReader.on('close', () => {
56+
console.log(`🧹 Total GC Events: ${totalGCCount}`);
57+
console.log(`⏱️ Total GC Pause Time: ${totalPauseTime.toFixed(2)} ms`);
58+
console.log(
59+
`📊 Average GC Pause: ${(totalGCCount
60+
? totalPauseTime / totalGCCount
61+
: 0
62+
).toFixed(2)} ms`,
63+
);
64+
console.log(`🚨 Longest GC Pause: ${maxPauseTime.toFixed(2)} ms`);
65+
console.log('\n📚 GC Breakdown by Type:\n');
66+
67+
for (const [type, stats] of Object.entries(gcStatsByType)) {
68+
const avg = stats.totalPause / stats.count;
69+
console.log(`👉 ${type}`);
70+
console.log(` - Count: ${stats.count}`);
71+
console.log(` - Total Pause: ${stats.totalPause.toFixed(2)} ms`);
72+
console.log(` - Average Pause: ${avg.toFixed(2)} ms`);
73+
console.log(` - Max Pause: ${stats.maxPause.toFixed(2)} ms\n`);
74+
}
75+
});

0 commit comments

Comments
 (0)