EtwTrace.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #ifdef ENABLE_JS_ETW
  7. //
  8. // ETW (Event Tracing for Windows) is a high-performance, low overhead and highly scalable
  9. // tracing facility provided by the Windows Operating System. There are
  10. // four main types of components in ETW: event providers, controllers, consumers, and event trace sessions.
  11. // An event provider is a logical entity that writes events to ETW sessions. The event provider must register
  12. // a provider ID with ETW through the registration API. A provider first registers with ETW and writes events
  13. // from various points in the code by invoking the ETW logging API. When a provider is enabled dynamically by
  14. // the ETW controller application, calls to the logging API sends events to a specific trace session
  15. // designated by the controller. Javascript runtime is an event provider.
  16. //
  17. // When ETW's sample profile is enabled it logs the entry point of each function as part of
  18. // stackwalking on the platform. For javascript functions these entry points are not meaningful.
  19. // Therefore, we log events which map the entry point to the name of the function. There are two types of events
  20. // that we log for symbol decoding:
  21. // Rundown: Represents the state of the process and logged when tracing is enabled. Enables the 'attach' scenario.
  22. // Runtime: Represents the change of state of the process and logged as the state changes e.g. when a jitted function gets loaded.
  23. //
  24. // C-style callback
  25. extern "C" {
  26. void EtwCallback(
  27. ULONG controlCode,
  28. PVOID callbackContext);
  29. }
  30. #include "TestEtwEventSink.h"
  31. //
  32. // Represents type of method entry point.
  33. //
  34. enum MethodType : uint16
  35. {
  36. MethodType_Interpreted = 0x1,
  37. MethodType_Jit = 0x2,
  38. MethodType_LoopBody = 0x3,
  39. MethodType_HostMethod = 0x4,
  40. };
  41. #ifdef TEST_ETW_EVENTS
  42. #define WriteMethodEvent(EventName, ScriptContextID, MethodStartAddress, MethodSize, MethodID, MethodFlags, MethodAddressRangeID, SourceID, Line, Column, MethodName) \
  43. if(TestEtwEventSink::IsLoaded()) \
  44. { \
  45. TestEtwEventSink::Instance->WriteMethodEvent(EventName, ScriptContextID, MethodStartAddress, MethodSize, MethodID, MethodFlags, MethodAddressRangeID, SourceID, Line, Column, MethodName); \
  46. }
  47. #define WriteSourceEvent(EventName, SourceContext, ScriptContextID, SourceFlags, Url) \
  48. if(TestEtwEventSink::IsLoaded()) \
  49. { \
  50. TestEtwEventSink::Instance->WriteSourceEvent(EventName, SourceContext, ScriptContextID, SourceFlags, Url); \
  51. }
  52. #else
  53. #define WriteMethodEvent(Event, ...)
  54. #define WriteSourceEvent(Event, ...)
  55. #endif
  56. // Helper macro to log all the method level events
  57. #define LogSourceEvent(Function, SourceContext, ScriptContext, SourceFlags, Url) \
  58. JS_ETW(Function(SourceContext, \
  59. ScriptContext, \
  60. SourceFlags, \
  61. Url)); \
  62. \
  63. WriteSourceEvent(STRINGIZEW(Function), \
  64. SourceContext, \
  65. ScriptContext, \
  66. SourceFlags, \
  67. Url);
  68. #define LogMethodNativeEvent(Function, Body, entryPoint) \
  69. Assert(entryPoint->GetNativeAddress() != NULL); \
  70. Assert(entryPoint->GetCodeSize() > 0); \
  71. Assert(entryPoint->IsNativeCode()); \
  72. char16 functionNameArray[NameBufferLength]; \
  73. const char16 *functionName; \
  74. size_t requiredCharCapacity = 0; \
  75. bool deleteFunctionName = false; \
  76. const ExecutionMode jitMode = entryPoint->GetJitMode(); \
  77. if(jitMode == ExecutionMode::SimpleJit) \
  78. { \
  79. requiredCharCapacity = \
  80. GetSimpleJitFunctionName(Body, functionNameArray, _countof(functionNameArray)); \
  81. if(requiredCharCapacity == 0) \
  82. { \
  83. functionName = functionNameArray; \
  84. } \
  85. else \
  86. { \
  87. Assert(requiredCharCapacity > NameBufferLength); \
  88. char16 *const allocatedFunctionName = HeapNewNoThrowArray(char16, requiredCharCapacity);\
  89. if(allocatedFunctionName) \
  90. { \
  91. const size_t newRequiredCharCapacity = \
  92. GetSimpleJitFunctionName(Body, allocatedFunctionName, requiredCharCapacity); \
  93. Assert(newRequiredCharCapacity == 0); \
  94. functionName = allocatedFunctionName; \
  95. deleteFunctionName = true; \
  96. } \
  97. else \
  98. { \
  99. functionNameArray[0] = _u('\0'); \
  100. functionName = functionNameArray; \
  101. } \
  102. } \
  103. } \
  104. else \
  105. { \
  106. functionName = GetFunctionName(Body); \
  107. } \
  108. JS_ETW(Function( \
  109. Body->GetScriptContext(), \
  110. (void *)entryPoint->GetNativeAddress(), \
  111. entryPoint->GetCodeSize(), \
  112. GetFunctionId(Body), \
  113. 0 /* methodFlags - for future use*/, \
  114. MethodType_Jit, \
  115. GetSourceId(Body), \
  116. Body->GetLineNumber(), \
  117. Body->GetColumnNumber(), \
  118. functionName)); \
  119. WriteMethodEvent(STRINGIZEW(Function), \
  120. Body->GetScriptContext(), \
  121. (void *)entryPoint->GetNativeAddress(), \
  122. entryPoint->GetCodeSize(), \
  123. GetFunctionId(Body), \
  124. 0 /* methodFlags - for future use*/, \
  125. MethodType_Jit, \
  126. GetSourceId(Body), \
  127. Body->GetLineNumber(), \
  128. Body->GetColumnNumber(), \
  129. functionName); \
  130. if(deleteFunctionName) \
  131. { \
  132. HeapDeleteArray(requiredCharCapacity, functionName); \
  133. }
  134. #define LogMethodInterpretedThunkEvent(Function, Body) \
  135. Assert(Body->GetDynamicInterpreterEntryPoint() != NULL); \
  136. JS_ETW(Function(Body->GetScriptContext(), \
  137. Body->GetDynamicInterpreterEntryPoint(), \
  138. Body->GetDynamicInterpreterThunkSize(), \
  139. GetFunctionId(Body), \
  140. 0 /* methodFlags - for future use*/, \
  141. MethodType_Interpreted, \
  142. GetSourceId(Body), \
  143. Body->GetLineNumber(), \
  144. Body->GetColumnNumber(), \
  145. GetFunctionName(Body))); \
  146. \
  147. WriteMethodEvent(STRINGIZEW(Function), \
  148. Body->GetScriptContext(), \
  149. Body->GetDynamicInterpreterEntryPoint(), \
  150. Body->GetDynamicInterpreterThunkSize(), \
  151. GetFunctionId(Body), \
  152. 0 /* methodFlags - for future use*/, \
  153. MethodType_Interpreted, \
  154. GetSourceId(Body), \
  155. Body->GetLineNumber(), \
  156. Body->GetColumnNumber(), \
  157. GetFunctionName(Body))
  158. #define LogLoopBodyEvent(Function, Body, entryPoint, loopNumber) \
  159. Assert(entryPoint->GetNativeAddress() != NULL); \
  160. Assert(entryPoint->GetCodeSize() > 0); \
  161. WCHAR loopBodyNameArray[NameBufferLength]; \
  162. WCHAR* loopBodyName = loopBodyNameArray; \
  163. size_t bufferSize = Body->GetLoopBodyName(loopNumber, loopBodyName, NameBufferLength); \
  164. if(bufferSize > NameBufferLength) /* insufficient buffer space*/ \
  165. { \
  166. loopBodyName = HeapNewNoThrowArray(WCHAR, bufferSize); \
  167. if(loopBodyName) \
  168. { \
  169. Body->GetLoopBodyName(loopNumber, loopBodyName, NameBufferLength); \
  170. } \
  171. else \
  172. { \
  173. loopBodyNameArray[0] = _u('\0'); \
  174. loopBodyName = loopBodyNameArray; \
  175. } \
  176. } \
  177. JS_ETW(Function(Body->GetScriptContext(), \
  178. (void *)entryPoint->GetNativeAddress(), \
  179. entryPoint->GetCodeSize(), \
  180. GetFunctionId(Body), \
  181. 0 /* methodFlags - for future use*/, \
  182. MethodType_LoopBody + (uint16)loopNumber, \
  183. GetSourceId(Body), \
  184. /*line*/ 0, \
  185. /*column*/ 0, \
  186. loopBodyName)); \
  187. WriteMethodEvent(STRINGIZEW(Function), \
  188. Body->GetScriptContext(), \
  189. (void *)entryPoint->GetNativeAddress(), \
  190. entryPoint->GetCodeSize(), \
  191. GetFunctionId(Body), \
  192. 0 /* methodFlags - for future use*/, \
  193. MethodType_LoopBody + (uint16)loopNumber, \
  194. GetSourceId(Body), \
  195. /*line*/ 0, \
  196. /*column*/ 0, \
  197. loopBodyName); \
  198. if(loopBodyNameArray != loopBodyName) \
  199. { \
  200. HeapDeleteArray(bufferSize, loopBodyName); \
  201. }
  202. //
  203. // Encapsulates all ETW event logging and registration related to symbol decoding.
  204. //
  205. class EtwTrace
  206. {
  207. private:
  208. static const size_t NameBufferLength = 256;
  209. public:
  210. static void Register();
  211. static void UnRegister();
  212. static void PerformRundown(bool start);
  213. /* Unload events */
  214. static void LogSourceUnloadEvents(Js::ScriptContext* scriptContext);
  215. static void LogMethodNativeUnloadEvent(Js::FunctionBody* body, Js::FunctionEntryPointInfo* entryPoint);
  216. static void LogMethodInterpreterThunkUnloadEvent(Js::FunctionBody* body);
  217. static void LogLoopBodyUnloadEvent(Js::FunctionBody* body, Js::LoopEntryPointInfo* entryPoint, uint loopNumber);
  218. /* Load events */
  219. static void LogMethodInterpreterThunkLoadEvent(Js::FunctionBody* body);
  220. static void LogMethodNativeLoadEvent(Js::FunctionBody* body, Js::FunctionEntryPointInfo* entryPoint);
  221. static void LogLoopBodyLoadEvent(Js::FunctionBody* body, Js::LoopEntryPointInfo* entryPoint, uint16 loopNumber);
  222. static void LogScriptContextLoadEvent(Js::ScriptContext* scriptContext);
  223. static void LogSourceModuleLoadEvent(Js::ScriptContext* scriptContext, DWORD_PTR sourceContext, _In_z_ const char16* url);
  224. static const char16* GetFunctionName(Js::FunctionBody* body);
  225. static size_t GetLoopBodyName(_In_ Js::FunctionBody* body, _In_ Js::LoopHeader* loopHeader, _Out_writes_opt_z_(size) char16* nameBuffer, _In_ size_t size );
  226. _Success_(return == 0)
  227. static size_t GetSimpleJitFunctionName(Js::FunctionBody *const body, _Out_writes_opt_z_(nameCharCapacity) char16 *const name, const size_t nameCharCapacity);
  228. static DWORD_PTR GetSourceId(Js::FunctionBody* body);
  229. static uint GetFunctionId(Js::FunctionProxy* body);
  230. };
  231. #endif