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 b771529

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 0d97ec4 commit b771529
Copy full SHA for b771529

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
@@ -1417,6 +1417,28 @@ added: v16.18.0
14171417

14181418
Emitted when a new process is created.
14191419

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

14221444
* `execPath` {string}
@@ -1448,6 +1470,7 @@ Emitted when a new thread is created.
14481470
[`channel.runStores(context, ...)`]: #channelrunstorescontext-fn-thisarg-args
14491471
[`channel.subscribe(onMessage)`]: #channelsubscribeonmessage
14501472
[`channel.unsubscribe(onMessage)`]: #channelunsubscribeonmessage
1473+
[`child_process.spawn()`]: child_process.md#child_processspawncommand-args-options
14511474
[`diagnostics_channel.channel(name)`]: #diagnostics_channelchannelname
14521475
[`diagnostics_channel.subscribe(name, onMessage)`]: #diagnostics_channelsubscribename-onmessage
14531476
[`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
@@ -61,6 +61,7 @@ const spawn_sync = internalBinding('spawn_sync');
6161
const { kStateSymbol } = require('internal/dgram');
6262
const dc = require('diagnostics_channel');
6363
const childProcessChannel = dc.channel('child_process');
64+
const childProcessSpawn = dc.tracingChannel('child_process.spawn');
6465

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

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

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

405417
// There is no point in continuing when we've hit EMFILE or ENFILE
@@ -417,8 +429,20 @@ ChildProcess.prototype.spawn = function spawn(options) {
417429

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

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.