Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 10b2bb5

Browse filesBrowse files
marcopiracciniaduh95
authored andcommitted
child_process: add tracing channel for spawn
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com> PR-URL: #61836 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 1f2025f commit 10b2bb5
Copy full SHA for 10b2bb5

3 files changed

+141Lines changed: 141 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/api/diagnostics_channel.md‎

Copy file name to clipboardExpand all lines: doc/api/diagnostics_channel.md
+23Lines changed: 23 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,28 @@ added: v16.18.0
14131413

14141414
Emitted when a new process is created.
14151415

1416+
`tracing:child_process.spawn:start`
1417+
1418+
* `process` {ChildProcess}
1419+
* `options` {Object}
1420+
1421+
Emitted when [`child_process.spawn()`][] is invoked, before the process is
1422+
actually spawned.
1423+
1424+
`tracing:child_process.spawn:end`
1425+
1426+
* `process` {ChildProcess}
1427+
1428+
Emitted when [`child_process.spawn()`][] has completed successfully and the
1429+
process has been created.
1430+
1431+
`tracing:child_process.spawn:error`
1432+
1433+
* `process` {ChildProcess}
1434+
* `error` {Error}
1435+
1436+
Emitted when [`child_process.spawn()`][] encounters an error.
1437+
14161438
##### Event: `'execve'`
14171439

14181440
* `execPath` {string}
@@ -1444,6 +1466,7 @@ Emitted when a new thread is created.
14441466
[`channel.runStores(context, ...)`]: #channelrunstorescontext-fn-thisarg-args
14451467
[`channel.subscribe(onMessage)`]: #channelsubscribeonmessage
14461468
[`channel.unsubscribe(onMessage)`]: #channelunsubscribeonmessage
1469+
[`child_process.spawn()`]: child_process.md#child_processspawncommand-args-options
14471470
[`diagnostics_channel.channel(name)`]: #diagnostics_channelchannelname
14481471
[`diagnostics_channel.subscribe(name, onMessage)`]: #diagnostics_channelsubscribename-onmessage
14491472
[`diagnostics_channel.tracingChannel()`]: #diagnostics_channeltracingchannelnameorchannels
Collapse file

‎lib/internal/child_process.js‎

Copy file name to clipboardExpand all lines: lib/internal/child_process.js
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const spawn_sync = internalBinding('spawn_sync');
6262
const { kStateSymbol } = require('internal/dgram');
6363
const dc = require('diagnostics_channel');
6464
const childProcessChannel = dc.channel('child_process');
65+
const childProcessSpawn = dc.tracingChannel('child_process.spawn');
6566

6667
const {
6768
UV_EACCES,
@@ -393,6 +394,10 @@ ChildProcess.prototype.spawn = function spawn(options) {
393394
this.spawnargs = options.args;
394395
}
395396

397+
if (childProcessSpawn.hasSubscribers) {
398+
childProcessSpawn.start.publish({ process: this, options });
399+
}
400+
396401
const err = this._handle.spawn(options);
397402

398403
// Run-time errors should emit an error, not throw an exception.
@@ -401,6 +406,13 @@ ChildProcess.prototype.spawn = function spawn(options) {
401406
err === UV_EMFILE ||
402407
err === UV_ENFILE ||
403408
err === UV_ENOENT) {
409+
if (childProcessSpawn.hasSubscribers) {
410+
childProcessSpawn.error.publish({
411+
process: this,
412+
error: new ErrnoException(err, 'spawn'),
413+
});
414+
}
415+
404416
process.nextTick(onErrorNT, this, err);
405417

406418
// There is no point in continuing when we've hit EMFILE or ENFILE
@@ -418,8 +430,20 @@ ChildProcess.prototype.spawn = function spawn(options) {
418430

419431
this._handle.close();
420432
this._handle = null;
433+
434+
if (childProcessSpawn.hasSubscribers) {
435+
childProcessSpawn.error.publish({
436+
process: this,
437+
error: new ErrnoException(err, 'spawn'),
438+
});
439+
}
440+
421441
throw new ErrnoException(err, 'spawn');
422442
} else {
443+
if (childProcessSpawn.hasSubscribers) {
444+
childProcessSpawn.end.publish({ process: this });
445+
}
446+
423447
process.nextTick(onSpawnNT, this);
424448
}
425449

Collapse file
+94Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const { spawn, ChildProcess } = require('child_process');
5+
const dc = require('diagnostics_channel');
6+
const path = require('path');
7+
const fs = require('fs');
8+
const tmpdir = require('../common/tmpdir');
9+
10+
const isChildProcess = (process) => process instanceof ChildProcess;
11+
12+
function testDiagnosticChannel(subscribers, test, after) {
13+
dc.tracingChannel('child_process.spawn').subscribe(subscribers);
14+
15+
test(common.mustCall(() => {
16+
dc.tracingChannel('child_process.spawn').unsubscribe(subscribers);
17+
after?.();
18+
}));
19+
}
20+
21+
const testSuccessfulSpawn = common.mustCall(() => {
22+
let cb;
23+
24+
testDiagnosticChannel(
25+
{
26+
start: common.mustCall(({ process: childProcess, options }) => {
27+
assert.strictEqual(isChildProcess(childProcess), true);
28+
assert.strictEqual(options.file, process.execPath);
29+
}),
30+
end: common.mustCall(({ process: childProcess }) => {
31+
assert.strictEqual(isChildProcess(childProcess), true);
32+
}),
33+
error: common.mustNotCall(),
34+
},
35+
common.mustCall((callback) => {
36+
cb = callback;
37+
const child = spawn(process.execPath, ['-e', 'process.exit(0)']);
38+
child.on('close', () => {
39+
cb();
40+
});
41+
}),
42+
testFailingSpawnENOENT
43+
);
44+
});
45+
46+
const testFailingSpawnENOENT = common.mustCall(() => {
47+
testDiagnosticChannel(
48+
{
49+
start: common.mustCall(({ process: childProcess, options }) => {
50+
assert.strictEqual(isChildProcess(childProcess), true);
51+
assert.strictEqual(options.file, 'does-not-exist');
52+
}),
53+
end: common.mustNotCall(),
54+
error: common.mustCall(({ process: childProcess, error }) => {
55+
assert.strictEqual(isChildProcess(childProcess), true);
56+
assert.strictEqual(error.code, 'ENOENT');
57+
}),
58+
},
59+
common.mustCall((callback) => {
60+
const child = spawn('does-not-exist');
61+
child.on('error', () => {});
62+
callback();
63+
}),
64+
common.isWindows ? undefined : testFailingSpawnEACCES,
65+
);
66+
});
67+
68+
const testFailingSpawnEACCES = !common.isWindows ? common.mustCall(() => {
69+
tmpdir.refresh();
70+
const noExecFile = path.join(tmpdir.path, 'no-exec');
71+
fs.writeFileSync(noExecFile, '');
72+
fs.chmodSync(noExecFile, 0o644);
73+
74+
testDiagnosticChannel(
75+
{
76+
start: common.mustCall(({ process: childProcess, options }) => {
77+
assert.strictEqual(isChildProcess(childProcess), true);
78+
assert.strictEqual(options.file, noExecFile);
79+
}),
80+
end: common.mustNotCall(),
81+
error: common.mustCall(({ process: childProcess, error }) => {
82+
assert.strictEqual(isChildProcess(childProcess), true);
83+
assert.strictEqual(error.code, 'EACCES');
84+
}),
85+
},
86+
common.mustCall((callback) => {
87+
const child = spawn(noExecFile);
88+
child.on('error', () => {});
89+
callback();
90+
}),
91+
);
92+
}) : undefined;
93+
94+
testSuccessfulSpawn();

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.