Skip to content

Commit be16a19

Browse files
trop[bot]codebytere
authored andcommitted
fix: ensure the inspector agent is shutdown before cleaning up the node env (electron#18077)
* fix: ensure the inspector agent is shutdown before cleaning up the node env * spec: add tests to ensure clean shutdown with connected inspector agent * Update node_debugger.cc
1 parent 2f10c0f commit be16a19

File tree

6 files changed

+55
-2
lines changed

6 files changed

+55
-2
lines changed

atom/app/node_main.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ int NodeMain(int argc, char* argv[]) {
101101
}
102102
} while (more == true);
103103

104+
node_debugger.Stop();
104105
exit_code = node::EmitExit(env);
105106
node::RunAtExit(env);
106107
gin_env.platform()->DrainTasks(env->isolate());

atom/browser/atom_browser_main_parts.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ void AtomBrowserMainParts::PostMainMessageLoopRun() {
469469
ui::SetX11ErrorHandlers(X11EmptyErrorHandler, X11EmptyIOErrorHandler);
470470
#endif
471471

472+
node_debugger_->Stop();
472473
js_env_->OnMessageLoopDestroying();
473474

474475
#if defined(OS_MACOSX)

atom/browser/node_debugger.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,10 @@ void NodeDebugger::Start() {
5858
DCHECK(env_->inspector_agent()->IsListening());
5959
}
6060

61+
void NodeDebugger::Stop() {
62+
auto* inspector = env_->inspector_agent();
63+
if (inspector && inspector->IsListening())
64+
inspector->Stop();
65+
}
66+
6167
} // namespace atom

atom/browser/node_debugger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class NodeDebugger {
2020
~NodeDebugger();
2121

2222
void Start();
23+
void Stop();
2324

2425
private:
2526
node::Environment* env_;

spec/fixtures/module/delay-exit.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { app } = require('electron')
2+
3+
process.on('message', () => app.quit())

spec/node-spec.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const os = require('os')
88
const { ipcRenderer, remote } = require('electron')
99
const features = process.electronBinding('features')
1010

11+
const { emittedOnce } = require('./events-helpers')
12+
1113
const isCI = remote.getGlobal('isCi')
1214
chai.use(dirtyChai)
1315

@@ -231,15 +233,22 @@ describe('node feature', () => {
231233

232234
describe('inspector', () => {
233235
let child = null
236+
let exitPromise = null
234237

235238
beforeEach(function () {
236239
if (!features.isRunAsNodeEnabled()) {
237240
this.skip()
238241
}
239242
})
240243

241-
afterEach(() => {
242-
if (child !== null) child.kill()
244+
afterEach(async () => {
245+
if (child && exitPromise) {
246+
const [code, signal] = await exitPromise
247+
expect(signal).to.equal(null)
248+
expect(code).to.equal(0)
249+
} else if (child) {
250+
child.kill()
251+
}
243252
})
244253

245254
it('supports starting the v8 inspector with --inspect/--inspect-brk', (done) => {
@@ -275,6 +284,7 @@ describe('node feature', () => {
275284
ELECTRON_RUN_AS_NODE: true
276285
}
277286
})
287+
exitPromise = emittedOnce(child, 'exit')
278288

279289
let output = ''
280290
function cleanup () {
@@ -299,6 +309,7 @@ describe('node feature', () => {
299309

300310
it('does not start the v8 inspector when --inspect is after a -- argument', (done) => {
301311
child = ChildProcess.spawn(remote.process.execPath, [path.join(__dirname, 'fixtures', 'module', 'noop.js'), '--', '--inspect'])
312+
exitPromise = emittedOnce(child, 'exit')
302313

303314
let output = ''
304315
function dataListener (data) {
@@ -315,13 +326,43 @@ describe('node feature', () => {
315326
})
316327
})
317328

329+
it('does does not crash when quitting with the inspector connected', function (done) {
330+
// IPC Electron child process not supported on Windows
331+
if (process.platform === 'win32') return this.skip()
332+
child = ChildProcess.spawn(remote.process.execPath, [path.join(__dirname, 'fixtures', 'module', 'delay-exit'), '--inspect=0'], {
333+
stdio: ['ipc']
334+
})
335+
exitPromise = emittedOnce(child, 'exit')
336+
337+
let output = ''
338+
function dataListener (data) {
339+
output += data
340+
341+
if (output.trim().startsWith('Debugger listening on ws://') && output.endsWith('\n')) {
342+
const socketMatch = output.trim().match(/(ws:\/\/.+:[0-9]+\/.+?)\n/gm)
343+
if (socketMatch && socketMatch[0]) {
344+
child.stderr.removeListener('data', dataListener)
345+
child.stdout.removeListener('data', dataListener)
346+
const connection = new WebSocket(socketMatch[0])
347+
connection.onopen = () => {
348+
child.send('plz-quit')
349+
done()
350+
}
351+
}
352+
}
353+
}
354+
child.stderr.on('data', dataListener)
355+
child.stdout.on('data', dataListener)
356+
})
357+
318358
it('supports js binding', (done) => {
319359
child = ChildProcess.spawn(process.execPath, ['--inspect', path.join(__dirname, 'fixtures', 'module', 'inspector-binding.js')], {
320360
env: {
321361
ELECTRON_RUN_AS_NODE: true
322362
},
323363
stdio: ['ipc']
324364
})
365+
exitPromise = emittedOnce(child, 'exit')
325366

326367
child.on('message', ({ cmd, debuggerEnabled, success }) => {
327368
if (cmd === 'assert') {

0 commit comments

Comments
 (0)