Skip to content

Commit 746af47

Browse files
ci(NODE-6918): Add debug logging to before hook in create leak reproduction script (#4516)
Co-authored-by: Neal Beeken <[email protected]>
1 parent b648a63 commit 746af47

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

test/integration/node-specific/resource_tracking_script_builder.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { on, once } from 'node:events';
33
import { openSync } from 'node:fs';
44
import { readFile, unlink, writeFile } from 'node:fs/promises';
55
import * as path from 'node:path';
6+
import { inspect } from 'node:util';
67

78
import { AssertionError, expect } from 'chai';
89
import type * as timers from 'timers';
@@ -92,6 +93,18 @@ export async function runScriptAndReturnHeapInfo(
9293
func: HeapResourceTestFunction,
9394
{ iterations = 100 } = {}
9495
) {
96+
const log = (...args) => {
97+
const payload =
98+
args
99+
.map(item =>
100+
typeof item === 'string'
101+
? item
102+
: inspect(item, { depth: Infinity, breakLength: Infinity })
103+
)
104+
.join(', ') + '\n';
105+
process.stdout.write(payload);
106+
};
107+
log('starting');
95108
const scriptName = `${name}.cjs`;
96109
const heapsnapshotFile = `${name}.heapsnapshot.json`;
97110

@@ -105,31 +118,49 @@ export async function runScriptAndReturnHeapInfo(
105118
await writeFile(scriptName, scriptContent, { encoding: 'utf8' });
106119

107120
const processDiedController = new AbortController();
108-
const script = fork(scriptName, { execArgv: ['--expose-gc'] });
121+
const script = fork(scriptName, { execArgv: ['--expose-gc'], stdio: 'inherit' });
109122
// Interrupt our awaiting of messages if the process crashed
110123
script.once('close', exitCode => {
111124
if (exitCode !== 0) {
125+
log('process exited with non-zero: ', exitCode);
112126
processDiedController.abort(new Error(`process exited with: ${exitCode}`));
113127
}
114128
});
115129

130+
script.once('error', error => {
131+
log(`processed errored: ${error}`);
132+
processDiedController.abort(new Error(`process errored: `, { cause: error }));
133+
});
134+
135+
script.once('spawn', () => log('script spawned successfully.'));
136+
116137
const messages = on(script, 'message', { signal: processDiedController.signal });
117138
const willClose = once(script, 'close');
118139

140+
log('fetching messages 1...');
119141
const starting = await messages.next();
142+
log('fetching messages 2: ', starting);
143+
120144
const ending = await messages.next();
121145

146+
log('fetching messages 3: ', ending);
147+
122148
const startingMemoryUsed = starting.value[0].startingMemoryUsed;
123149
const endingMemoryUsed = ending.value[0].endingMemoryUsed;
124150

125151
// make sure the process ended
126152
const [exitCode] = await willClose;
153+
154+
log('child process closed.');
155+
127156
expect(exitCode, 'process should have exited with zero').to.equal(0);
128157

129158
const heap = await readFile(heapsnapshotFile, { encoding: 'utf8' }).then(c =>
130159
parseSnapshot(JSON.parse(c))
131160
);
132161

162+
log('done.');
163+
133164
// If any of the above throws we won't reach these unlinks that clean up the created files.
134165
// This is intentional so that when debugging the file will still be present to check it for errors
135166
await unlink(scriptName);

test/tools/fixtures/heap_resource_script.in.js

+27
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,63 @@ const func = FUNCTION_STRING;
77
const name = SCRIPT_NAME_STRING;
88
const uri = URI_STRING;
99
const iterations = ITERATIONS_STRING;
10+
const { inspect } = require('util');
1011

1112
const { MongoClient } = require(driverPath);
1213
const process = require('node:process');
1314
const v8 = require('node:v8');
1415
const util = require('node:util');
1516
const timers = require('node:timers');
1617

18+
const now = performance.now.bind(performance);
1719
const sleep = util.promisify(timers.setTimeout);
1820

1921
const run = func;
2022

2123
const MB = (2 ** 10) ** 2;
2224

25+
const log = (...args) => {
26+
const payload =
27+
args
28+
.map(item =>
29+
typeof item === 'string' ? item : inspect(item, { depth: Infinity, breakLength: Infinity })
30+
)
31+
.join(', ') + '\n';
32+
process.stdout.write('(subprocess): ' + payload);
33+
};
34+
2335
async function main() {
36+
log('starting execution');
2437
const startingMemoryUsed = process.memoryUsage().heapUsed / MB;
2538
process.send({ startingMemoryUsed });
2639

40+
log('sent first message');
41+
2742
for (let iteration = 0; iteration < iterations; iteration++) {
2843
await run({ MongoClient, uri, iteration });
44+
iteration % 20 === 0 && log(`iteration ${iteration} complete`);
2945
global.gc();
3046
}
3147

48+
log('script executed');
49+
3250
global.gc();
3351
// Sleep b/c maybe gc will run
3452
await sleep(100);
3553
global.gc();
3654

3755
const endingMemoryUsed = process.memoryUsage().heapUsed / MB;
56+
57+
log('sending second message');
58+
3859
process.send({ endingMemoryUsed });
60+
log('second message sent.');
61+
62+
const start = now();
3963
v8.writeHeapSnapshot(`${name}.heapsnapshot.json`);
64+
const end = now();
65+
66+
log(`heap snapshot written in ${end - start}ms. script exiting`);
4067
}
4168

4269
main()

0 commit comments

Comments
 (0)