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