FaultInjection.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. namespace Js
  7. {
  8. #ifdef FAULT_INJECTION
  9. class FaultInjection
  10. {
  11. public:
  12. static const unsigned int MAX_FRAME_COUNT = 64;
  13. // Fault types
  14. #define FAULT_TYPE(x) x, \
  15. enum FaultType
  16. {
  17. #include "FaultTypes.h"
  18. FaultTypeCount,
  19. InvalidFaultType
  20. };
  21. #undef FAULT_TYPE
  22. // use bit array to save the enabled type
  23. class FaultInjectionTypes
  24. {
  25. private:
  26. char faultTypeBitArray[InvalidFaultType/8+1];
  27. char getBit(int index) {
  28. return (faultTypeBitArray[index/8] >> (7-(index & 0x7))) & 0x1;
  29. }
  30. void setBit(int index, int value) {
  31. faultTypeBitArray[index/8] = faultTypeBitArray[index/8] | (value & 0x1) << (7-(index & 0x7));
  32. }
  33. public:
  34. FaultInjectionTypes(){
  35. memset(&faultTypeBitArray, 0, sizeof(faultTypeBitArray));
  36. }
  37. void EnableAll(){
  38. memset(&faultTypeBitArray, ~0, sizeof(faultTypeBitArray));
  39. }
  40. void EnableType(FaultType type);
  41. void EnableType(int type){
  42. EnableType((FaultType)type);
  43. }
  44. bool IsEnabled(FaultType type);
  45. bool IsEnabled(const char16* name);
  46. };
  47. static const char16 *FaultTypeNames[];
  48. void ParseFaultTypes(const char16* szFaultTypes);
  49. public:
  50. enum FaultMode
  51. {
  52. CountOnly = 0,
  53. CountEquals = 1,
  54. CountEqualsOrAbove = 2,
  55. StackMatch = 3,
  56. StackMatchCountOnly = 4,
  57. StackHashCountOnly = 5,
  58. DisplayAvailableFaultTypes = 6,
  59. InstallExceptionHandlerOnly = 7,
  60. };
  61. uint countOfInjectionPoints;
  62. int FaultInjectionCookie;
  63. enum StackMatchInitializationState
  64. {
  65. Uninitialized = 0,
  66. FailedToInitialize = 1,
  67. Succeeded = 2
  68. };
  69. StackMatchInitializationState stackMatchInitialized; // tri-state: 0-uninitialized, 1-tried to init, 2-initialized
  70. private:
  71. HMODULE hDbgHelp;
  72. bool InitializeSym();
  73. FaultInjectionTypes* faultInjectionTypes;
  74. bool IsCurrentStackMatch();
  75. bool EnsureStackMatchInfraInitialized();
  76. uint baselineFrameCount;
  77. char16 *baselineStack[MAX_FRAME_COUNT];
  78. UINT_PTR baselineAddresses[MAX_FRAME_COUNT];
  79. ULONG_PTR* stackHashOfAllInjectionPoints;
  80. UINT stackHashOfAllInjectionPointsSize;
  81. public:
  82. static FaultInjection Global;
  83. FaultInjection();
  84. ~FaultInjection();
  85. bool IsFaultEnabled(FaultType faultType);
  86. bool IsFaultInjectionOn(FaultType faultType);
  87. bool ShouldInjectFault(FaultType fType, LPCWSTR name = nullptr, size_t size = 0);// name and size are used for OOM only
  88. // sample for customized fault type
  89. template<class Pred>
  90. bool ShouldInjectFault(FaultType fType, Pred p) {
  91. bool shouldInjectionFault = Js::Configuration::Global.flags.FaultInjectionCount == 0
  92. || ShouldInjectFaultHelper(fType);
  93. if (shouldInjectionFault && p()) {
  94. if(IsDebuggerPresent()) {
  95. DebugBreak();
  96. }
  97. dumpCurrentStackData();
  98. }
  99. return shouldInjectionFault;
  100. }
  101. private:
  102. bool ShouldInjectFaultHelper(FaultType fType, LPCWSTR name = nullptr, size_t size = 0);
  103. private:
  104. // for reconstruction stack of the fault injection points in postmortem debugging
  105. struct InjectionRecord{
  106. void* StackFrames[MAX_FRAME_COUNT];
  107. UINT_PTR hash;
  108. WORD FrameCount;
  109. void* StackData;
  110. size_t StackDataLength;
  111. CONTEXT Context;
  112. WCHAR name[32];
  113. size_t allocSize;
  114. InjectionRecord* next;
  115. };
  116. public:
  117. InjectionRecord* InjectionFirstRecord;
  118. InjectionRecord** InjectionLastRecordRef;
  119. int InjectionRecordsCount;
  120. void dumpCurrentStackData(LPCWSTR name = nullptr, size_t size = 0);
  121. static THREAD_LOCAL int(*pfnHandleAV)(int, PEXCEPTION_POINTERS);
  122. private:
  123. bool symInitialized;
  124. static PVOID vectoredExceptionHandler;
  125. static DWORD exceptionFilterRemovalLastError;
  126. static void InstallExceptionFilters();
  127. static void RemoveExceptionFilters();
  128. static UINT_PTR CalculateStackHash(void* frames[], WORD frameCount, WORD framesToSkip);
  129. static LONG WINAPI FaultInjectionExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo);
  130. void FaultInjectionAnalyzeException(_EXCEPTION_POINTERS *ep);
  131. };
  132. #endif
  133. } // namespace Js
  134. #ifdef FAULT_INJECTION
  135. #define IS_FAULTINJECT_NO_THROW_ON \
  136. Js::FaultInjection::Global.IsFaultInjectionOn(Js::FaultInjection::Global.NoThrow)
  137. #define FAULTINJECT_MEMORY_NOTHROW(name, size) \
  138. if(Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.NoThrow, name, size)) \
  139. return NULL;
  140. #define FAULTINJECT_MEMORY_THROW(name, size) \
  141. if(Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.Throw, name, size)) \
  142. Js::Throw::OutOfMemory();
  143. #define FAULTINJECT_MEMORY_MARK_THROW(name, size) \
  144. if(Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.MarkThrow, name, size)) { \
  145. Js::Throw::OutOfMemory(); \
  146. }
  147. #define FAULTINJECT_MEMORY_MARK_NOTHROW(name, size) \
  148. if(Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.MarkNoThrow, name, size)) { \
  149. return false; \
  150. }
  151. #define FAULTINJECT_SCRIPT_TERMINATION \
  152. if((this->threadContextFlags & ThreadContextFlagCanDisableExecution) != 0){ \
  153. if( Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.ScriptTermination)){ \
  154. this->stackLimitForCurrentThread = Js::Constants::StackLimitForScriptInterrupt; \
  155. }\
  156. }
  157. #define FAULTINJECT_STACK_PROBE \
  158. if( Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.StackProbe)){ \
  159. stackAvailable = false; \
  160. }
  161. #define IS_FAULTINJECT_STACK_PROBE_ON \
  162. Js::FaultInjection::Global.IsFaultInjectionOn(Js::FaultInjection::Global.StackProbe)
  163. #define FAULTINJECT_SCRIPT_TERMINATION_ON_DISPOSE \
  164. Js::FaultInjection::Global.ShouldInjectFault(Js::FaultInjection::Global.ScriptTerminationOnDispose)
  165. // A general implementation of customized fault type injection
  166. #define INJECT_FAULT(type, condition, execution) \
  167. do{\
  168. if(Js::FaultInjection::Global.ShouldInjectFault(type, condition)) {\
  169. ##execution##\
  170. };\
  171. }while(0)
  172. #else
  173. #define IS_FAULTINJECT_NO_THROW_ON false
  174. #define FAULTINJECT_MEMORY_NOTHROW(name, size)
  175. #define FAULTINJECT_MEMORY_THROW(name, size)
  176. #define FAULTINJECT_MEMORY_MARK_THROW(name, size)
  177. #define FAULTINJECT_MEMORY_MARK_NOTHROW(name, size)
  178. #define FAULTINJECT_SCRIPT_TERMINATION
  179. #define FAULTINJECT_SCRIPT_TERMINATION_ON_DISPOSE false
  180. #define FAULTINJECT_STACK_PROBE
  181. #define IS_FAULTINJECT_STACK_PROBE_ON false
  182. #define INJECT_FAULT(type, condition, execution)
  183. #endif