From cf057b4c07e3d448f7ca35c7091cbed221701433 Mon Sep 17 00:00:00 2001 From: Danielle Adams Date: Tue, 26 Jul 2022 18:04:41 -0400 Subject: [PATCH] lib: add diagnostics channel and perf hooks detail PR-URL: #43984 Reviewed-By: Matteo Collina matteo.collina@gmail.com Reviewed-By: Mohammed Keyvanzadeh mohammadkeyvanzade94@gmail.com Reviewed-By: Minwoo Jung nodecorelab@gmail.com --- doc/api/diagnostics_channel.md | 18 ++++++++ doc/api/perf_hooks.md | 8 ++-- lib/dgram.js | 8 ++++ lib/dns.js | 8 ++-- lib/internal/dns/promises.js | 8 ++-- lib/net.js | 16 ++++++- test/parallel/test-bootstrap-modules.js | 1 + test/parallel/test-diagnostics-channel-net.js | 28 +++++++++++++ test/parallel/test-diagnostics-channel-udp.js | 15 +++++++ test/parallel/test-dns-perf_hooks.js | 42 +++++++++++++++++-- 10 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 test/parallel/test-diagnostics-channel-net.js create mode 100644 test/parallel/test-diagnostics-channel-udp.js diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index 3bcf6b7966a21b..cdce9d6b1c86a5 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -428,6 +428,24 @@ Emitted when server receives a request. Emitted when server sends a response. +`net.client.socket` + +* `socket` {net.Socket} + +Emitted when a new TCP or pipe client socket is created. + +`net.server.socket` + +* `socket` {net.Socket} + +Emitted when a new TCP or pipe connection is received. + +`udp.socket` + +* `socket` {dgram.Socket} + +Emitted when a new UDP socket is created. + [`'uncaughtException'`]: process.md#event-uncaughtexception [`channel.subscribe(onMessage)`]: #channelsubscribeonmessage [`diagnostics_channel.channel(name)`]: #diagnostics_channelchannelname diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index a56b1f232a1b06..bb5648f0a74439 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -602,13 +602,15 @@ When `performanceEntry.type` is equal to `'dns'`, the additional information. If `performanceEntry.name` is equal to `lookup`, the `detail` -will contain the following properties: `hostname`, `family`, `hints`, `verbatim`. +will contain the following properties: `hostname`, `family`, `hints`, `verbatim`, +`addresses`. If `performanceEntry.name` is equal to `lookupService`, the `detail` will -contain the following properties: `host`, `port`. +contain the following properties: `host`, `port`, `hostname`, `service`. If `performanceEntry.name` is equal to `queryxxx` or `getHostByAddr`, the `detail` will -contain the following properties: `host`, `ttl`. +contain the following properties: `host`, `ttl`, `result`. The value of `result` is +same as the result of `queryxxx` or `getHostByAddr`. ## Class: `PerformanceNodeTiming` diff --git a/lib/dgram.js b/lib/dgram.js index 5f644d2a6c30e3..9a11a2287c6155 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -74,6 +74,9 @@ const { SendWrap } = internalBinding('udp_wrap'); +const dc = require('diagnostics_channel'); +const udpSocketChannel = dc.channel('udp.socket'); + const BIND_STATE_UNBOUND = 0; const BIND_STATE_BINDING = 1; const BIND_STATE_BOUND = 2; @@ -145,6 +148,11 @@ function Socket(type, listener) { this.once('close', () => signal.removeEventListener('abort', onAborted)); } } + if (udpSocketChannel.hasSubscribers) { + udpSocketChannel.publish({ + socket: this, + }); + } } ObjectSetPrototypeOf(Socket.prototype, EventEmitter.prototype); ObjectSetPrototypeOf(Socket, EventEmitter); diff --git a/lib/dns.js b/lib/dns.js index 97793eb12aaf3a..5fe34b4b049358 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -112,7 +112,7 @@ function onlookup(err, addresses) { } this.callback(null, addresses[0], this.family || isIP(addresses[0])); if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupContext); + stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } }); } } @@ -133,7 +133,7 @@ function onlookupall(err, addresses) { this.callback(null, addresses); if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupContext); + stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } }); } } @@ -251,7 +251,7 @@ function onlookupservice(err, hostname, service) { this.callback(null, hostname, service); if (this[kPerfHooksDnsLookupServiceContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupServiceContext); + stopPerf(this, kPerfHooksDnsLookupServiceContext, { detail: { hostname, service } }); } } @@ -304,7 +304,7 @@ function onresolve(err, result, ttls) { else { this.callback(null, result); if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupResolveContext); + stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } }); } } } diff --git a/lib/internal/dns/promises.js b/lib/internal/dns/promises.js index 334260923faa7b..ac40bc5541b997 100644 --- a/lib/internal/dns/promises.js +++ b/lib/internal/dns/promises.js @@ -89,7 +89,7 @@ function onlookup(err, addresses) { const family = this.family || isIP(addresses[0]); this.resolve({ address: addresses[0], family }); if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupContext); + stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } }); } } @@ -112,7 +112,7 @@ function onlookupall(err, addresses) { this.resolve(addresses); if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupContext); + stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } }); } } @@ -205,7 +205,7 @@ function onlookupservice(err, hostname, service) { this.resolve({ hostname, service }); if (this[kPerfHooksDnsLookupServiceContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupServiceContext); + stopPerf(this, kPerfHooksDnsLookupServiceContext, { detail: { hostname, service } }); } } @@ -261,7 +261,7 @@ function onresolve(err, result, ttls) { this.resolve(result); if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) { - stopPerf(this, kPerfHooksDnsLookupResolveContext); + stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } }); } } diff --git a/lib/net.js b/lib/net.js index cf48edb0274cc7..e84cbd80b847c6 100644 --- a/lib/net.js +++ b/lib/net.js @@ -133,6 +133,11 @@ const isWindows = process.platform === 'win32'; const noop = () => {}; const kPerfHooksNetConnectContext = Symbol('kPerfHooksNetConnectContext'); + +const dc = require('diagnostics_channel'); +const netClientSocketChannel = dc.channel('net.client.socket'); +const netServerSocketChannel = dc.channel('net.server.socket'); + const { hasObserver, startPerf, @@ -204,7 +209,11 @@ function connect(...args) { const options = normalized[0]; debug('createConnection', normalized); const socket = new Socket(options); - + if (netClientSocketChannel.hasSubscribers) { + netClientSocketChannel.publish({ + socket, + }); + } if (options.timeout) { socket.setTimeout(options.timeout); } @@ -1714,6 +1723,11 @@ function onconnection(err, clientHandle) { DTRACE_NET_SERVER_CONNECTION(socket); self.emit('connection', socket); + if (netServerSocketChannel.hasSubscribers) { + netServerSocketChannel.publish({ + socket, + }); + } } /** diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 495f08f716f573..ddccd22ea8ffe1 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -170,6 +170,7 @@ const expectedModules = new Set([ 'NativeModule v8', 'NativeModule internal/v8/startup_snapshot', 'NativeModule vm', + 'NativeModule diagnostics_channel', ]); if (!common.isMainThread) { diff --git a/test/parallel/test-diagnostics-channel-net.js b/test/parallel/test-diagnostics-channel-net.js new file mode 100644 index 00000000000000..c03078a12659ac --- /dev/null +++ b/test/parallel/test-diagnostics-channel-net.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const dc = require('diagnostics_channel'); + +const netClientSocketChannel = dc.channel('net.client.socket'); +const netServerSocketChannel = dc.channel('net.server.socket'); + +const isNetSocket = (socket) => socket instanceof net.Socket; + +netClientSocketChannel.subscribe(common.mustCall(({ socket }) => { + assert.strictEqual(isNetSocket(socket), true); +})); + +netServerSocketChannel.subscribe(common.mustCall(({ socket }) => { + assert.strictEqual(isNetSocket(socket), true); +})); + +const server = net.createServer(common.mustCall((socket) => { + socket.destroy(); + server.close(); +})); + +server.listen(() => { + const { port } = server.address(); + net.connect(port); +}); diff --git a/test/parallel/test-diagnostics-channel-udp.js b/test/parallel/test-diagnostics-channel-udp.js new file mode 100644 index 00000000000000..79869c6d807926 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-udp.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const dc = require('diagnostics_channel'); + +const udpSocketChannel = dc.channel('udp.socket'); + +const isUDPSocket = (socket) => socket instanceof dgram.Socket; + +udpSocketChannel.subscribe(common.mustCall(({ socket }) => { + assert.strictEqual(isUDPSocket(socket), true); +})); +const socket = dgram.createSocket('udp4'); +socket.close(); diff --git a/test/parallel/test-dns-perf_hooks.js b/test/parallel/test-dns-perf_hooks.js index d90b7dfe3dea95..7636020bf6fe31 100644 --- a/test/parallel/test-dns-perf_hooks.js +++ b/test/parallel/test-dns-perf_hooks.js @@ -6,21 +6,55 @@ const dns = require('dns'); const { PerformanceObserver } = require('perf_hooks'); const entries = []; -const obs = new PerformanceObserver(common.mustCallAtLeast((items) => { +const obs = new PerformanceObserver((items) => { entries.push(...items.getEntries()); -})); +}); obs.observe({ type: 'dns' }); -dns.lookup('localhost', () => {}); +let count = 0; + +function inc() { + count++; +} + +// If DNS resolution fails, skip it +// https://github.com/nodejs/node/issues/44003 +dns.lookup('localhost', common.mustCall((err) => { !err && inc(); })); +dns.lookupService('127.0.0.1', 80, common.mustCall((err) => { !err && inc(); })); +dns.resolveAny('localhost', common.mustCall((err) => { !err && inc(); })); + +dns.promises.lookup('localhost').then(inc).catch(() => {}); +dns.promises.lookupService('127.0.0.1', 80).then(inc).catch(() => {}); +dns.promises.resolveAny('localhost').then(inc).catch(() => {}); process.on('exit', () => { - assert.strictEqual(entries.length, 1); + assert.strictEqual(entries.length, count); entries.forEach((entry) => { assert.strictEqual(!!entry.name, true); assert.strictEqual(entry.entryType, 'dns'); assert.strictEqual(typeof entry.startTime, 'number'); assert.strictEqual(typeof entry.duration, 'number'); assert.strictEqual(typeof entry.detail, 'object'); + switch (entry.name) { + case 'lookup': + assert.strictEqual(typeof entry.detail.hostname, 'string'); + assert.strictEqual(typeof entry.detail.family, 'number'); + assert.strictEqual(typeof entry.detail.hints, 'number'); + assert.strictEqual(typeof entry.detail.verbatim, 'boolean'); + assert.strictEqual(Array.isArray(entry.detail.addresses), true); + break; + case 'lookupService': + assert.strictEqual(typeof entry.detail.host, 'string'); + assert.strictEqual(typeof entry.detail.port, 'number'); + assert.strictEqual(typeof entry.detail.hostname, 'string'); + assert.strictEqual(typeof entry.detail.service, 'string'); + break; + case 'queryAny': + assert.strictEqual(typeof entry.detail.host, 'string'); + assert.strictEqual(typeof entry.detail.ttl, 'boolean'); + assert.strictEqual(Array.isArray(entry.detail.result), true); + break; + } }); });