• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ObjectKeys,
5  SafeMap,
6  SafeSet,
7  Symbol,
8} = primordials;
9
10const { trace } = internalBinding('trace_events');
11const async_wrap = internalBinding('async_wrap');
12const async_hooks = require('async_hooks');
13const {
14  CHAR_LOWERCASE_B,
15  CHAR_LOWERCASE_E,
16} = require('internal/constants');
17
18// Use small letters such that chrome://tracing groups by the name.
19// The behavior is not only useful but the same as the events emitted using
20// the specific C++ macros.
21const kBeforeEvent = CHAR_LOWERCASE_B;
22const kEndEvent = CHAR_LOWERCASE_E;
23const kTraceEventCategory = 'node,node.async_hooks';
24
25const kEnabled = Symbol('enabled');
26
27// It is faster to emit traceEvents directly from C++. Thus, this happens
28// in async_wrap.cc. However, events emitted from the JavaScript API or the
29// Embedder C++ API can't be emitted from async_wrap.cc. Thus they are
30// emitted using the JavaScript API. To prevent emitting the same event
31// twice the async_wrap.Providers list is used to filter the events.
32const nativeProviders = new SafeSet(ObjectKeys(async_wrap.Providers));
33const typeMemory = new SafeMap();
34
35function createHook() {
36  // In traceEvents it is not only the id but also the name that needs to be
37  // repeated. Since async_hooks doesn't expose the provider type in the
38  // non-init events, use a map to manually map the asyncId to the type name.
39
40  const hook = async_hooks.createHook({
41    init(asyncId, type, triggerAsyncId, resource) {
42      if (nativeProviders.has(type)) return;
43
44      typeMemory.set(asyncId, type);
45      trace(kBeforeEvent, kTraceEventCategory,
46            type, asyncId,
47            {
48              triggerAsyncId,
49              executionAsyncId: async_hooks.executionAsyncId()
50            });
51    },
52
53    before(asyncId) {
54      const type = typeMemory.get(asyncId);
55      if (type === undefined) return;
56
57      trace(kBeforeEvent, kTraceEventCategory, `${type}_CALLBACK`, asyncId);
58    },
59
60    after(asyncId) {
61      const type = typeMemory.get(asyncId);
62      if (type === undefined) return;
63
64      trace(kEndEvent, kTraceEventCategory, `${type}_CALLBACK`, asyncId);
65    },
66
67    destroy(asyncId) {
68      const type = typeMemory.get(asyncId);
69      if (type === undefined) return;
70
71      trace(kEndEvent, kTraceEventCategory, type, asyncId);
72
73      // Cleanup asyncId to type map
74      typeMemory.delete(asyncId);
75    }
76  });
77
78  return {
79    enable() {
80      if (this[kEnabled])
81        return;
82      this[kEnabled] = true;
83      hook.enable();
84    },
85
86    disable() {
87      if (!this[kEnabled])
88        return;
89      this[kEnabled] = false;
90      hook.disable();
91      typeMemory.clear();
92    }
93  };
94}
95
96exports.createHook = createHook;
97