• 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
35// Promises are not AsyncWrap resources so they should emit trace_events here.
36nativeProviders.delete('PROMISE');
37
38function createHook() {
39  // In traceEvents it is not only the id but also the name that needs to be
40  // repeated. Since async_hooks doesn't expose the provider type in the
41  // non-init events, use a map to manually map the asyncId to the type name.
42
43  const hook = async_hooks.createHook({
44    init(asyncId, type, triggerAsyncId, resource) {
45      if (nativeProviders.has(type)) return;
46
47      typeMemory.set(asyncId, type);
48      trace(kBeforeEvent, kTraceEventCategory,
49            type, asyncId,
50            {
51              triggerAsyncId,
52              executionAsyncId: async_hooks.executionAsyncId()
53            });
54    },
55
56    before(asyncId) {
57      const type = typeMemory.get(asyncId);
58      if (type === undefined) return;
59
60      trace(kBeforeEvent, kTraceEventCategory, `${type}_CALLBACK`, asyncId);
61    },
62
63    after(asyncId) {
64      const type = typeMemory.get(asyncId);
65      if (type === undefined) return;
66
67      trace(kEndEvent, kTraceEventCategory, `${type}_CALLBACK`, asyncId);
68    },
69
70    destroy(asyncId) {
71      const type = typeMemory.get(asyncId);
72      if (type === undefined) return;
73
74      trace(kEndEvent, kTraceEventCategory, type, asyncId);
75
76      // Cleanup asyncId to type map
77      typeMemory.delete(asyncId);
78    }
79  });
80
81  return {
82    enable() {
83      if (this[kEnabled])
84        return;
85      this[kEnabled] = true;
86      hook.enable();
87    },
88
89    disable() {
90      if (!this[kEnabled])
91        return;
92      this[kEnabled] = false;
93      hook.disable();
94      typeMemory.clear();
95    }
96  };
97}
98
99exports.createHook = createHook;
100