Skip to content

Commit b240868

Browse files
theanarkhtargos
authored andcommitted
net,dns: trace tcp connection and dns by perf_hooks
use the perf_hooks to trace the time spent by net.connect, dns.lookup, dns.lookupService, dns.resolvexxx. PR-URL: #42390 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Paolo Insogna <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
1 parent 14a929b commit b240868

File tree

8 files changed

+272
-3
lines changed

8 files changed

+272
-3
lines changed

doc/api/perf_hooks.md

+60
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,30 @@ When `performanceEntry.type` is equal to `'function'`, the
531531
`performanceEntry.detail` property will be an {Array} listing
532532
the input arguments to the timed function.
533533

534+
### Net ('net') Details
535+
536+
When `performanceEntry.type` is equal to `'net'`, the
537+
`performanceEntry.detail` property will be an {Object} containing
538+
additional information.
539+
540+
If `performanceEntry.name` is equal to `connect`, the `detail`
541+
will contain the following properties: `host`, `port`.
542+
543+
### DNS ('dns') Details
544+
545+
When `performanceEntry.type` is equal to `'dns'`, the
546+
`performanceEntry.detail` property will be an {Object} containing
547+
additional information.
548+
549+
If `performanceEntry.name` is equal to `lookup`, the `detail`
550+
will contain the following properties: `hostname`, `family`, `hints`, `verbatim`.
551+
552+
If `performanceEntry.name` is equal to `lookupService`, the `detail` will
553+
contain the following properties: `host`, `port`.
554+
555+
If `performanceEntry.name` is equal to `queryxxx` or `getHostByAddr`, the `detail` will
556+
contain the following properties: `host`, `ttl`.
557+
534558
## Class: `PerformanceNodeTiming`
535559

536560
<!-- YAML
@@ -1276,6 +1300,42 @@ http.createServer((req, res) => {
12761300
});
12771301
```
12781302

1303+
### Measuring how long the `net.connect` (only for TCP) takes when the connection is successful
1304+
1305+
```js
1306+
'use strict';
1307+
const { PerformanceObserver } = require('perf_hooks');
1308+
const net = require('net');
1309+
const obs = new PerformanceObserver((items) => {
1310+
items.getEntries().forEach((item) => {
1311+
console.log(item);
1312+
});
1313+
});
1314+
obs.observe({ entryTypes: ['net'] });
1315+
const PORT = 8080;
1316+
net.createServer((socket) => {
1317+
socket.destroy();
1318+
}).listen(PORT, () => {
1319+
net.connect(PORT);
1320+
});
1321+
```
1322+
1323+
### Measuring how long the DNS takes when the request is successful
1324+
1325+
```js
1326+
'use strict';
1327+
const { PerformanceObserver } = require('perf_hooks');
1328+
const dns = require('dns');
1329+
const obs = new PerformanceObserver((items) => {
1330+
items.getEntries().forEach((item) => {
1331+
console.log(item);
1332+
});
1333+
});
1334+
obs.observe({ entryTypes: ['dns'] });
1335+
dns.lookup('localhost', () => {});
1336+
dns.promises.resolve('localhost');
1337+
```
1338+
12791339
[Async Hooks]: async_hooks.md
12801340
[High Resolution Time]: https://www.w3.org/TR/hr-time-2
12811341
[Performance Timeline]: https://w3c.github.io/performance-timeline/

lib/dns.js

+39-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const {
2727
ObjectDefineProperties,
2828
ObjectDefineProperty,
2929
ReflectApply,
30+
Symbol,
3031
} = primordials;
3132

3233
const cares = internalBinding('cares_wrap');
@@ -62,6 +63,15 @@ const {
6263
QueryReqWrap,
6364
} = cares;
6465

66+
const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
67+
const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
68+
const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
69+
70+
const {
71+
startPerf,
72+
stopPerf,
73+
} = require('internal/perf/observe');
74+
6575
const dnsException = errors.dnsException;
6676

6777
let promises = null; // Lazy loaded
@@ -71,6 +81,7 @@ function onlookup(err, addresses) {
7181
return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
7282
}
7383
this.callback(null, addresses[0], this.family || isIP(addresses[0]));
84+
stopPerf(this, kPerfHooksDnsLookupContext);
7485
}
7586

7687

@@ -89,6 +100,7 @@ function onlookupall(err, addresses) {
89100
}
90101

91102
this.callback(null, addresses);
103+
stopPerf(this, kPerfHooksDnsLookupContext);
92104
}
93105

94106

@@ -161,6 +173,13 @@ function lookup(hostname, options, callback) {
161173
process.nextTick(callback, dnsException(err, 'getaddrinfo', hostname));
162174
return {};
163175
}
176+
const detail = {
177+
hostname,
178+
family,
179+
hints,
180+
verbatim,
181+
};
182+
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
164183
return req;
165184
}
166185

@@ -173,6 +192,7 @@ function onlookupservice(err, hostname, service) {
173192
return this.callback(dnsException(err, 'getnameinfo', this.hostname));
174193

175194
this.callback(null, hostname, service);
195+
stopPerf(this, kPerfHooksDnsLookupServiceContext);
176196
}
177197

178198

@@ -197,6 +217,14 @@ function lookupService(address, port, callback) {
197217

198218
const err = cares.getnameinfo(req, address, port);
199219
if (err) throw dnsException(err, 'getnameinfo', address);
220+
startPerf(req, kPerfHooksDnsLookupServiceContext, {
221+
type: 'dns',
222+
name: 'lookupService',
223+
detail: {
224+
host: address,
225+
port
226+
}
227+
});
200228
return req;
201229
}
202230

@@ -211,8 +239,10 @@ function onresolve(err, result, ttls) {
211239

212240
if (err)
213241
this.callback(dnsException(err, this.bindingName, this.hostname));
214-
else
242+
else {
215243
this.callback(null, result);
244+
stopPerf(this, kPerfHooksDnsLookupResolveContext);
245+
}
216246
}
217247

218248
function resolver(bindingName) {
@@ -234,6 +264,14 @@ function resolver(bindingName) {
234264
req.ttl = !!(options && options.ttl);
235265
const err = this._handle[bindingName](req, toASCII(name));
236266
if (err) throw dnsException(err, bindingName, name);
267+
startPerf(req, kPerfHooksDnsLookupResolveContext, {
268+
type: 'dns',
269+
name: bindingName,
270+
detail: {
271+
host: name,
272+
ttl: req.ttl
273+
}
274+
});
237275
return req;
238276
}
239277
ObjectDefineProperty(query, 'name', { value: bindingName });

lib/internal/dns/promises.js

+41
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
ObjectDefineProperty,
66
Promise,
77
ReflectApply,
8+
Symbol,
89
} = primordials;
910

1011
const {
@@ -37,6 +38,15 @@ const {
3738
validateOneOf,
3839
} = require('internal/validators');
3940

41+
const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
42+
const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
43+
const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
44+
45+
const {
46+
startPerf,
47+
stopPerf,
48+
} = require('internal/perf/observe');
49+
4050
function onlookup(err, addresses) {
4151
if (err) {
4252
this.reject(dnsException(err, 'getaddrinfo', this.hostname));
@@ -45,6 +55,7 @@ function onlookup(err, addresses) {
4555

4656
const family = this.family || isIP(addresses[0]);
4757
this.resolve({ address: addresses[0], family });
58+
stopPerf(this, kPerfHooksDnsLookupContext);
4859
}
4960

5061
function onlookupall(err, addresses) {
@@ -65,6 +76,7 @@ function onlookupall(err, addresses) {
6576
}
6677

6778
this.resolve(addresses);
79+
stopPerf(this, kPerfHooksDnsLookupContext);
6880
}
6981

7082
function createLookupPromise(family, hostname, all, hints, verbatim) {
@@ -95,6 +107,14 @@ function createLookupPromise(family, hostname, all, hints, verbatim) {
95107

96108
if (err) {
97109
reject(dnsException(err, 'getaddrinfo', hostname));
110+
} else {
111+
const detail = {
112+
hostname,
113+
family,
114+
hints,
115+
verbatim,
116+
};
117+
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
98118
}
99119
});
100120
}
@@ -136,6 +156,7 @@ function onlookupservice(err, hostname, service) {
136156
}
137157

138158
this.resolve({ hostname, service });
159+
stopPerf(this, kPerfHooksDnsLookupServiceContext);
139160
}
140161

141162
function createLookupServicePromise(hostname, port) {
@@ -152,6 +173,15 @@ function createLookupServicePromise(hostname, port) {
152173

153174
if (err)
154175
reject(dnsException(err, 'getnameinfo', hostname));
176+
else
177+
startPerf(req, kPerfHooksDnsLookupServiceContext, {
178+
type: 'dns',
179+
name: 'lookupService',
180+
detail: {
181+
host: hostname,
182+
port
183+
}
184+
});
155185
});
156186
}
157187

@@ -179,6 +209,7 @@ function onresolve(err, result, ttls) {
179209
result, (address, index) => ({ address, ttl: ttls[index] }));
180210

181211
this.resolve(result);
212+
stopPerf(this, kPerfHooksDnsLookupResolveContext);
182213
}
183214

184215
function createResolverPromise(resolver, bindingName, hostname, ttl) {
@@ -196,6 +227,16 @@ function createResolverPromise(resolver, bindingName, hostname, ttl) {
196227

197228
if (err)
198229
reject(dnsException(err, bindingName, hostname));
230+
else {
231+
startPerf(req, kPerfHooksDnsLookupResolveContext, {
232+
type: 'dns',
233+
name: bindingName,
234+
detail: {
235+
host: hostname,
236+
ttl
237+
}
238+
});
239+
}
199240
});
200241
}
201242

lib/internal/perf/observe.js

+34
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const {
2424
NODE_PERFORMANCE_ENTRY_TYPE_GC,
2525
NODE_PERFORMANCE_ENTRY_TYPE_HTTP2,
2626
NODE_PERFORMANCE_ENTRY_TYPE_HTTP,
27+
NODE_PERFORMANCE_ENTRY_TYPE_NET,
28+
NODE_PERFORMANCE_ENTRY_TYPE_DNS,
2729
},
2830
installGarbageCollectionTracking,
2931
observerCounts,
@@ -79,12 +81,14 @@ const kTypeMultiple = 1;
7981
let gcTrackingInstalled = false;
8082

8183
const kSupportedEntryTypes = ObjectFreeze([
84+
'dns',
8285
'function',
8386
'gc',
8487
'http',
8588
'http2',
8689
'mark',
8790
'measure',
91+
'net',
8892
]);
8993

9094
// Performance timeline entry Buffers
@@ -118,6 +122,8 @@ function getObserverType(type) {
118122
case 'gc': return NODE_PERFORMANCE_ENTRY_TYPE_GC;
119123
case 'http2': return NODE_PERFORMANCE_ENTRY_TYPE_HTTP2;
120124
case 'http': return NODE_PERFORMANCE_ENTRY_TYPE_HTTP;
125+
case 'net': return NODE_PERFORMANCE_ENTRY_TYPE_NET;
126+
case 'dns': return NODE_PERFORMANCE_ENTRY_TYPE_DNS;
121127
}
122128
}
123129

@@ -443,11 +449,39 @@ function hasObserver(type) {
443449
return observerCounts[observerType] > 0;
444450
}
445451

452+
453+
function startPerf(target, key, context = {}) {
454+
if (hasObserver(context.type)) {
455+
target[key] = {
456+
...context,
457+
startTime: process.hrtime(),
458+
};
459+
}
460+
}
461+
462+
function stopPerf(target, key, context = {}) {
463+
const ctx = target[key];
464+
if (ctx && hasObserver(ctx.type)) {
465+
const startTime = ctx.startTime;
466+
const diff = process.hrtime(startTime);
467+
const entry = new InternalPerformanceEntry(
468+
ctx.name,
469+
ctx.type,
470+
startTime[0] * 1000 + startTime[1] / 1e6,
471+
diff[0] * 1000 + diff[1] / 1e6,
472+
{ ...ctx.detail, ...context.detail },
473+
);
474+
enqueue(entry);
475+
}
476+
}
477+
446478
module.exports = {
447479
PerformanceObserver,
448480
PerformanceObserverEntryList,
449481
enqueue,
450482
hasObserver,
451483
clearEntriesFromBuffer,
452484
filterBufferMapByNameAndType,
485+
startPerf,
486+
stopPerf,
453487
};

lib/net.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ const isWindows = process.platform === 'win32';
130130

131131
const noop = () => {};
132132

133+
const kPerfHooksNetConnectContext = Symbol('kPerfHooksNetConnectContext');
134+
const {
135+
startPerf,
136+
stopPerf,
137+
} = require('internal/perf/observe');
138+
133139
function getFlags(ipv6Only) {
134140
return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0;
135141
}
@@ -952,6 +958,8 @@ function internalConnect(
952958

953959
const ex = exceptionWithHostPort(err, 'connect', address, port, details);
954960
self.destroy(ex);
961+
} else if (addressType === 6 || addressType === 4) {
962+
startPerf(self, kPerfHooksNetConnectContext, { type: 'net', name: 'connect', detail: { host: address, port } });
955963
}
956964
}
957965

@@ -1177,7 +1185,7 @@ function afterConnect(status, handle, req, readable, writable) {
11771185
// this doesn't actually consume any bytes, because len=0.
11781186
if (readable && !self.isPaused())
11791187
self.read(0);
1180-
1188+
stopPerf(self, kPerfHooksNetConnectContext);
11811189
} else {
11821190
self.connecting = false;
11831191
let details;

src/node_perf_common.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ extern uint64_t performance_v8_start;
3434
#define NODE_PERFORMANCE_ENTRY_TYPES(V) \
3535
V(GC, "gc") \
3636
V(HTTP, "http") \
37-
V(HTTP2, "http2")
37+
V(HTTP2, "http2") \
38+
V(NET, "net") \
39+
V(DNS, "dns")
3840

3941
enum PerformanceMilestone {
4042
#define V(name, _) NODE_PERFORMANCE_MILESTONE_##name,

0 commit comments

Comments
 (0)