node/lib/internal/trace_events_async_hooks.js
James M Snell 1dd9c9787b src: add tracing category macros
Adds `TRACING_CATEGORY_NODE`, `TRACING_CATEGORY_NODE1` and
`TRACING_CATEGORY_NODE2` helper macros for consistently building
trace event category strings. For instance,

`TRACING_CATEGORY_NODE2(foo, bar)` would generate the category
string `node,node.foo,node.foo.bar`, such that...

```
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
    TRACING_CATEGORY_NODE2(foo, bar),
    "baz", 1);
```

Would emit if trace events are enabled for categories: `node`,
`node.foo`, or `node.foo.bar`.

This allows a natural scoping down of what trace events a user
may want to receive. Enabling the `node` category would receive
everything Node.js produces.

PR-URL: https://github.com/nodejs/node/pull/19155
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
2018-03-09 08:09:41 -08:00

72 lines
2.3 KiB
JavaScript

'use strict';
const trace_events = process.binding('trace_events');
const async_wrap = process.binding('async_wrap');
const async_hooks = require('async_hooks');
// Use small letters such that chrome://tracing groups by the name.
// The behavior is not only useful but the same as the events emitted using
// the specific C++ macros.
const BEFORE_EVENT = 'b'.charCodeAt(0);
const END_EVENT = 'e'.charCodeAt(0);
// In trace_events it is not only the id but also the name that needs to be
// repeated. Since async_hooks doesn't expose the provider type in the
// non-init events, use a map to manually map the asyncId to the type name.
const typeMemory = new Map();
const trace_event_category = 'node,node.async_hooks';
// It is faster to emit trace_events directly from C++. Thus, this happens in
// async_wrap.cc. However, events emitted from the JavaScript API or the
// Embedder C++ API can't be emitted from async_wrap.cc. Thus they are
// emitted using the JavaScript API. To prevent emitting the same event
// twice the async_wrap.Providers list is used to filter the events.
const nativeProviders = new Set(Object.keys(async_wrap.Providers));
const hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (nativeProviders.has(type)) return;
typeMemory.set(asyncId, type);
trace_events.emit(BEFORE_EVENT, trace_event_category,
type, asyncId,
'triggerAsyncId', triggerAsyncId,
'executionAsyncId', async_hooks.executionAsyncId());
},
before(asyncId) {
const type = typeMemory.get(asyncId);
if (type === undefined) return;
trace_events.emit(BEFORE_EVENT, trace_event_category,
type + '_CALLBACK', asyncId);
},
after(asyncId) {
const type = typeMemory.get(asyncId);
if (type === undefined) return;
trace_events.emit(END_EVENT, trace_event_category,
type + '_CALLBACK', asyncId);
},
destroy(asyncId) {
const type = typeMemory.get(asyncId);
if (type === undefined) return;
trace_events.emit(END_EVENT, trace_event_category,
type, asyncId);
// cleanup asyncId to type map
typeMemory.delete(asyncId);
}
});
exports.setup = function() {
if (trace_events.categoryGroupEnabled(trace_event_category)) {
hook.enable();
}
};