FaultInjection.cpp 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472
  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. #include "CommonCorePch.h"
  6. #ifdef FAULT_INJECTION
  7. #include "io.h"
  8. #include "share.h"
  9. #undef DBGHELP_TRANSLATE_TCHAR
  10. #define _NO_CVCONST_H
  11. // dbghelp.h is not clean with warning 4091
  12. #pragma warning(push)
  13. #pragma warning(disable: 4091) /* warning C4091: 'typedef ': ignored on left of '' when no variable is declared */
  14. #include <dbghelp.h>
  15. #pragma warning(pop)
  16. namespace Js
  17. {
  18. #pragma region helpers
  19. #define FIDELAYLOAD(fn) static decltype(fn)* pfn##fn = nullptr
  20. FIDELAYLOAD(SymInitialize);
  21. FIDELAYLOAD(SymCleanup);
  22. FIDELAYLOAD(SymFromAddrW);
  23. FIDELAYLOAD(SymFromNameW);
  24. FIDELAYLOAD(SymEnumSymbolsW);
  25. FIDELAYLOAD(SymGetModuleInfoW64);
  26. FIDELAYLOAD(SymMatchStringW);
  27. FIDELAYLOAD(SymSetOptions);
  28. FIDELAYLOAD(MiniDumpWriteDump);
  29. FIDELAYLOAD(SymFunctionTableAccess64);
  30. FIDELAYLOAD(SymGetModuleBase64);
  31. FIDELAYLOAD(StackWalk64);
  32. #undef FIDELAYLOAD
  33. template<typename CharT>
  34. bool isEqualIgnoreCase(CharT c1, CharT c2)
  35. {
  36. return c1 == c2
  37. || ((c2 <= 'Z') && (c1 >= 'a') && (c1 - c2 == 'a' - 'A'))
  38. || ((c1 <= 'Z') && (c2 >= 'a') && (c2 - c1 == 'a' - 'A'));
  39. }
  40. template<typename CharT>
  41. CharT *stristr(const CharT * cs1,
  42. const CharT * cs2)
  43. {
  44. CharT *cp = (CharT *)cs1;
  45. CharT *s1, *s2;
  46. if (!*cs2)
  47. return (CharT *)cs1;
  48. while (*cp)
  49. {
  50. s1 = cp;
  51. s2 = (CharT *)cs2;
  52. while (*s1 && *s2 && isEqualIgnoreCase(*s1, *s2))
  53. s1++, s2++;
  54. if (!*s2)
  55. return cp;
  56. cp++;
  57. }
  58. return nullptr;
  59. }
  60. static char16* trimRight(_Inout_z_ char16* str)
  61. {
  62. auto tmp = str + wcslen(str);
  63. while (!isprint(*--tmp));
  64. *(tmp + 1) = _u('\0');
  65. return str;
  66. }
  67. static int8 const* hexTable = []()->int8*{
  68. static int8 hex[256] = { 0 };
  69. memset(hex, 0xff, 256);
  70. for (int8 i = '0'; i <= '9'; i++) hex[i] = i - '0';
  71. for (int8 i = 'a'; i <= 'f'; i++) hex[i] = i - 'a' + 10;
  72. for (int8 i = 'A'; i <= 'F'; i++) hex[i] = i - 'A' + 10;
  73. return hex;
  74. }();
  75. template<typename CharT>
  76. static UINT_PTR HexStrToAddress(const CharT* str)
  77. {
  78. UINT_PTR address = 0;
  79. while (*str == '0' || *str == '`' || *str == 'x' || *str == 'X')
  80. str++; // leading zero
  81. do
  82. {
  83. if (*str == '`') // amd64 address
  84. continue;
  85. if (hexTable[*str & 0xff] < 0)
  86. return address;
  87. address = 16 * address + hexTable[*str & 0xff];
  88. } while (*(++str));
  89. return address;
  90. }
  91. #if _M_X64
  92. // for amd64 jit frame, RtlCaptureStackBackTrace stops walking after hitting jit frame on amd64
  93. _NOINLINE
  94. WORD StackTrace64(_In_ DWORD FramesToSkip,
  95. _In_ DWORD FramesToCapture,
  96. _Out_writes_to_(FramesToCapture, return) PVOID * BackTrace,
  97. _Out_opt_ PDWORD BackTraceHash,
  98. _In_opt_ const CONTEXT* pCtx = nullptr)
  99. {
  100. CONTEXT Context;
  101. KNONVOLATILE_CONTEXT_POINTERS NvContext;
  102. UNWIND_HISTORY_TABLE UnwindHistoryTable;
  103. PRUNTIME_FUNCTION RuntimeFunction;
  104. PVOID HandlerData;
  105. ULONG64 EstablisherFrame;
  106. ULONG64 ImageBase;
  107. ULONG Frame = 0;
  108. if (BackTraceHash)
  109. {
  110. *BackTraceHash = 0;
  111. }
  112. if (pCtx == nullptr)
  113. {
  114. RtlCaptureContext(&Context);
  115. }
  116. else
  117. {
  118. memcpy(&Context, pCtx, sizeof(CONTEXT));
  119. }
  120. RtlZeroMemory(&UnwindHistoryTable, sizeof(UNWIND_HISTORY_TABLE));
  121. while (true)
  122. {
  123. RuntimeFunction = RtlLookupFunctionEntry(Context.Rip, &ImageBase, &UnwindHistoryTable);
  124. RtlZeroMemory(&NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
  125. if (!RuntimeFunction)
  126. {
  127. Context.Rip = (ULONG64)(*(PULONG64)Context.Rsp);
  128. Context.Rsp += 8;
  129. }
  130. else
  131. {
  132. RtlVirtualUnwind(UNW_FLAG_NHANDLER, ImageBase, Context.Rip, RuntimeFunction,
  133. &Context, &HandlerData, &EstablisherFrame, &NvContext);
  134. }
  135. if (!Context.Rip)
  136. {
  137. break;
  138. }
  139. if (FramesToSkip > 0)
  140. {
  141. FramesToSkip--;
  142. continue;
  143. }
  144. if (Frame >= FramesToCapture)
  145. {
  146. break;
  147. }
  148. BackTrace[Frame] = (PVOID)Context.Rip;
  149. if (BackTraceHash)
  150. {
  151. *BackTraceHash += (Context.Rip & 0xffffffff);
  152. }
  153. Frame++;
  154. }
  155. return (WORD)Frame;
  156. }
  157. #define CaptureStack(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash) \
  158. StackTrace64(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash)
  159. #elif defined (_M_IX86)
  160. #pragma optimize( "g", off )
  161. #pragma warning( push )
  162. #pragma warning( disable : 4748 )
  163. #pragma warning( disable : 4995 )
  164. WORD StackTrace86(
  165. _In_ DWORD FramesToSkip,
  166. _In_ DWORD FramesToCapture,
  167. _Out_writes_to_(FramesToCapture, return) PVOID * BackTrace,
  168. _Inout_opt_ PDWORD BackTraceHash,
  169. __in_opt CONST PCONTEXT InitialContext = NULL
  170. )
  171. {
  172. _Analysis_assume_(FramesToSkip >= 0);
  173. _Analysis_assume_(FramesToCapture >= 0);
  174. DWORD MachineType;
  175. CONTEXT Context;
  176. STACKFRAME64 StackFrame;
  177. if (InitialContext == NULL)
  178. {
  179. //RtlCaptureContext( &Context );
  180. ZeroMemory(&Context, sizeof(CONTEXT));
  181. Context.ContextFlags = CONTEXT_CONTROL;
  182. __asm
  183. {
  184. Label:
  185. mov[Context.Ebp], ebp;
  186. mov[Context.Esp], esp;
  187. mov eax, [Label];
  188. mov[Context.Eip], eax;
  189. }
  190. }
  191. else
  192. {
  193. CopyMemory(&Context, InitialContext, sizeof(CONTEXT));
  194. }
  195. ZeroMemory(&StackFrame, sizeof(STACKFRAME64));
  196. MachineType = IMAGE_FILE_MACHINE_I386;
  197. StackFrame.AddrPC.Offset = Context.Eip;
  198. StackFrame.AddrPC.Mode = AddrModeFlat;
  199. StackFrame.AddrFrame.Offset = Context.Ebp;
  200. StackFrame.AddrFrame.Mode = AddrModeFlat;
  201. StackFrame.AddrStack.Offset = Context.Esp;
  202. StackFrame.AddrStack.Mode = AddrModeFlat;
  203. WORD FrameCount = 0;
  204. while (FrameCount < FramesToSkip + FramesToCapture)
  205. {
  206. if (!pfnStackWalk64(MachineType, GetCurrentProcess(), GetCurrentThread(), &StackFrame,
  207. NULL, NULL, pfnSymFunctionTableAccess64, pfnSymGetModuleBase64, NULL))
  208. {
  209. break;
  210. }
  211. if (StackFrame.AddrPC.Offset != 0)
  212. {
  213. if (FrameCount >= FramesToSkip)
  214. {
  215. #pragma warning(suppress: 22102)
  216. #pragma warning(suppress: 26014)
  217. BackTrace[FrameCount - FramesToSkip] = (PVOID)StackFrame.AddrPC.Offset;
  218. if (BackTraceHash)
  219. {
  220. *BackTraceHash += (StackFrame.AddrPC.Offset & 0xffffffff);
  221. }
  222. }
  223. FrameCount++;
  224. }
  225. else
  226. {
  227. break;
  228. }
  229. }
  230. if (FrameCount > FramesToSkip)
  231. {
  232. return (WORD)(FrameCount - FramesToSkip);
  233. }
  234. else
  235. {
  236. return 0;
  237. }
  238. }
  239. #pragma warning( pop )
  240. #pragma optimize( "", on )
  241. #define CaptureStack(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash) \
  242. RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash)
  243. #else
  244. #define CaptureStack(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash) \
  245. RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash)
  246. #endif
  247. struct SymbolInfoPackage : public SYMBOL_INFO_PACKAGEW
  248. {
  249. SymbolInfoPackage() { Init(); }
  250. void Init()
  251. {
  252. si.SizeOfStruct = sizeof(SYMBOL_INFOW);
  253. si.MaxNameLen = sizeof(name);
  254. }
  255. };
  256. struct ModuleInfo : public IMAGEHLP_MODULEW64
  257. {
  258. ModuleInfo() { Init(); }
  259. void Init()
  260. {
  261. SizeOfStruct = sizeof(IMAGEHLP_MODULEW64);
  262. }
  263. };
  264. bool FaultInjection::InitializeSym()
  265. {
  266. if (symInitialized)
  267. {
  268. return true;
  269. }
  270. // load dbghelp APIs
  271. if (hDbgHelp == NULL)
  272. {
  273. hDbgHelp = LoadLibraryEx(_u("dbghelp.dll"), 0, 0);
  274. }
  275. if (hDbgHelp == NULL)
  276. {
  277. fwprintf(stderr, _u("Failed to load dbghelp.dll for stack walking, gle=0x%08x\n"), GetLastError());
  278. fflush(stderr);
  279. return false;
  280. }
  281. #define FIDELAYLOAD(fn) pfn##fn = (decltype(fn)*)GetProcAddress(hDbgHelp, #fn); \
  282. if (pfn##fn == nullptr){\
  283. fwprintf(stderr, _u("Failed to load sigs:%s\n"), _u(#fn)); \
  284. fflush(stderr); \
  285. return false; \
  286. }
  287. FIDELAYLOAD(SymInitialize);
  288. FIDELAYLOAD(SymCleanup);
  289. FIDELAYLOAD(SymFromAddrW);
  290. FIDELAYLOAD(SymFromNameW);
  291. FIDELAYLOAD(SymEnumSymbolsW);
  292. FIDELAYLOAD(SymGetModuleInfoW64);
  293. FIDELAYLOAD(SymMatchStringW);
  294. FIDELAYLOAD(SymSetOptions);
  295. FIDELAYLOAD(MiniDumpWriteDump);
  296. FIDELAYLOAD(SymFunctionTableAccess64);
  297. FIDELAYLOAD(SymGetModuleBase64);
  298. FIDELAYLOAD(StackWalk64);
  299. #undef FIDELAYLOAD
  300. // TODO: StackBackTrace.cpp also call SymInitialize, but this can only be called once before cleanup
  301. if (!pfnSymInitialize(GetCurrentProcess(), NULL, TRUE))
  302. {
  303. fwprintf(stderr, _u("SymInitialize failed, gle=0x%08x\n"), GetLastError());
  304. fflush(stderr);
  305. return false;
  306. }
  307. symInitialized = true;
  308. return true;
  309. }
  310. #pragma endregion helpers
  311. FaultInjection FaultInjection::Global;
  312. static CriticalSection cs_Sym; // for Sym* method is not thread safe
  313. const auto& globalFlags = Js::Configuration::Global.flags;
  314. PVOID FaultInjection::vectoredExceptionHandler = nullptr;
  315. DWORD FaultInjection::exceptionFilterRemovalLastError = 0;
  316. THREAD_LOCAL int(*Js::FaultInjection::pfnHandleAV)(int, PEXCEPTION_POINTERS) = nullptr;
  317. static SymbolInfoPackage sip;
  318. static ModuleInfo mi;
  319. const char16* crashStackStart = _u("=====Callstack for this exception=======\n");
  320. const char16* crashStackEnd = _u("=====End of callstack for this exception=======\n");
  321. const char16* injectionStackStart = _u("=====Fault injecting record=====\n");
  322. const char16* injectionStackEnd = _u("=====End of Fault injecting record=====\n");
  323. typedef struct _RANGE{
  324. UINT_PTR startAddress;
  325. UINT_PTR endAddress;
  326. }RANGE, *PRANGE;
  327. typedef struct _FUNCTION_SIGNATURES
  328. {
  329. int count;
  330. RANGE signatures[ANYSIZE_ARRAY];
  331. } FUNCTION_SIGNATURES, *PFUNCTION_SIGNATURES;
  332. // function address ranges of each signature
  333. // use for faster address matching instead of symbol table lookup when reproducing
  334. PFUNCTION_SIGNATURES baselineFuncSigs[FaultInjection::MAX_FRAME_COUNT] = { 0 };
  335. // record hit count of each frame when Faults are injected.
  336. unsigned int stackMatchRank[FaultInjection::MAX_FRAME_COUNT] = { 0 };
  337. #define FAULT_TYPE(x) _u(#x),\
  338. const char16 *FaultInjection::FaultTypeNames[] =
  339. {
  340. #include "FaultTypes.h"
  341. };
  342. #undef FAULT_TYPE
  343. static_assert(sizeof(FaultInjection::FaultTypeNames) == FaultInjection::FaultType::FaultTypeCount*sizeof(char16*),
  344. "FaultTypeNames count is wrong");
  345. void FaultInjection::FaultInjectionTypes::EnableType(FaultType type)
  346. {
  347. Assert(type >= 0 && type < FaultType::FaultTypeCount);
  348. setBit(type, 1);
  349. }
  350. bool FaultInjection::FaultInjectionTypes::IsEnabled(FaultType type)
  351. {
  352. Assert(type >= 0 && type < FaultType::FaultTypeCount);
  353. return getBit(type) == 0x1;
  354. }
  355. bool FaultInjection::FaultInjectionTypes::IsEnabled(const char16* name)
  356. {
  357. for (int type = 0; type < FaultType::FaultTypeCount; type++)
  358. {
  359. if (wcscmp(FaultTypeNames[type], name) == 0)
  360. return getBit(type) == 0x1;
  361. }
  362. AssertMsg(false, "Unknown fault type name");
  363. return false;
  364. }
  365. FaultInjection::FaultInjection()
  366. {
  367. stackMatchInitialized = Uninitialized;
  368. countOfInjectionPoints = 0;
  369. hDbgHelp = NULL;
  370. InjectionFirstRecord = nullptr;
  371. InjectionLastRecordRef = &InjectionFirstRecord;
  372. InjectionRecordsCount = 0;
  373. FaultInjectionCookie = 0;
  374. baselineFrameCount = 0;
  375. stackHashOfAllInjectionPointsSize = 256;
  376. stackHashOfAllInjectionPoints = (ULONG_PTR*)malloc(stackHashOfAllInjectionPointsSize*sizeof(ULONG_PTR));
  377. faultInjectionTypes = nullptr;
  378. symInitialized = false;
  379. for (int i = 0; i < MAX_FRAME_COUNT; i++)
  380. {
  381. baselineStack[i] = nullptr;
  382. baselineAddresses[i] = 0;
  383. }
  384. }
  385. FaultInjection::~FaultInjection()
  386. {
  387. RemoveExceptionFilters();
  388. // when fault injection count only is passing from jscript.config(in case of running on 3rd part host)
  389. // and the host don't have code to output the fault injection count, we still able to do the fault injection test
  390. if (globalFlags.FaultInjection == FaultMode::CountOnly
  391. || globalFlags.FaultInjection == FaultMode::StackMatchCountOnly)
  392. {
  393. fprintf(stderr, "FaultInjection - Total Allocation Count:%u\n", countOfInjectionPoints);
  394. fflush(stderr);
  395. FILE *fp;
  396. char countFileName[64];
  397. sprintf_s(countFileName, "ChakraFaultInjectionCount_%u.txt", GetCurrentProcessId());
  398. if (fopen_s(&fp, countFileName, "w") == 0)
  399. {
  400. fprintf(fp, "FaultInjection - Total Allocation Count:%u\n", countOfInjectionPoints);
  401. fflush(fp);
  402. fclose(fp);
  403. }
  404. for (int i = 0; i < MAX_FRAME_COUNT; i++)
  405. {
  406. if (stackMatchRank[i] == 0)
  407. {
  408. break;
  409. }
  410. fwprintf(stderr, _u("FaultInjection stack matching rank %d: %u\n"), i + 1, stackMatchRank[i]);
  411. }
  412. fflush(stderr);
  413. }
  414. if (globalFlags.FaultInjection == StackHashCountOnly)
  415. {
  416. FILE *fp;
  417. if (fopen_s(&fp, "ChakraFaultInjectionHashes.txt", "w") == 0)
  418. {
  419. for (uint i = 0; i < countOfInjectionPoints; i++)
  420. {
  421. fprintf(fp, "%p\n", (void*)stackHashOfAllInjectionPoints[i]);
  422. }
  423. fflush(fp);
  424. fclose(fp);
  425. }
  426. }
  427. free(stackHashOfAllInjectionPoints);
  428. stackHashOfAllInjectionPoints = nullptr;
  429. if (globalFlags.FaultInjection == FaultMode::DisplayAvailableFaultTypes)
  430. {
  431. Output::Print(_u("Available Fault Types:\n"));
  432. for (int i = 0; i < FaultType::FaultTypeCount; i++)
  433. {
  434. Output::Print(_u("%d-%s\n"), i, FaultTypeNames[i]);
  435. }
  436. Output::Flush();
  437. }
  438. InjectionRecord* head = InjectionFirstRecord;
  439. while (head != nullptr)
  440. {
  441. InjectionRecord* next = head->next;
  442. if (head->StackData)
  443. {
  444. free(head->StackData);
  445. }
  446. free(head);
  447. head = next;
  448. }
  449. for (int i = 0; i < MAX_FRAME_COUNT; i++)
  450. {
  451. if (baselineStack[i])
  452. {
  453. free(baselineStack[i]);
  454. }
  455. if (baselineFuncSigs[i])
  456. {
  457. free(baselineFuncSigs[i]);
  458. }
  459. }
  460. if (stackMatchInitialized == Succeeded)
  461. {
  462. pfnSymCleanup(GetCurrentProcess());
  463. }
  464. if (hDbgHelp)
  465. {
  466. FreeLibrary(hDbgHelp);
  467. }
  468. if (faultInjectionTypes)
  469. {
  470. faultInjectionTypes->~FaultInjectionTypes();
  471. NoCheckHeapDelete(faultInjectionTypes);
  472. }
  473. }
  474. bool FaultInjection::IsFaultEnabled(FaultType faultType)
  475. {
  476. if (!faultInjectionTypes)
  477. {
  478. faultInjectionTypes = NoCheckHeapNew(FaultInjectionTypes);
  479. if ((const char16*)globalFlags.FaultInjectionType == nullptr)
  480. {
  481. // no -FaultInjectionType specified, inject all
  482. faultInjectionTypes->EnableAll();
  483. }
  484. else
  485. {
  486. ParseFaultTypes(globalFlags.FaultInjectionType);
  487. }
  488. }
  489. return faultInjectionTypes->IsEnabled(faultType);
  490. }
  491. bool FaultInjection::IsFaultInjectionOn(FaultType faultType)
  492. {
  493. return globalFlags.FaultInjection >= 0 //-FaultInjection switch
  494. && IsFaultEnabled(faultType);
  495. }
  496. void FaultInjection::ParseFaultTypes(const char16* szFaultTypes)
  497. {
  498. auto charCount = wcslen(szFaultTypes) + 1;
  499. char16* szTypes = (char16*)malloc(charCount*sizeof(char16));
  500. AssertMsg(szTypes, "OOM in FaultInjection Infra");
  501. wcscpy_s(szTypes, charCount, szFaultTypes);
  502. const char16* delims = _u(",");
  503. char16 *nextTok = nullptr;
  504. char16* tok = wcstok_s(szTypes, delims, &nextTok);
  505. while (tok != NULL)
  506. {
  507. if (wcslen(tok) > 0)
  508. {
  509. if (iswdigit(tok[0]))
  510. {
  511. auto numType = _wtoi(tok);
  512. for (int i = 0; i< FaultType::FaultTypeCount; i++)
  513. {
  514. if (numType & (1 << i))
  515. {
  516. faultInjectionTypes->EnableType(i);
  517. }
  518. }
  519. }
  520. else if (tok[0] == _u('#'))
  521. {
  522. // FaultInjectionType:#1-4,#6 format, not flags
  523. auto tok1 = tok + 1;
  524. if (wcslen(tok1)>0 && iswdigit(tok1[0]))
  525. {
  526. char16* pDash = wcschr(tok1, _u('-'));
  527. if (pDash)
  528. {
  529. for (int i = _wtoi(tok1); i <= _wtoi(pDash + 1); i++)
  530. {
  531. faultInjectionTypes->EnableType(i);
  532. }
  533. }
  534. else
  535. {
  536. faultInjectionTypes->EnableType(_wtoi(tok1));
  537. }
  538. }
  539. }
  540. else
  541. {
  542. for (int i = 0; i < FaultType::FaultTypeCount; i++)
  543. {
  544. if (_wcsicmp(FaultTypeNames[i], tok) == 0)
  545. {
  546. faultInjectionTypes->EnableType(i);
  547. break;
  548. }
  549. }
  550. }
  551. }
  552. tok = wcstok_s(NULL, delims, &nextTok);
  553. }
  554. free(szTypes);
  555. }
  556. static void SmashLambda(_Inout_z_ char16* str)
  557. {
  558. //jscript9test!<lambda_dc7f9e8c591f1832700d6567e43faa6c>::operator()
  559. const char16 lambdaSig[] = _u("<lambda_");
  560. const int lambdaSigLen = (int)wcslen(lambdaSig);
  561. auto temp = str;
  562. while (temp != nullptr)
  563. {
  564. auto lambdaStart = wcsstr(temp, lambdaSig);
  565. temp = nullptr;
  566. if (lambdaStart != nullptr)
  567. {
  568. auto lambdaEnd = wcschr(lambdaStart, _u('>'));
  569. temp = lambdaEnd;
  570. if (lambdaEnd != nullptr && lambdaEnd - lambdaStart == lambdaSigLen + 32)
  571. {
  572. lambdaStart += lambdaSigLen;
  573. while (lambdaStart < lambdaEnd)
  574. {
  575. *(lambdaStart++) = _u('?');
  576. }
  577. }
  578. }
  579. }
  580. }
  581. bool FaultInjection::EnsureStackMatchInfraInitialized()
  582. {
  583. if (stackMatchInitialized == Succeeded)
  584. {
  585. return true;
  586. }
  587. else if (stackMatchInitialized == FailedToInitialize)
  588. {
  589. // previous try to initialize and failed
  590. return false;
  591. }
  592. else if (stackMatchInitialized == Uninitialized)
  593. {
  594. stackMatchInitialized = FailedToInitialize; //tried
  595. if (!InitializeSym())
  596. {
  597. return false;
  598. }
  599. // read baseline stack file
  600. FILE *fp = nullptr;
  601. const char16 *stackFile = globalFlags.FaultInjectionStackFile;//default: _u("stack.txt");
  602. auto err = _wfopen_s(&fp, stackFile, _u("r"));
  603. if (err != 0 || fp == nullptr)
  604. {
  605. fwprintf(stderr, _u("Failed to load %s, gle=0x%08x\n"), stackFile, GetLastError());
  606. fflush(stderr);
  607. return false;
  608. }
  609. char16 buffer[MAX_SYM_NAME]; // assume the file is normal
  610. unsigned int maxLineCount =
  611. (globalFlags.FaultInjectionStackLineCount < 0
  612. || globalFlags.FaultInjectionStackLineCount > MAX_FRAME_COUNT
  613. || globalFlags.FaultInjection == FaultMode::StackMatchCountOnly)
  614. ? MAX_FRAME_COUNT : globalFlags.FaultInjectionStackLineCount;
  615. while (fgetws(buffer, MAX_SYM_NAME, fp))
  616. {
  617. if (wcscmp(buffer, injectionStackStart) == 0)
  618. {
  619. baselineFrameCount = 0;
  620. continue;
  621. }
  622. if (baselineFrameCount >= maxLineCount)
  623. {
  624. continue; // don't break because we can hit the start marker and reset
  625. }
  626. const char16 jscript9test[] = _u("jscript9test!");
  627. const char16 jscript9[] = _u("jscript9!");
  628. char16* symbolStart = stristr(buffer, jscript9test);
  629. if (symbolStart == nullptr)
  630. {
  631. symbolStart = stristr(buffer, jscript9);
  632. }
  633. if (symbolStart == nullptr)
  634. {
  635. continue;// no "jscript9test!", skip this line
  636. }
  637. if (wcsstr(symbolStart, _u("Js::FaultInjection")) != NULL)
  638. { // skip faultinjection infra frames.
  639. continue;
  640. }
  641. auto plus = wcschr(symbolStart, _u('+'));
  642. if (plus)
  643. {
  644. *plus = _u('\0');
  645. }
  646. else
  647. {
  648. trimRight(symbolStart);
  649. }
  650. SmashLambda(symbolStart);
  651. size_t len = wcslen(symbolStart);
  652. if (baselineStack[baselineFrameCount] == nullptr)
  653. {
  654. baselineStack[baselineFrameCount] = (char16*)malloc((len + 1)*sizeof(char16));
  655. AssertMsg(baselineStack[baselineFrameCount], "OOM in FaultInjection Infra");
  656. }
  657. else
  658. {
  659. auto tmp = (char16*)realloc(baselineStack[baselineFrameCount], (len + 1)*sizeof(char16));
  660. AssertMsg(tmp, "OOM in FaultInjection Infra");
  661. baselineStack[baselineFrameCount] = tmp;
  662. }
  663. wcscpy_s(baselineStack[baselineFrameCount], len + 1, symbolStart);
  664. baselineFrameCount++;
  665. }
  666. fclose(fp);
  667. OutputDebugString(_u("Fault will be injected when hit following stack:\n"));
  668. for (uint i = 0; i<baselineFrameCount; i++)
  669. {
  670. OutputDebugString(baselineStack[i]);
  671. OutputDebugString(_u("\n"));
  672. if (wcschr(baselineStack[i], '*') != nullptr || wcschr(baselineStack[i], '?') != nullptr)
  673. {
  674. continue; // there's wildcard in this line, don't use address matching
  675. }
  676. // enum symbols, if succeed we compare with address when doing stack matching
  677. pfnSymEnumSymbolsW(GetCurrentProcess(), 0, baselineStack[i],
  678. [](_In_ PSYMBOL_INFOW pSymInfo, _In_ ULONG SymbolSize, _In_opt_ PVOID UserContext)->BOOL
  679. {
  680. Assert(UserContext != nullptr); // did passed in the user context
  681. if (pSymInfo->Size > 0)
  682. {
  683. PFUNCTION_SIGNATURES* sigs = (PFUNCTION_SIGNATURES*)UserContext;
  684. int count = (*sigs) == nullptr ? 0 : (*sigs)->count;
  685. auto tmp = (PFUNCTION_SIGNATURES)realloc(*sigs, sizeof(FUNCTION_SIGNATURES) + count*sizeof(RANGE));
  686. AssertMsg(tmp, "OOM when allocating for FaultInjection Stack matching objects");
  687. *sigs = tmp;
  688. (*sigs)->count = count;
  689. (*sigs)->signatures[count].startAddress = (UINT_PTR)pSymInfo->Address;
  690. (*sigs)->signatures[count].endAddress = (UINT_PTR)(pSymInfo->Address + pSymInfo->Size);
  691. (*sigs)->count++;
  692. }
  693. return TRUE;
  694. }, &baselineFuncSigs[i]);
  695. }
  696. stackMatchInitialized = Succeeded; // initialized
  697. return true;
  698. }
  699. return false;
  700. }
  701. bool FaultInjection::IsCurrentStackMatch()
  702. {
  703. AutoCriticalSection autocs(&cs_Sym); // sym* API is thread unsafe
  704. if (!EnsureStackMatchInfraInitialized())
  705. {
  706. return false;
  707. }
  708. DWORD64 dwSymDisplacement = 0;
  709. auto hProcess = GetCurrentProcess();
  710. static void* framesBuffer[FaultInjection::MAX_FRAME_COUNT];
  711. auto frameCount = CaptureStack(0, MAX_FRAME_COUNT, framesBuffer, 0);
  712. uint n = 0;
  713. for (uint i = 0; i < frameCount; i++)
  714. {
  715. if (n >= baselineFrameCount)
  716. {
  717. return true;
  718. }
  719. if (!AutoSystemInfo::Data.IsJscriptModulePointer(framesBuffer[i]))
  720. { // skip non-Chakra frame
  721. continue;
  722. }
  723. bool match = false;
  724. if (baselineFuncSigs[n] != nullptr)
  725. {
  726. for (int j = 0; j<baselineFuncSigs[n]->count; j++)
  727. {
  728. match = baselineFuncSigs[n]->signatures[j].startAddress <= (UINT_PTR)framesBuffer[i]
  729. && (UINT_PTR)framesBuffer[i] < baselineFuncSigs[n]->signatures[j].endAddress;
  730. if (match)
  731. {
  732. break;
  733. }
  734. }
  735. }
  736. else
  737. {
  738. // fallback to symbol name matching
  739. sip.Init();
  740. if (!pfnSymFromAddrW(hProcess, (DWORD64)framesBuffer[i], &dwSymDisplacement, &sip.si))
  741. {
  742. continue;
  743. }
  744. SmashLambda(sip.si.Name);
  745. // Only search sigs name, can use wildcard in baseline file
  746. match = stristr(baselineStack[n], sip.si.Name) != nullptr
  747. || pfnSymMatchStringW(sip.si.Name, baselineStack[n], false);// wildcard
  748. }
  749. if (match)
  750. {
  751. stackMatchRank[n]++;
  752. if (n == 0)
  753. {
  754. n++;
  755. continue;
  756. }
  757. }
  758. else if (n > 0)
  759. {
  760. return false;
  761. }
  762. // First line in baseline is found, moving forward.
  763. if (n > 0)
  764. {
  765. n++;
  766. }
  767. }
  768. return false;
  769. }
  770. static bool faultInjectionDebug = false;
  771. static bool triedToInstallExceptionFilter = false;
  772. static CriticalSection csFautInjection;
  773. void FaultInjection::InstallExceptionFilters()
  774. {
  775. if (triedToInstallExceptionFilter)
  776. {
  777. return;
  778. }
  779. AutoCriticalSection autoCS(&csFautInjection);
  780. if (triedToInstallExceptionFilter)
  781. {
  782. return;
  783. }
  784. triedToInstallExceptionFilter = true;
  785. if (GetEnvironmentVariable(_u("FAULTINJECTION_DEBUG"), nullptr, 0) != 0)
  786. {
  787. faultInjectionDebug = true;
  788. }
  789. if (globalFlags.FaultInjection >= 0)
  790. {
  791. // initialize symbol system here instead of inside the exception filter
  792. // because some hard stack overflow can happen in SymInitialize
  793. // when the exception filter is handling stack overflow exception
  794. if (!FaultInjection::Global.InitializeSym())
  795. {
  796. return;
  797. }
  798. //C28725: Use Watson instead of this SetUnhandledExceptionFilter.
  799. #pragma prefast(suppress: 28725)
  800. SetUnhandledExceptionFilter([](_In_ struct _EXCEPTION_POINTERS *ExceptionInfo)->LONG
  801. {
  802. return FaultInjectionExceptionFilter(ExceptionInfo);
  803. });
  804. vectoredExceptionHandler = AddVectoredExceptionHandler(0, [](_In_ struct _EXCEPTION_POINTERS *ExceptionInfo)->LONG
  805. {
  806. switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
  807. {
  808. // selected fatal exceptions:
  809. case STATUS_ACCESS_VIOLATION:
  810. {
  811. if (pfnHandleAV
  812. && pfnHandleAV(ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo) == EXCEPTION_CONTINUE_EXECUTION)
  813. {
  814. return EXCEPTION_CONTINUE_EXECUTION;
  815. }
  816. }
  817. case STATUS_ASSERTION_FAILURE:
  818. case STATUS_STACK_OVERFLOW:
  819. FaultInjectionExceptionFilter(ExceptionInfo);
  820. TerminateProcess(::GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
  821. default:
  822. return EXCEPTION_CONTINUE_SEARCH;
  823. }
  824. });
  825. }
  826. }
  827. void FaultInjection::RemoveExceptionFilters()
  828. {
  829. //C28725: Use Watson instead of this SetUnhandledExceptionFilter.
  830. #pragma prefast(suppress: 28725)
  831. SetUnhandledExceptionFilter(nullptr);
  832. if (vectoredExceptionHandler != nullptr)
  833. {
  834. RemoveVectoredExceptionHandler(vectoredExceptionHandler);
  835. // remove the handler from the list second time.
  836. // This code is called inside an exception handler, when the exception handler is called,
  837. // the refcount of the handler in ntdll!LdrpVectorHandlerList is increased,
  838. // so need to call RemoveVectoredExceptionHandler twice to really remove the handler from the list
  839. // otherwise the exception from the handler itself will re-enter the handler
  840. RemoveVectoredExceptionHandler(vectoredExceptionHandler);
  841. vectoredExceptionHandler = nullptr;
  842. }
  843. }
  844. // Calculate stack hash by adding the addresses (only jscript9 frames)
  845. UINT_PTR FaultInjection::CalculateStackHash(void* frames[], WORD frameCount, WORD framesToSkip)
  846. {
  847. UINT_PTR hash = 0;
  848. for (int i = framesToSkip; i < frameCount; i++)
  849. {
  850. if (AutoSystemInfo::Data.IsJscriptModulePointer(frames[i]))
  851. {
  852. hash += (UINT_PTR)frames[i] - AutoSystemInfo::Data.dllLoadAddress;
  853. }
  854. }
  855. return hash;
  856. }
  857. // save the stack data for dump debugging use
  858. // to get list of fault injection points:
  859. // !list -t jscript9test!Js::FaultInjection::InjectionRecord.next -e -x "dps @$extret @$extret+0x128" poi(@@c++(&jscript9test!Js::FaultInjection::Global.InjectionFirstRecord))
  860. // to rebuild the stack (locals are available)
  861. // .cxr @@C++(&jscript9test!Js::FaultInjection::Global.InjectionFirstRecord->Context)
  862. _NOINLINE void FaultInjection::dumpCurrentStackData(LPCWSTR name /*= nullptr*/, size_t size /*= 0*/)
  863. {
  864. #if !defined(_M_ARM32_OR_ARM64)
  865. static bool keepBreak = true; // for disabling following breakpoint by editing the value
  866. if (keepBreak && IsDebuggerPresent())
  867. {
  868. DebugBreak();
  869. }
  870. InjectionRecord* record = (InjectionRecord*)malloc(sizeof(InjectionRecord));
  871. if (record == nullptr) return;
  872. ZeroMemory(record, sizeof(InjectionRecord));
  873. auto _stackbasepointer = ((PNT_TIB)NtCurrentTeb())->StackBase;
  874. // context
  875. RtlCaptureContext(&record->Context);
  876. #if _M_X64
  877. auto& _stackpointer = record->Context.Rsp;
  878. auto& _basepointer = record->Context.Rbp;
  879. #elif _M_IX86
  880. auto& _stackpointer = record->Context.Esp;
  881. auto& _basepointer = record->Context.Ebp;
  882. #endif
  883. typedef decltype(_stackpointer) spType;
  884. record->StackDataLength = (spType)_stackbasepointer - _stackpointer;
  885. record->StackData = malloc(record->StackDataLength);
  886. if (record->StackData)
  887. {
  888. memcpy(record->StackData, (void*)_stackpointer, record->StackDataLength);
  889. _basepointer = _basepointer + (spType)record->StackData - _stackpointer;
  890. _stackpointer = (spType)record->StackData; // for .cxr switching to this state
  891. }
  892. if (name)
  893. {
  894. wcscpy_s(record->name, name);
  895. }
  896. record->allocSize = size;
  897. // stack frames
  898. record->FrameCount = CaptureStack(0, MAX_FRAME_COUNT, record->StackFrames, 0);
  899. // hash
  900. record->hash = CalculateStackHash(record->StackFrames, record->FrameCount, 2);
  901. fwprintf(stderr, _u("***FI: Fault Injected, StackHash:%p\n"), (void*)record->hash);
  902. fflush(stderr);
  903. *InjectionLastRecordRef = record;
  904. InjectionLastRecordRef = &record->next;
  905. InjectionRecordsCount++;
  906. #endif // _M_ARM || _M_ARM64
  907. }
  908. bool FaultInjection::ShouldInjectFault(FaultType fType, LPCWSTR name, size_t size)
  909. {
  910. bool shouldInjectionFault = ShouldInjectFaultHelper(fType, name, size);
  911. if (shouldInjectionFault && fType != FaultType::ScriptTerminationOnDispose)
  912. {
  913. dumpCurrentStackData(name, size);
  914. }
  915. return shouldInjectionFault;
  916. }
  917. bool FaultInjection::ShouldInjectFaultHelper(FaultType fType, LPCWSTR name, size_t size)
  918. {
  919. if (globalFlags.FaultInjection < 0)
  920. {
  921. return false; // no -FaultInjection switch
  922. }
  923. if (globalFlags.FaultInjectionFilter && _wcsicmp(globalFlags.FaultInjectionFilter, name) != 0)
  924. {
  925. return false;
  926. }
  927. if (globalFlags.FaultInjectionAllocSize >= 0 && size != (size_t)globalFlags.FaultInjectionAllocSize)
  928. {
  929. return false;
  930. }
  931. // install exception filter to smart dump for faultinjection
  932. // when reproducing in debugger, only let debugger catch the exception
  933. // can't do this in ctor because the global flags are not initialized yet
  934. InstallExceptionFilters();
  935. bool validInjectionPoint = IsFaultEnabled(fType);
  936. if (!validInjectionPoint)
  937. {
  938. return false;
  939. }
  940. bool shouldInjectionFault = false;
  941. switch (globalFlags.FaultInjection)
  942. {
  943. case CountEquals:
  944. //Fault inject on count only when equal
  945. if (countOfInjectionPoints == (uint)globalFlags.FaultInjectionCount)
  946. {
  947. shouldInjectionFault = true;
  948. }
  949. break;
  950. case CountEqualsOrAbove:
  951. //Fault inject on count greater than or equal
  952. if (countOfInjectionPoints >= (uint)globalFlags.FaultInjectionCount)
  953. {
  954. shouldInjectionFault = true;
  955. }
  956. break;
  957. case StackMatch:
  958. // We don't care about the fault if we already passed in terms of count, or the stack doesn't match
  959. if (countOfInjectionPoints > (uint)globalFlags.FaultInjectionCount || !IsCurrentStackMatch())
  960. {
  961. validInjectionPoint = false;
  962. }
  963. else // otherwise determine if we will be injecting this time around
  964. {
  965. shouldInjectionFault = countOfInjectionPoints == (uint)globalFlags.FaultInjectionCount || globalFlags.FaultInjectionCount == -1;
  966. }
  967. break;
  968. case StackMatchCountOnly:
  969. validInjectionPoint = IsCurrentStackMatch();
  970. break;
  971. case StackHashCountOnly:
  972. {
  973. // extend the storage when necessary
  974. if (countOfInjectionPoints > stackHashOfAllInjectionPointsSize)
  975. {
  976. stackHashOfAllInjectionPointsSize += 1024;
  977. auto extended = (ULONG_PTR*)realloc(stackHashOfAllInjectionPoints,
  978. stackHashOfAllInjectionPointsSize*sizeof(ULONG_PTR));
  979. AssertMsg(extended, "OOM in FaultInjection Infra");
  980. stackHashOfAllInjectionPoints = extended;
  981. }
  982. void* StackFrames[MAX_FRAME_COUNT];
  983. auto FrameCount = CaptureStack(0, MAX_FRAME_COUNT, StackFrames, 0);
  984. UINT_PTR hash = CalculateStackHash(StackFrames, FrameCount, 2);
  985. stackHashOfAllInjectionPoints[countOfInjectionPoints] = hash;
  986. break;
  987. }
  988. case CountOnly:
  989. break;
  990. case DisplayAvailableFaultTypes:
  991. case InstallExceptionHandlerOnly:
  992. return false;
  993. default:
  994. AssertMsg(false, "Invalid FaultInjection mode");
  995. break;
  996. }
  997. if (validInjectionPoint)
  998. {
  999. countOfInjectionPoints++;
  1000. }
  1001. // try to lookup stack hash, to see if it matches
  1002. if (!shouldInjectionFault)
  1003. {
  1004. const UINT_PTR expectedHash = HexStrToAddress((LPCWSTR)globalFlags.FaultInjectionStackHash);
  1005. if (expectedHash != 0)
  1006. {
  1007. void* StackFrames[MAX_FRAME_COUNT];
  1008. auto FrameCount = CaptureStack(0, MAX_FRAME_COUNT, StackFrames, 0);
  1009. UINT_PTR hash = CalculateStackHash(StackFrames, FrameCount, 2);
  1010. if (hash == expectedHash)
  1011. {
  1012. shouldInjectionFault = true;
  1013. }
  1014. }
  1015. }
  1016. return shouldInjectionFault;
  1017. }
  1018. // For faster fault injection test run, filter out the AVs on same IP/hash
  1019. void FaultInjection::FaultInjectionAnalyzeException(_EXCEPTION_POINTERS *ep)
  1020. {
  1021. #if !defined(_M_ARM32_OR_ARM64) // not support ARM for now, add support in case we run fault injection on ARM
  1022. AutoCriticalSection autocs(&cs_Sym);
  1023. CONTEXT* pContext = ep->ContextRecord;
  1024. // always show stack for crash and fault injection points in console,
  1025. // this can be used for additional stack matching repro
  1026. HANDLE hProcess = GetCurrentProcess();
  1027. DWORD64 dwSymDisplacement = 0;
  1028. auto printFrame = [&](LPVOID addr)
  1029. {
  1030. sip.Init();
  1031. if (pfnSymFromAddrW(hProcess, (DWORD64)addr, &dwSymDisplacement, &sip.si))
  1032. {
  1033. mi.Init();
  1034. pfnSymGetModuleInfoW64(hProcess, (DWORD64)addr, &mi);
  1035. fwprintf(stderr, _u("%s!%s+0x%llx\n"), mi.ModuleName, sip.si.Name, (ULONGLONG)dwSymDisplacement);
  1036. }
  1037. else
  1038. {
  1039. fwprintf(stderr, _u("0x%p\n"), addr);
  1040. }
  1041. };
  1042. LPVOID backTrace[MAX_FRAME_COUNT] = { 0 };
  1043. DWORD64 displacements[MAX_FRAME_COUNT] = { 0 };
  1044. #if _M_IX86
  1045. WORD nStackCount = StackTrace86(0, MAX_FRAME_COUNT, backTrace, 0, pContext);
  1046. #elif _M_X64
  1047. WORD nStackCount = StackTrace64(0, MAX_FRAME_COUNT, backTrace, 0, pContext);
  1048. #else
  1049. WORD nStackCount = CaptureStack(0, MAX_FRAME_COUNT, backTrace, 0);
  1050. #endif
  1051. // Print current crash stacks
  1052. fwprintf(stderr, crashStackStart);
  1053. for (int i = 0; i < nStackCount; i++)
  1054. {
  1055. printFrame(backTrace[i]);
  1056. displacements[i] = dwSymDisplacement;
  1057. }
  1058. LPVOID internalExceptionAddr = nullptr;
  1059. for (int i = 0; i < nStackCount - 1 && internalExceptionAddr == nullptr; i++)
  1060. {
  1061. if (backTrace[i] == (char*)Js::Throw::FatalInternalError + displacements[i])
  1062. {
  1063. internalExceptionAddr = backTrace[i + 1];
  1064. }
  1065. else if (backTrace[i] == (char*)Js::Throw::ReportAssert + displacements[i])
  1066. {
  1067. if (backTrace[i + 1] == (char*)Js::Throw::InternalError + displacements[i + 1])
  1068. {
  1069. // skip to next frame
  1070. }
  1071. else
  1072. {
  1073. internalExceptionAddr = backTrace[i + 1];
  1074. }
  1075. }
  1076. else if (backTrace[i] == (char*)Js::Throw::InternalError + displacements[i])
  1077. {
  1078. internalExceptionAddr = backTrace[i + 1];
  1079. }
  1080. }
  1081. fwprintf(stderr, crashStackEnd);
  1082. // Print fault injecting point stacks
  1083. auto record = InjectionFirstRecord;
  1084. while (record)
  1085. {
  1086. if (record->StackFrames)
  1087. {
  1088. fwprintf(stderr, injectionStackStart);
  1089. for (int i = 0; i < record->FrameCount; i++)
  1090. {
  1091. printFrame(backTrace[i]);
  1092. }
  1093. fwprintf(stderr, injectionStackEnd);
  1094. }
  1095. record = record->next;
  1096. }
  1097. // we called RaiseException() which always use RaiseException as exception address, restore the real exception addr
  1098. if (internalExceptionAddr != nullptr)
  1099. {
  1100. ep->ExceptionRecord->ExceptionAddress = internalExceptionAddr;
  1101. }
  1102. bool needDump = true;
  1103. uintptr_t ip = (uintptr_t)ep->ExceptionRecord->ExceptionAddress;
  1104. uintptr_t offset = 0;
  1105. // static to not use local stack space since stack space might be low at this point
  1106. THREAD_LOCAL static char16 modulePath[MAX_PATH + 1];
  1107. THREAD_LOCAL static WCHAR filename[MAX_PATH + 1];
  1108. HMODULE mod = nullptr;
  1109. GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<LPCTSTR>(ip), &mod);
  1110. offset = ip - (uintptr_t)mod;
  1111. auto& faultModule = modulePath;
  1112. GetModuleFileName(mod, faultModule, MAX_PATH);
  1113. fwprintf(stderr, _u("***FI: Exception: %08x, module: %s, offset: 0x%p\n"),
  1114. ep->ExceptionRecord->ExceptionCode, faultModule, (void*)offset);
  1115. //analyze duplication
  1116. uintptr_t savedOffset = 0;
  1117. auto& mainModule = modulePath;
  1118. PlatformAgnostic::SystemInfo::GetBinaryLocation(mainModule, MAX_PATH);
  1119. // multiple session of Fault Injection run shares the single crash offset recording file
  1120. _snwprintf_s(filename, _TRUNCATE, _u("%s.FICrashes.txt"), mainModule);
  1121. auto fp = _wfsopen(filename, _u("a+t"), _SH_DENYNO);
  1122. if (fp != nullptr)
  1123. {
  1124. HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fp));
  1125. OVERLAPPED overlapped;
  1126. memset(&overlapped, 0, sizeof(overlapped));
  1127. const int lockSize = 1024 * 64;
  1128. if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, lockSize, 0, &overlapped))
  1129. {
  1130. fwprintf(stderr, _u("LockFileEx(%ls) Failed when saving offset to file, gle=%8x\n"), filename, GetLastError());
  1131. fclose(fp);
  1132. }
  1133. else
  1134. { // file locked
  1135. char16 content[32] = { 0 };
  1136. while (fgetws(content, 31, fp))
  1137. {
  1138. savedOffset = HexStrToAddress(content);
  1139. if (offset == savedOffset)
  1140. {
  1141. // found duplicate so not creating dump
  1142. needDump = false;
  1143. }
  1144. }
  1145. if (needDump)
  1146. {
  1147. fwprintf(stderr, _u("This is new Exception\n"));
  1148. fwprintf(fp, _u("0x%p\n"), (void*)offset);
  1149. }
  1150. else
  1151. {
  1152. fwprintf(stderr, _u("This is not a new Exception\n"));
  1153. }
  1154. fflush(fp);
  1155. // save the hit count to a file, for bug prioritizing
  1156. _snwprintf_s(filename, _TRUNCATE, _u("%s.HitCount_%llx.txt"), mainModule, (long long)offset);
  1157. auto hcfp = _wfsopen(filename, _u("r+"), _SH_DENYNO);
  1158. if (!hcfp)
  1159. {
  1160. hcfp = _wfsopen(filename, _u("w+"), _SH_DENYNO);
  1161. }
  1162. if (hcfp)
  1163. {
  1164. auto count = 0;
  1165. fscanf_s(hcfp, "%d", &count);
  1166. count++;
  1167. fseek(hcfp, -ftell(hcfp), SEEK_CUR);
  1168. fwprintf(hcfp, _u("%d"), count);
  1169. fclose(hcfp);
  1170. }
  1171. fclose(fp);
  1172. UnlockFileEx(hFile, 0, lockSize, 0, &overlapped);
  1173. }
  1174. fflush(stderr);
  1175. }
  1176. if (globalFlags.FaultInjection == InstallExceptionHandlerOnly)
  1177. {
  1178. needDump = true;
  1179. }
  1180. // create dump for this crash
  1181. if (needDump)
  1182. {
  1183. THREAD_LOCAL static char16 dumpName[MAX_PATH + 1];
  1184. wcscpy_s(filename, globalFlags.Filename);
  1185. char16* jsFile = filename;
  1186. char16 *pch = jsFile;
  1187. // remove path and keep only alphabet and number to make a valid filename
  1188. while (*pch)
  1189. {
  1190. if (*pch == _u(':') || *pch == _u('\\'))
  1191. {
  1192. jsFile = pch + 1;
  1193. }
  1194. else if (!isalnum(*pch))
  1195. {
  1196. *pch = _u('_');
  1197. }
  1198. pch++;
  1199. }
  1200. // get dump file name
  1201. int suffix = 1;
  1202. const char16* fiType = _u("undefined");
  1203. if (globalFlags.FaultInjectionType != nullptr)
  1204. {
  1205. fiType = (LPCWSTR)globalFlags.FaultInjectionType;
  1206. }
  1207. while (true)
  1208. {
  1209. _snwprintf_s(dumpName, _TRUNCATE, _u("%s_%s_M%d_T%s_C%d_%llx_%llx_%d.dmp"),
  1210. mainModule, jsFile,
  1211. globalFlags.FaultInjection, fiType, globalFlags.FaultInjectionCount,
  1212. (ULONGLONG)offset, (ULONGLONG)ep->ExceptionRecord->ExceptionCode, suffix);
  1213. WIN32_FIND_DATAW data;
  1214. HANDLE hExist = FindFirstFile(dumpName, &data);
  1215. if (hExist == INVALID_HANDLE_VALUE)
  1216. {
  1217. FindClose(hExist);
  1218. break;
  1219. }
  1220. FindClose(hExist);
  1221. suffix++;
  1222. }
  1223. // writing the dump file
  1224. HANDLE hFile = CreateFile(dumpName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1225. if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE))
  1226. {
  1227. fwprintf(stderr, _u("CreateFile <%s> failed. gle=0x%08x\n"), dumpName, GetLastError());
  1228. }
  1229. else
  1230. {
  1231. MINIDUMP_EXCEPTION_INFORMATION mdei;
  1232. mdei.ThreadId = GetCurrentThreadId();
  1233. mdei.ExceptionPointers = ep;
  1234. mdei.ClientPointers = FALSE;
  1235. MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpNormal
  1236. | MiniDumpWithFullMemory
  1237. | MiniDumpWithPrivateReadWriteMemory
  1238. | MiniDumpWithIndirectlyReferencedMemory
  1239. | MiniDumpWithThreadInfo);
  1240. // removing extension for windbg module name style
  1241. auto& jscript9Path = modulePath;
  1242. wcsncpy_s(jscript9Path, AutoSystemInfo::Data.GetJscriptDllFileName(),
  1243. wcslen(AutoSystemInfo::Data.GetJscriptDllFileName()) - 4);
  1244. char16* jscript9Name = jscript9Path + wcslen(jscript9Path);
  1245. while (*(jscript9Name - 1) != _u('\\') && jscript9Name > jscript9Path)
  1246. {
  1247. jscript9Name--;
  1248. }
  1249. // This buffer will be written to a dump stream when creating the minidump file.
  1250. // It contains windbg debugging instructions on how to figure out the injected faults,
  1251. // And the message will be showing in windbg while loading the minidump.
  1252. // If you need to add more instructions please increase the buffer capacity accordingly
  1253. THREAD_LOCAL static char16 dbgTip[1024];
  1254. if (InjectionFirstRecord == nullptr)
  1255. {
  1256. wcsncpy_s(dbgTip,
  1257. _u("\n")
  1258. _u("************************************************************\n")
  1259. _u("* The dump is made by FaultInjection framework, however, the fault is not actually injected yet.\n")
  1260. _u("************************************************************\n"), _TRUNCATE);
  1261. }
  1262. else
  1263. {
  1264. _snwprintf_s(dbgTip, _TRUNCATE, _u("\n")
  1265. _u("************************************************************\n")
  1266. _u("* To find the Fault Injecting points run following command: \n")
  1267. _u("* !list -t %s!Js::FaultInjection::InjectionRecord.next -e -x \"dps @$extret @$extret+0x128\" poi(@@c++(&%s!Js::FaultInjection::Global.InjectionFirstRecord))\n")
  1268. _u("* To rebuild the stack (locals are available):\n")
  1269. _u("* .cxr @@C++(&%s!Js::FaultInjection::Global.InjectionFirstRecord->Context)\n")
  1270. _u("************************************************************\n"), jscript9Name, jscript9Name, jscript9Name);
  1271. }
  1272. MINIDUMP_USER_STREAM UserStreams[1];
  1273. UserStreams[0].Type = CommentStreamW;
  1274. UserStreams[0].Buffer = dbgTip;
  1275. UserStreams[0].BufferSize = (ULONG)wcslen(dbgTip)*sizeof(char16);
  1276. MINIDUMP_USER_STREAM_INFORMATION musi;
  1277. musi.UserStreamCount = 1;
  1278. musi.UserStreamArray = UserStreams;
  1279. BOOL rv = pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (ep != 0) ? &mdei : 0, &musi, 0);
  1280. if (rv)
  1281. {
  1282. fwprintf(stderr, _u("Minidump created: %s\n"), dumpName);
  1283. }
  1284. else
  1285. {
  1286. fwprintf(stderr, _u("MiniDumpWriteDump failed. gle=0x%08x\n"), GetLastError());
  1287. }
  1288. CloseHandle(hFile);
  1289. }
  1290. }
  1291. fflush(stderr);
  1292. #endif //_M_ARM and _M_ARM64
  1293. }
  1294. static volatile bool inExceptionHandler = false;
  1295. LONG WINAPI FaultInjection::FaultInjectionExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo)
  1296. {
  1297. if (inExceptionHandler)
  1298. {
  1299. // re-entering, this can happen if RemoveExceptionFilters() failed because of stack overflow
  1300. // Let it crash and the postmortem debugger can catch it.
  1301. DebugBreak();
  1302. }
  1303. inExceptionHandler = true;
  1304. RemoveExceptionFilters();
  1305. // for debugging, can't hit here in windbg because of using vectored exception handling
  1306. if (faultInjectionDebug)
  1307. {
  1308. DebugBreak();
  1309. }
  1310. if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) // hard stack overflow
  1311. {
  1312. DebugBreak(); // let the postmortem debugger to create the dump, make sure they are filing bug with same bucket
  1313. }
  1314. __try
  1315. {
  1316. // sometimes the OS is really low memory and can't commit page for stack expanding
  1317. // even stack is not deep yet
  1318. FaultInjection::Global.FaultInjectionAnalyzeException(ExceptionInfo);
  1319. }
  1320. __except (EXCEPTION_EXECUTE_HANDLER)
  1321. {
  1322. DebugBreak();
  1323. }
  1324. inExceptionHandler = false;
  1325. return EXCEPTION_EXECUTE_HANDLER;
  1326. }
  1327. } //namespace Js
  1328. #endif //FAULT_INJECTION