Skip to content

Commit

Permalink
diagnostics_channel: early-exit tracing channel trace methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Belanger committed Feb 28, 2024
1 parent f4af4b1 commit 91cb93b
Show file tree
Hide file tree
Showing 24 changed files with 287 additions and 139 deletions.
2 changes: 1 addition & 1 deletion deps/histogram/src/hdr_atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ static void hdr_atomic_store_pointer(void** pointer, void* value)
}

static int64_t __inline hdr_atomic_load_64(int64_t* field)
{
{
_ReadBarrier();
return *field;
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions deps/undici/src/node_modules/@fastify/busboy/lib/main.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion deps/undici/src/types/readable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ declare class BodyReadable extends Readable {
*/
readonly bodyUsed: boolean

/**
/**
* If body is null, it should return null as the body
*
* If body is not null, should return the body as a ReadableStream
Expand Down
2 changes: 1 addition & 1 deletion deps/undici/src/types/webidl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ interface WebidlConverters {
): NodeJS.TypedArray | ArrayBufferLike | DataView

['sequence<ByteString>']: SequenceConverter<string>

['sequence<sequence<ByteString>>']: SequenceConverter<string[]>

['record<ByteString, ByteString>']: RecordConverter<string, string>
Expand Down
2 changes: 1 addition & 1 deletion deps/uv/src/unix/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1677,7 +1677,7 @@ int uv_thread_setpriority(uv_thread_t tid, int priority) {
param.sched_priority = prio;
r = pthread_setschedparam(tid, policy, &param);
if (r != 0)
return UV__ERR(errno);
return UV__ERR(errno);
}

return 0;
Expand Down
3 changes: 1 addition & 2 deletions deps/v8/third_party/ittapi/src/ittnotify/ittnotify_static.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <stdarg.h>
#include <string.h>

#define INTEL_NO_MACRO_BODY
#define INTEL_NO_MACRO_BODY
#define INTEL_ITTNOTIFY_API_PRIVATE
#include "ittnotify.h"
#include "legacy/ittnotify.h"
Expand Down Expand Up @@ -1356,4 +1356,3 @@ ITT_EXTERN_C void _N_(mark_pt_region_end)(__itt_pt_region region)
(void)region;
#endif
}

20 changes: 20 additions & 0 deletions doc/api/diagnostics_channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,11 @@ if the given function throws an error. This will run the given function using
[`channel.runStores(context, ...)`][] on the `start` channel which ensures all
events should have any bound stores set to match this trace context.

To ensure only correct trace graphs are formed, events will only be published
if subscribers are present prior to starting the trace. Subscriptions which are
added after the trace begins will not receive future events from that trace,
only future traces will be seen.

```mjs
import diagnostics_channel from 'node:diagnostics_channel';

Expand Down Expand Up @@ -838,6 +843,11 @@ returned promise rejects. This will run the given function using
[`channel.runStores(context, ...)`][] on the `start` channel which ensures all
events should have any bound stores set to match this trace context.

To ensure only correct trace graphs are formed, events will only be published
if subscribers are present prior to starting the trace. Subscriptions which are
added after the trace begins will not receive future events from that trace,
only future traces will be seen.

```mjs
import diagnostics_channel from 'node:diagnostics_channel';

Expand Down Expand Up @@ -891,6 +901,11 @@ the callback is set. This will run the given function using
[`channel.runStores(context, ...)`][] on the `start` channel which ensures all
events should have any bound stores set to match this trace context.

To ensure only correct trace graphs are formed, events will only be published
if subscribers are present prior to starting the trace. Subscriptions which are
added after the trace begins will not receive future events from that trace,
only future traces will be seen.

```mjs
import diagnostics_channel from 'node:diagnostics_channel';

Expand Down Expand Up @@ -979,6 +994,11 @@ of the callback while the `error` will either be a thrown error visible in the
`end` event or the first callback argument in either of the `asyncStart` or
`asyncEnd` events.

To ensure only correct trace graphs are formed, events should only be published
if subscribers are present prior to starting the trace. Subscriptions which are
added after the trace begins should not receive future events from that trace,
only future traces will be seen.

Tracing channels should follow a naming pattern of:

* `tracing:module.class.method:start` or `tracing:module.function:start`
Expand Down
66 changes: 42 additions & 24 deletions lib/diagnostics_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
ArrayPrototypePush,
ArrayPrototypeSplice,
SafeFinalizationRegistry,
ObjectDefineProperty,
ObjectGetPrototypeOf,
ObjectSetPrototypeOf,
Promise,
Expand Down Expand Up @@ -250,35 +251,40 @@ function assertChannel(value, name) {
}
}

function tracingChannelFrom(nameOrChannels, name) {
if (typeof nameOrChannels === 'string') {
return channel(`tracing:${nameOrChannels}:${name}`);
}

if (typeof nameOrChannels === 'object' && nameOrChannels !== null) {
const channel = nameOrChannels[name];
assertChannel(channel, `nameOrChannels.${name}`);
return channel;
}

throw new ERR_INVALID_ARG_TYPE('nameOrChannels',
['string', 'object', 'TracingChannel'],
nameOrChannels);
}

class TracingChannel {
constructor(nameOrChannels) {
if (typeof nameOrChannels === 'string') {
this.start = channel(`tracing:${nameOrChannels}:start`);
this.end = channel(`tracing:${nameOrChannels}:end`);
this.asyncStart = channel(`tracing:${nameOrChannels}:asyncStart`);
this.asyncEnd = channel(`tracing:${nameOrChannels}:asyncEnd`);
this.error = channel(`tracing:${nameOrChannels}:error`);
} else if (typeof nameOrChannels === 'object') {
const { start, end, asyncStart, asyncEnd, error } = nameOrChannels;

assertChannel(start, 'nameOrChannels.start');
assertChannel(end, 'nameOrChannels.end');
assertChannel(asyncStart, 'nameOrChannels.asyncStart');
assertChannel(asyncEnd, 'nameOrChannels.asyncEnd');
assertChannel(error, 'nameOrChannels.error');

this.start = start;
this.end = end;
this.asyncStart = asyncStart;
this.asyncEnd = asyncEnd;
this.error = error;
} else {
throw new ERR_INVALID_ARG_TYPE('nameOrChannels',
['string', 'object', 'Channel'],
nameOrChannels);
for (const eventName of traceEvents) {
ObjectDefineProperty(this, eventName, {
__proto__: null,
value: tracingChannelFrom(nameOrChannels, eventName),
});
}
}

get hasSubscribers() {
return this.start.hasSubscribers ||
this.end.hasSubscribers ||
this.asyncStart.hasSubscribers ||
this.asyncEnd.hasSubscribers ||
this.error.hasSubscribers;
}

subscribe(handlers) {
for (const name of traceEvents) {
if (!handlers[name]) continue;
Expand All @@ -302,6 +308,10 @@ class TracingChannel {
}

traceSync(fn, context = {}, thisArg, ...args) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}

const { start, end, error } = this;

return start.runStores(context, () => {
Expand All @@ -320,6 +330,10 @@ class TracingChannel {
}

tracePromise(fn, context = {}, thisArg, ...args) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}

const { start, end, asyncStart, asyncEnd, error } = this;

function reject(err) {
Expand Down Expand Up @@ -358,6 +372,10 @@ class TracingChannel {
}

traceCallback(fn, position = -1, context = {}, thisArg, ...args) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}

const { start, end, asyncStart, asyncEnd, error } = this;

function wrappedCallback(err, res) {
Expand Down
16 changes: 8 additions & 8 deletions test/fixtures/wpt/resources/webidl2/lib/webidl2.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ assert.throws(() => (channel = dc.tracingChannel(0)), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message:
/The "nameOrChannels" argument must be of type string or an instance of Channel or Object/,
/The "nameOrChannels" argument must be of type string or an instance of TracingChannel or Object/,
});

// tracingChannel creating without instance of Channel must throw error
Expand Down
60 changes: 0 additions & 60 deletions test/parallel/test-diagnostics-channel-tracing-channel-async.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

const common = require('../common');
const dc = require('diagnostics_channel');

const channel = dc.tracingChannel('test');

const handlers = {
start: common.mustNotCall(),
end: common.mustNotCall(),
asyncStart: common.mustNotCall(),
asyncEnd: common.mustNotCall(),
error: common.mustNotCall()
};

// While subscribe occurs _before_ the callback executes,
// no async events should be published.
channel.traceCallback(setImmediate, 0, {}, null, common.mustCall());
channel.subscribe(handlers);
Loading

0 comments on commit 91cb93b

Please sign in to comment.