diff options
author | legendecas <legendecas@gmail.com> | 2021-05-11 18:38:49 +0300 |
---|---|---|
committer | legendecas <legendecas@gmail.com> | 2021-05-21 06:41:33 +0300 |
commit | 50f076c4f59bbaaf129e858abfe8327b64d1113c (patch) | |
tree | bb2822b0f3f53a47aeb8459dcb83ed9dc8df03a6 /test | |
parent | d7611ec6e662a6c66ce27f2e965b611dbbc5a428 (diff) |
src: write named pipe info in diagnostic report
Writes pipe handles with `uv_pipe_getsockname()`
and `uv_pipe_getpeername()`.
PR-URL: https://github.com/nodejs/node/pull/38637
Fixes: https://github.com/nodejs/node/issues/38625
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/report/test-report-uncaught-exception-primitives.js | 2 | ||||
-rw-r--r-- | test/report/test-report-uncaught-exception-symbols.js | 2 | ||||
-rw-r--r-- | test/report/test-report-uv-handles.js | 179 |
3 files changed, 138 insertions, 45 deletions
diff --git a/test/report/test-report-uncaught-exception-primitives.js b/test/report/test-report-uncaught-exception-primitives.js index 75a05f335cf..8de67eeb6a2 100644 --- a/test/report/test-report-uncaught-exception-primitives.js +++ b/test/report/test-report-uncaught-exception-primitives.js @@ -15,7 +15,7 @@ process.on('uncaughtException', common.mustCall((err) => { assert.strictEqual(err, exception); const reports = helper.findReports(process.pid, tmpdir.path); assert.strictEqual(reports.length, 1); - console.log(reports[0]); + helper.validate(reports[0], [ ['header.event', 'Exception'], ['javascriptStack.message', `${exception}`], diff --git a/test/report/test-report-uncaught-exception-symbols.js b/test/report/test-report-uncaught-exception-symbols.js index 5997d0e0898..b1656172851 100644 --- a/test/report/test-report-uncaught-exception-symbols.js +++ b/test/report/test-report-uncaught-exception-symbols.js @@ -15,7 +15,7 @@ process.on('uncaughtException', common.mustCall((err) => { assert.strictEqual(err, exception); const reports = helper.findReports(process.pid, tmpdir.path); assert.strictEqual(reports.length, 1); - console.log(reports[0]); + helper.validate(reports[0], [ ['header.event', 'Exception'], ['javascriptStack.message', 'Symbol(foobar)'], diff --git a/test/report/test-report-uv-handles.js b/test/report/test-report-uv-handles.js index 32140cf887f..6a60aa4c4bc 100644 --- a/test/report/test-report-uv-handles.js +++ b/test/report/test-report-uv-handles.js @@ -2,18 +2,22 @@ // Testcase to check reporting of uv handles. const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const path = require('path'); if (common.isIBMi) common.skip('IBMi does not support fs.watch()'); -if (process.argv[2] === 'child') { - // Exit on loss of parent process - const exit = () => process.exit(2); - process.on('disconnect', exit); +// This is quite similar to common.PIPE except that it uses an extended prefix +// of "\\?\pipe" on windows. +const PIPE = (() => { + const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`); + const pipePrefix = common.isWindows ? '\\\\?\\pipe\\' : localRelative; + const pipeName = `node-test.${process.pid}.sock`; + return path.join(pipePrefix, pipeName); +})(); +function createFsHandle(childData) { const fs = require('fs'); - const http = require('http'); - const spawn = require('child_process').spawn; - // Watching files should result in fs_event/fs_poll uv handles. let watcher; try { @@ -22,59 +26,129 @@ if (process.argv[2] === 'child') { // fs.watch() unavailable } fs.watchFile(__filename, () => {}); + childData.skip_fs_watch = watcher === undefined; + + return () => { + if (watcher) watcher.close(); + fs.unwatchFile(__filename); + }; +} +function createChildProcessHandle(childData) { + const spawn = require('child_process').spawn; // Child should exist when this returns as child_process.pid must be set. - const child_process = spawn(process.execPath, - ['-e', "process.stdin.on('data', (x) => " + - 'console.log(x.toString()));']); + const cp = spawn(process.execPath, + ['-e', "process.stdin.on('data', (x) => " + + 'console.log(x.toString()));']); + childData.pid = cp.pid; + + return () => { + cp.kill(); + }; +} +function createTimerHandle() { const timeout = setInterval(() => {}, 1000); // Make sure the timer doesn't keep the test alive and let // us check we detect unref'd handles correctly. timeout.unref(); + return () => { + clearInterval(timeout); + }; +} + +function createTcpHandle(childData) { + const http = require('http'); + return new Promise((resolve) => { + // Simple server/connection to create tcp uv handles. + const server = http.createServer((req, res) => { + req.on('end', () => { + resolve(() => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); + server.close(); + }); + }); + req.resume(); + }); + server.listen(() => { + childData.tcp_address = server.address(); + http.get({ port: server.address().port }); + }); + }); +} + +function createUdpHandle(childData) { // Datagram socket for udp uv handles. const dgram = require('dgram'); - const udp_socket = dgram.createSocket('udp4'); - const connected_udp_socket = dgram.createSocket('udp4'); - udp_socket.bind({}, common.mustCall(() => { - connected_udp_socket.connect(udp_socket.address().port); - })); + const udpSocket = dgram.createSocket('udp4'); + const connectedUdpSocket = dgram.createSocket('udp4'); + + return new Promise((resolve) => { + udpSocket.bind({}, common.mustCall(() => { + connectedUdpSocket.connect(udpSocket.address().port); - // Simple server/connection to create tcp uv handles. - const server = http.createServer((req, res) => { - req.on('end', () => { - // Generate the report while the connection is active. - console.log(JSON.stringify(process.report.getReport(), null, 2)); - child_process.kill(); - - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.end(); - - // Tidy up to allow process to exit cleanly. - server.close(() => { - if (watcher) watcher.close(); - fs.unwatchFile(__filename); - connected_udp_socket.close(); - udp_socket.close(); - process.removeListener('disconnect', exit); + childData.udp_address = udpSocket.address(); + resolve(() => { + connectedUdpSocket.close(); + udpSocket.close(); + }); + })); + }); +} + +function createNamedPipeHandle(childData) { + const net = require('net'); + const sockPath = PIPE; + return new Promise((resolve) => { + const server = net.createServer((socket) => { + childData.pipe_sock_path = server.address(); + resolve(() => { + socket.end(); + server.close(); }); }); - req.resume(); + server.listen( + sockPath, + () => { + net.connect(sockPath, (socket) => {}); + }); }); - server.listen(() => { - const data = { pid: child_process.pid, - tcp_address: server.address(), - udp_address: udp_socket.address(), - skip_fs_watch: (watcher === undefined) }; - process.send(data); - http.get({ port: server.address().port }); +} + +async function child() { + // Exit on loss of parent process + const exit = () => process.exit(2); + process.on('disconnect', exit); + + const childData = {}; + const disposes = await Promise.all([ + createFsHandle(childData), + createChildProcessHandle(childData), + createTimerHandle(childData), + createTcpHandle(childData), + createUdpHandle(childData), + createNamedPipeHandle(childData), + ]); + process.send(childData); + + // Generate the report while the connection is active. + console.log(JSON.stringify(process.report.getReport(), null, 2)); + + // Tidy up to allow process to exit cleanly. + disposes.forEach((it) => { + it(); }); + process.removeListener('disconnect', exit); +} + +if (process.argv[2] === 'child') { + child(); } else { const helper = require('../common/report.js'); const fork = require('child_process').fork; const assert = require('assert'); - const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); const options = { encoding: 'utf8', silent: true, cwd: tmpdir.path }; const child = fork(__filename, ['child'], options); @@ -86,11 +160,11 @@ if (process.argv[2] === 'child') { const report_msg = 'Report files were written: unexpectedly'; child.stdout.on('data', (chunk) => { stdout += chunk; }); child.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(stderr.trim(), ''); assert.deepStrictEqual(code, 0, 'Process exited unexpectedly with code: ' + `${code}`); assert.deepStrictEqual(signal, null, 'Process should have exited cleanly,' + ` but did not: ${signal}`); - assert.strictEqual(stderr.trim(), ''); const reports = helper.findReports(child.pid, tmpdir.path); assert.deepStrictEqual(reports, [], report_msg, reports); @@ -116,6 +190,7 @@ if (process.argv[2] === 'child') { const expected_filename = `${prefix}${__filename}`; const found_tcp = []; const found_udp = []; + const found_named_pipe = []; // Functions are named to aid debugging when they are not called. const validators = { fs_event: common.mustCall(function fs_event_validator(handle) { @@ -133,6 +208,21 @@ if (process.argv[2] === 'child') { }), pipe: common.mustCallAtLeast(function pipe_validator(handle) { assert(handle.is_referenced); + // Pipe handles. The report should contain three pipes: + // 1. The server's listening pipe. + // 2. The inbound pipe making the request. + // 3. The outbound pipe sending the response. + // + // There is no way to distinguish inbound and outbound in a cross + // platform manner, so we just check inbound here. + const sockPath = child_data.pipe_sock_path; + if (handle.localEndpoint === sockPath) { + if (handle.writable === false) { + found_named_pipe.push('listening'); + } + } else if (handle.remoteEndpoint === sockPath) { + found_named_pipe.push('inbound'); + } }), process: common.mustCall(function process_validator(handle) { assert.strictEqual(handle.pid, child_data.pid); @@ -172,7 +262,7 @@ if (process.argv[2] === 'child') { assert(handle.is_referenced); }, 2), }; - console.log(report.libuv); + for (const entry of report.libuv) { if (validators[entry.type]) validators[entry.type](entry); } @@ -182,6 +272,9 @@ if (process.argv[2] === 'child') { for (const socket of ['connected', 'unconnected']) { assert(found_udp.includes(socket), `${socket} UDP socket was not found`); } + for (const socket of ['listening', 'inbound']) { + assert(found_named_pipe.includes(socket), `${socket} named pipe socket was not found`); + } // Common report tests. helper.validateContent(stdout); |