ConfigParser.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  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. #ifndef USING_PAL_STDLIB
  7. #include <io.h>
  8. #include <share.h>
  9. #include <fcntl.h>
  10. #include <strsafe.h>
  11. #endif
  12. #include "Memory/MemoryLogger.h"
  13. #include "Memory/ForcedMemoryConstraints.h"
  14. #include "Core/ICustomConfigFlags.h"
  15. #include "Core/CmdParser.h"
  16. #include "Core/ConfigParser.h"
  17. ConfigParser ConfigParser::s_moduleConfigParser(Js::Configuration::Global.flags);
  18. #ifdef ENABLE_TRACE
  19. class ArenaHost
  20. {
  21. AllocationPolicyManager m_allocationPolicyManager;
  22. PageAllocator m_pageAllocator;
  23. ArenaAllocator m_allocator;
  24. public:
  25. ArenaHost(__in_z const char16* arenaName) :
  26. m_allocationPolicyManager(/* needConcurrencySupport = */ true),
  27. m_pageAllocator(&m_allocationPolicyManager, Js::Configuration::Global.flags),
  28. m_allocator(arenaName, &m_pageAllocator, Js::Throw::OutOfMemory)
  29. {
  30. }
  31. ArenaAllocator* GetAllocator() { return &m_allocator; }
  32. };
  33. static ArenaHost s_arenaHost1(_u("For Output::Trace (1)"));
  34. static ArenaHost s_arenaHost2(_u("For Output::Trace (2)"));
  35. ArenaAllocator* GetOutputAllocator1()
  36. {
  37. return s_arenaHost1.GetAllocator();
  38. }
  39. ArenaAllocator* GetOutputAllocator2()
  40. {
  41. return s_arenaHost2.GetAllocator();
  42. }
  43. #endif
  44. void ConfigParser::ParseOnModuleLoad(CmdLineArgsParser& parser, HANDLE hmod)
  45. {
  46. Assert(!s_moduleConfigParser.HasReadConfig());
  47. s_moduleConfigParser.ParseRegistry(parser);
  48. s_moduleConfigParser.ParseConfig(hmod, parser);
  49. s_moduleConfigParser.ProcessConfiguration(hmod);
  50. // 'parser' destructor post-processes some configuration
  51. }
  52. void ConfigParser::ParseRegistry(CmdLineArgsParser &parser)
  53. {
  54. #ifdef _WIN32
  55. HKEY hk;
  56. bool includeUserHive = true;
  57. if (NOERROR == RegOpenKeyExW(HKEY_LOCAL_MACHINE, JsUtil::ExternalApi::GetFeatureKeyName(), 0, KEY_READ, &hk))
  58. {
  59. DWORD dwValue;
  60. DWORD dwSize = sizeof(dwValue);
  61. ParseRegistryKey(hk, parser);
  62. // HKLM can prevent user config from being read.
  63. if (NOERROR == RegGetValueW(hk, nullptr, _u("AllowUserConfig"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize) && dwValue == 0)
  64. {
  65. includeUserHive = false;
  66. }
  67. RegCloseKey(hk);
  68. }
  69. if (includeUserHive && NOERROR == RegOpenKeyExW(HKEY_CURRENT_USER, JsUtil::ExternalApi::GetFeatureKeyName(), 0, KEY_READ, &hk))
  70. {
  71. ParseRegistryKey(hk, parser);
  72. RegCloseKey(hk);
  73. }
  74. #endif // _WIN32
  75. }
  76. void ConfigParser::ParseRegistryKey(HKEY hk, CmdLineArgsParser &parser)
  77. {
  78. #ifdef _WIN32
  79. DWORD dwSize;
  80. DWORD dwValue;
  81. #if ENABLE_DEBUG_CONFIG_OPTIONS
  82. char16 regBuffer[MaxRegSize];
  83. dwSize = sizeof(regBuffer);
  84. if (NOERROR == RegGetValueW(hk, nullptr, _u("JScript9"), RRF_RT_REG_SZ, nullptr, (LPBYTE)regBuffer, &dwSize))
  85. {
  86. LPWSTR regValue = regBuffer, nextValue = nullptr;
  87. regValue = wcstok_s(regBuffer, _u(" "), &nextValue);
  88. while (regValue != nullptr)
  89. {
  90. int err = 0;
  91. if ((err = parser.Parse(regValue)) != 0)
  92. {
  93. break;
  94. }
  95. regValue = wcstok_s(nullptr, _u(" "), &nextValue);
  96. }
  97. }
  98. #endif
  99. // MemSpect - This setting controls whether MemSpect instrumentation is enabled.
  100. // The value is treated as a bit field with the following bits:
  101. // 0x01 - Track Arena memory
  102. // 0x02 - Track Recycler memory
  103. // 0x04 - Track Page allocations
  104. dwValue = 0;
  105. dwSize = sizeof(dwValue);
  106. if (NOERROR == ::RegGetValueW(hk, nullptr, _u("MemSpect"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  107. {
  108. if (dwValue & 0x01)
  109. {
  110. ArenaMemoryTracking::Activate();
  111. }
  112. if (dwValue & 0x02)
  113. {
  114. RecyclerMemoryTracking::Activate();
  115. }
  116. if (dwValue & 0x04)
  117. {
  118. PageTracking::Activate();
  119. }
  120. }
  121. // JScriptJIT - This setting controls the JIT/interpretation of Jscript code.
  122. // The legal values are as follows:
  123. // 1- Force JIT code to be generated for everything.
  124. // 2- Force interpretation without profiling (turn off JIT)
  125. // 3- Default
  126. // 4- Interpreter, simple JIT, and full JIT run a predetermined number of times. Requires >= 3 calls to functions.
  127. // 5- Interpreter, simple JIT, and full JIT run a predetermined number of times. Requires >= 4 calls to functions.
  128. // 6- Force interpretation with profiling
  129. //
  130. // This reg key is present in released builds. The QA team's tests use these switches to
  131. // get reliable JIT coverage in servicing runs done by IE/Windows. Because this reg key is
  132. // released, the number of possible values is limited to reduce surface area.
  133. dwValue = 0;
  134. dwSize = sizeof(dwValue);
  135. if (NOERROR == RegGetValueW(hk, nullptr, _u("JScriptJIT"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  136. {
  137. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  138. switch (dwValue)
  139. {
  140. case 1:
  141. configFlags.Enable(Js::ForceNativeFlag);
  142. configFlags.ForceNative = true;
  143. break;
  144. case 6:
  145. configFlags.Enable(Js::ForceDynamicProfileFlag);
  146. configFlags.ForceDynamicProfile = true;
  147. // fall through
  148. case 2:
  149. configFlags.Enable(Js::NoNativeFlag);
  150. configFlags.NoNative = true;
  151. break;
  152. case 3:
  153. break;
  154. case 4:
  155. configFlags.Enable(Js::AutoProfilingInterpreter0LimitFlag);
  156. configFlags.Enable(Js::ProfilingInterpreter0LimitFlag);
  157. configFlags.Enable(Js::AutoProfilingInterpreter1LimitFlag);
  158. configFlags.Enable(Js::SimpleJitLimitFlag);
  159. configFlags.Enable(Js::ProfilingInterpreter1LimitFlag);
  160. configFlags.Enable(Js::EnforceExecutionModeLimitsFlag);
  161. configFlags.AutoProfilingInterpreter0Limit = 0;
  162. configFlags.AutoProfilingInterpreter1Limit = 0;
  163. if (
  164. #if ENABLE_DEBUG_CONFIG_OPTIONS
  165. configFlags.NewSimpleJit
  166. #else
  167. DEFAULT_CONFIG_NewSimpleJit
  168. #endif
  169. )
  170. {
  171. configFlags.ProfilingInterpreter0Limit = 0;
  172. configFlags.SimpleJitLimit = 0;
  173. configFlags.ProfilingInterpreter1Limit = 2;
  174. }
  175. else
  176. {
  177. configFlags.ProfilingInterpreter0Limit = 1;
  178. configFlags.SimpleJitLimit = 1;
  179. configFlags.ProfilingInterpreter1Limit = 0;
  180. }
  181. configFlags.EnforceExecutionModeLimits = true;
  182. break;
  183. case 5:
  184. configFlags.Enable(Js::AutoProfilingInterpreter0LimitFlag);
  185. configFlags.Enable(Js::ProfilingInterpreter0LimitFlag);
  186. configFlags.Enable(Js::AutoProfilingInterpreter1LimitFlag);
  187. configFlags.Enable(Js::SimpleJitLimitFlag);
  188. configFlags.Enable(Js::ProfilingInterpreter1LimitFlag);
  189. configFlags.Enable(Js::EnforceExecutionModeLimitsFlag);
  190. configFlags.AutoProfilingInterpreter0Limit = 0;
  191. configFlags.ProfilingInterpreter0Limit = 0;
  192. configFlags.AutoProfilingInterpreter1Limit = 1;
  193. if (
  194. #if ENABLE_DEBUG_CONFIG_OPTIONS
  195. configFlags.NewSimpleJit
  196. #else
  197. DEFAULT_CONFIG_NewSimpleJit
  198. #endif
  199. )
  200. {
  201. configFlags.SimpleJitLimit = 0;
  202. configFlags.ProfilingInterpreter1Limit = 2;
  203. }
  204. else
  205. {
  206. configFlags.SimpleJitLimit = 2;
  207. configFlags.ProfilingInterpreter1Limit = 0;
  208. }
  209. configFlags.EnforceExecutionModeLimits = true;
  210. break;
  211. }
  212. }
  213. // ES6 feature control
  214. // This setting allows enabling\disabling es6 features
  215. // 0 - Enable ES6 flag - Also default behavior
  216. // 1 - Disable ES6 flag
  217. dwValue = 0;
  218. dwSize = sizeof(dwValue);
  219. if (NOERROR == RegGetValueW(hk, nullptr, _u("DisableES6"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  220. {
  221. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  222. if (dwValue == 1)
  223. {
  224. configFlags.Enable(Js::ES6Flag);
  225. configFlags.SetAsBoolean(Js::ES6Flag, false);
  226. }
  227. }
  228. // WebAssembly experimental feature control
  229. // 1 - Enable WebAssembly Experimental features
  230. dwValue = 0;
  231. dwSize = sizeof(dwValue);
  232. if (NOERROR == RegGetValueW(hk, nullptr, _u("EnableWasmExperimental"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  233. {
  234. if (dwValue == 1)
  235. {
  236. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  237. configFlags.Enable(Js::WasmExperimentalFlag);
  238. configFlags.SetAsBoolean(Js::WasmExperimentalFlag, true);
  239. }
  240. }
  241. // BgParse feature control
  242. // 0 - Disable BgParse
  243. // 1 - Enable BgParse
  244. dwValue = 0;
  245. dwSize = sizeof(dwValue);
  246. if (NOERROR == RegGetValueW(hk, nullptr, _u("EnableBgParse"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  247. {
  248. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  249. configFlags.Enable(Js::BgParseFlag);
  250. if (dwValue == 0)
  251. {
  252. configFlags.SetAsBoolean(Js::BgParseFlag, false);
  253. }
  254. else if (dwValue == 1)
  255. {
  256. configFlags.SetAsBoolean(Js::BgParseFlag, true);
  257. }
  258. #if ENABLE_DEBUG_CONFIG_OPTIONS
  259. Output::Print(_u("BgParse controlled by registry: %u\n"), dwValue);
  260. #endif
  261. }
  262. // Spectre mitigation feature control
  263. // This setting allows enabling\disabling spectre mitigations
  264. // 0 - Disable Spectre mitigations
  265. // 1 - Enable Spectre mitigations - Also default behavior
  266. dwValue = 0;
  267. dwSize = sizeof(dwValue);
  268. if (NOERROR == RegGetValueW(hk, nullptr, _u("MitigateSpectre"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  269. {
  270. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  271. configFlags.Enable(Js::MitigateSpectreFlag);
  272. if (dwValue == 0)
  273. {
  274. configFlags.SetAsBoolean(Js::MitigateSpectreFlag, false);
  275. }
  276. else if (dwValue == 1)
  277. {
  278. configFlags.SetAsBoolean(Js::MitigateSpectreFlag, true);
  279. }
  280. }
  281. #ifdef ENABLE_BASIC_TELEMETRY
  282. SetConfigStringFromRegistry(hk, _u("Telemetry"), _u("Discriminator1"), Js::Configuration::Global.flags.TelemetryDiscriminator1);
  283. SetConfigStringFromRegistry(hk, _u("Telemetry"), _u("Discriminator2"), Js::Configuration::Global.flags.TelemetryDiscriminator2);
  284. SetConfigStringFromRegistry(hk, _u("Telemetry"), _u("RunType"), Js::Configuration::Global.flags.TelemetryRunType);
  285. #endif
  286. #endif // _WIN32
  287. }
  288. #ifdef _WIN32
  289. void ConfigParser::SetConfigStringFromRegistry(_In_ HKEY hk, _In_z_ const char16* subKeyName, _In_z_ const char16* valName, _Inout_ Js::String& str)
  290. {
  291. const char16* regValue = nullptr;
  292. DWORD len = 0;
  293. ReadRegistryString(hk, subKeyName, valName, &regValue, &len);
  294. if (regValue != nullptr)
  295. {
  296. str = regValue;
  297. // Js::String makes a copy of buffer so delete here
  298. NoCheckHeapDeleteArray(len, regValue);
  299. }
  300. }
  301. /**
  302. * Read a string from the registry. Will return nullptr if string registry entry
  303. * doesn't exist, or if we can't allocate memory.
  304. * Will allocate a char16* buffer on the heap. Caller is responsible for freeing.
  305. */
  306. void ConfigParser::ReadRegistryString(_In_ HKEY hk, _In_z_ const char16* subKeyName, _In_z_ const char16* valName, _Outptr_result_maybenull_z_ const char16** sz, _Out_ DWORD* length)
  307. {
  308. DWORD bufLength = 0;
  309. *length = 0;
  310. *sz = nullptr;
  311. // first read to get size of string
  312. DWORD result = RegGetValueW(hk, subKeyName, valName, RRF_RT_REG_SZ, nullptr, nullptr, &bufLength);
  313. if (NOERROR == result)
  314. {
  315. if (bufLength > 0)
  316. {
  317. byte* buf = NoCheckHeapNewArrayZ(byte, bufLength);
  318. if (buf != nullptr)
  319. {
  320. result = RegGetValueW(hk, subKeyName, valName, RRF_RT_REG_SZ, nullptr, buf, &bufLength);
  321. if (NOERROR == result)
  322. {
  323. // if successful, bufLength won't include null terminator so add 1
  324. *length = (bufLength / sizeof(char16)) + 1;
  325. *sz = reinterpret_cast<char16*>(buf);
  326. }
  327. else
  328. {
  329. NoCheckHeapDeleteArray(bufLength, buf);
  330. }
  331. }
  332. }
  333. }
  334. }
  335. #endif // _WIN32
  336. void ConfigParser::ParseConfig(HANDLE hmod, CmdLineArgsParser &parser, const char16* strCustomConfigFile)
  337. {
  338. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && CONFIG_PARSE_CONFIG_FILE
  339. Assert(!_hasReadConfig || strCustomConfigFile != nullptr);
  340. _hasReadConfig = true;
  341. const char16* configFileName = strCustomConfigFile;
  342. const char16* configFileExt = _u(""); /* in the custom config case,
  343. ext is expected to be passed
  344. in as part of the filename */
  345. if (configFileName == nullptr)
  346. {
  347. configFileName = _configFileName;
  348. configFileExt = _u(".config");
  349. }
  350. int err = 0;
  351. char16 modulename[_MAX_PATH];
  352. char16 filename[_MAX_PATH];
  353. GetModuleFileName((HMODULE)hmod, modulename, _MAX_PATH);
  354. char16 drive[_MAX_DRIVE];
  355. char16 dir[_MAX_DIR];
  356. _wsplitpath_s(modulename, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
  357. _wmakepath_s(filename, drive, dir, configFileName, configFileExt);
  358. FILE* configFile;
  359. #ifdef _WIN32
  360. if (_wfopen_s(&configFile, filename, _u("r, ccs=UNICODE")) != 0 || configFile == nullptr)
  361. {
  362. WCHAR configFileFullName[MAX_PATH];
  363. StringCchPrintf(configFileFullName, MAX_PATH, _u("%s%s"), configFileName, configFileExt);
  364. // try the one in the current working directory (Desktop)
  365. if (_wfullpath(filename, configFileFullName, _MAX_PATH) == nullptr)
  366. {
  367. return;
  368. }
  369. if (_wfopen_s(&configFile, filename, _u("r, ccs=UNICODE")) != 0 || configFile == nullptr)
  370. {
  371. return;
  372. }
  373. }
  374. #else
  375. // Two-pathed for a couple reasons
  376. // 1. PAL doesn't like the ccs option passed in.
  377. // 2. _wfullpath is not implemented in the PAL.
  378. // Instead, on xplat, we'll check the HOME directory to see if there is
  379. // a config file there that we can use
  380. if (_wfopen_s(&configFile, filename, _u("r")) != 0 || configFile == nullptr)
  381. {
  382. WCHAR homeDir[MAX_PATH];
  383. if (GetEnvironmentVariable(_u("HOME"), homeDir, MAX_PATH) == 0)
  384. {
  385. return;
  386. }
  387. WCHAR configFileFullName[MAX_PATH];
  388. StringCchPrintf(configFileFullName, MAX_PATH, _u("%s/%s%s"), homeDir, configFileName, configFileExt);
  389. if (_wfopen_s(&configFile, configFileFullName, _u("r")) != 0 || configFile == nullptr)
  390. {
  391. return;
  392. }
  393. }
  394. #endif
  395. char16 configBuffer[MaxTokenSize];
  396. int index = 0;
  397. #ifdef _WIN32
  398. #define ReadChar(file) fgetwc(file)
  399. #define UnreadChar(c, file) ungetwc(c, file)
  400. #define CharType wint_t
  401. #define EndChar WEOF
  402. #else
  403. #define ReadChar(file) fgetc(file)
  404. #define UnreadChar(c, file) ungetc(c, file)
  405. #define CharType int
  406. #define EndChar EOF
  407. #endif
  408. // We don't expect the token to overflow- if it does
  409. // the simplest thing to do would be to ignore the
  410. // read tokens
  411. // We could use _fwscanf_s here but the function
  412. // isn't implemented in the PAL and we'd have to deal with
  413. // wchar => char16 impedance mismatch.
  414. while (index < MaxTokenSize)
  415. {
  416. CharType curChar = ReadChar(configFile);
  417. if (this->_flags.rawInputFromConfigFileIndex < sizeof(this->_flags.rawInputFromConfigFile) / sizeof(this->_flags.rawInputFromConfigFile[0]))
  418. {
  419. this->_flags.rawInputFromConfigFile[this->_flags.rawInputFromConfigFileIndex++] = curChar;
  420. }
  421. if (curChar == EndChar || isspace(curChar) || curChar == 0)
  422. {
  423. configBuffer[index] = 0;
  424. // Parse only if there's something in configBuffer
  425. if (index > 0 && (err = parser.Parse(configBuffer)) != 0)
  426. {
  427. break;
  428. }
  429. while(curChar != EndChar && (isspace(curChar) || curChar == 0))
  430. {
  431. curChar = ReadChar(configFile);
  432. }
  433. if (curChar == EndChar)
  434. {
  435. break;
  436. }
  437. else
  438. {
  439. UnreadChar(curChar, configFile);
  440. }
  441. index = 0;
  442. }
  443. else
  444. {
  445. // The expectation is that non-ANSI characters
  446. // are not used in the config- otherwise it will
  447. // be interpreted incorrectly here
  448. configBuffer[index++] = (char16) curChar;
  449. }
  450. }
  451. #undef ReadChar
  452. #undef UnreadChar
  453. #undef CharType
  454. #undef EndChar
  455. fclose(configFile);
  456. if (err !=0)
  457. {
  458. return;
  459. }
  460. #endif
  461. }
  462. void ConfigParser::ProcessConfiguration(HANDLE hmod)
  463. {
  464. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  465. bool hasOutput = false;
  466. char16 modulename[_MAX_PATH];
  467. GetModuleFileName((HMODULE)hmod, modulename, _MAX_PATH);
  468. // Win32 specific console creation code
  469. // xplat-todo: Consider having this mechanism available on other
  470. // platforms
  471. // Not a pressing need since ChakraCore runs only in consoles by
  472. // default so we don't need to allocate a second console for this
  473. #if CONFIG_CONSOLE_AVAILABLE
  474. if (Js::Configuration::Global.flags.Console)
  475. {
  476. int fd;
  477. FILE *fp;
  478. // fail usually means there is an existing console. We don't really care.
  479. if (AllocConsole())
  480. {
  481. fd = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), O_TEXT);
  482. fp = _wfdopen(fd, _u("w"));
  483. if (fp != nullptr)
  484. {
  485. *stdout = *fp;
  486. setvbuf(stdout, nullptr, _IONBF, 0);
  487. fd = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), O_TEXT);
  488. fp = _wfdopen(fd, _u("w"));
  489. if (fp != nullptr)
  490. {
  491. *stderr = *fp;
  492. setvbuf(stderr, nullptr, _IONBF, 0);
  493. char16 buffer[_MAX_PATH + 70];
  494. if (ConfigParserAPI::FillConsoleTitle(buffer, _MAX_PATH + 20, modulename))
  495. {
  496. SetConsoleTitle(buffer);
  497. }
  498. hasOutput = true;
  499. }
  500. }
  501. }
  502. }
  503. #endif
  504. if (Js::Configuration::Global.flags.IsEnabled(Js::OutputFileFlag)
  505. && Js::Configuration::Global.flags.OutputFile != nullptr)
  506. {
  507. SetOutputFile(Js::Configuration::Global.flags.OutputFile, Js::Configuration::Global.flags.OutputFileOpenMode);
  508. hasOutput = true;
  509. }
  510. if (Js::Configuration::Global.flags.DebugWindow)
  511. {
  512. Output::UseDebuggerWindow();
  513. hasOutput = true;
  514. }
  515. #ifdef ENABLE_TRACE
  516. if (CONFIG_FLAG(InMemoryTrace))
  517. {
  518. Output::SetInMemoryLogger(
  519. Js::MemoryLogger::Create(::GetOutputAllocator1(),
  520. CONFIG_FLAG(InMemoryTraceBufferSize) * 3)); // With stack each trace is 3 entries (header, msg, stack).
  521. hasOutput = true;
  522. }
  523. #ifdef STACK_BACK_TRACE
  524. if (CONFIG_FLAG(TraceWithStack))
  525. {
  526. Output::SetStackTraceHelper(Js::StackTraceHelper::Create(::GetOutputAllocator2()));
  527. }
  528. #endif // STACK_BACK_TRACE
  529. #endif // ENABLE_TRACE
  530. if (hasOutput)
  531. {
  532. ConfigParserAPI::DisplayInitialOutput(modulename);
  533. Output::Print(_u("\n"));
  534. Js::Configuration::Global.flags.VerboseDump();
  535. Output::Flush();
  536. }
  537. if (Js::Configuration::Global.flags.ForceSerialized)
  538. {
  539. // Can't generate or execute byte code under forced serialize
  540. Js::Configuration::Global.flags.GenerateByteCodeBufferReturnsCantGenerate = true;
  541. Js::Configuration::Global.flags.ExecuteByteCodeBufferReturnsInvalidByteCode = true;
  542. }
  543. ForcedMemoryConstraint::Apply();
  544. #endif
  545. #ifdef MEMSPECT_TRACKING
  546. bool all = false;
  547. if (Js::Configuration::Global.flags.Memspect.IsEnabled(Js::AllPhase))
  548. {
  549. all = true;
  550. }
  551. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::RecyclerPhase))
  552. {
  553. RecyclerMemoryTracking::Activate();
  554. }
  555. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::PageAllocatorPhase))
  556. {
  557. PageTracking::Activate();
  558. }
  559. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::ArenaPhase))
  560. {
  561. ArenaMemoryTracking::Activate();
  562. }
  563. #endif
  564. }
  565. HRESULT ConfigParser::SetOutputFile(const WCHAR* outputFile, const WCHAR* openMode)
  566. {
  567. // If present, replace the {PID} token with the process ID
  568. const WCHAR* pidStr = nullptr;
  569. WCHAR buffer[_MAX_PATH];
  570. if ((pidStr = wcsstr(outputFile, _u("{PID}"))) != nullptr)
  571. {
  572. size_t pidStartPosition = pidStr - outputFile;
  573. WCHAR* pDest = buffer;
  574. size_t bufferLen = _MAX_PATH;
  575. // Copy the filename before the {PID} token
  576. wcsncpy_s(pDest, bufferLen, outputFile, pidStartPosition);
  577. pDest += pidStartPosition;
  578. bufferLen = bufferLen - pidStartPosition;
  579. // Copy the PID
  580. _itow_s(GetCurrentProcessId(), pDest, /*bufferSize=*/_MAX_PATH - pidStartPosition, /*radix=*/10);
  581. #pragma prefast(suppress: 26014, "ultow string length is smaller than 256")
  582. pDest += wcslen(pDest);
  583. bufferLen = bufferLen - wcslen(pDest);
  584. // Copy the rest of the string.
  585. #pragma prefast(suppress: 26014, "Overwriting pDset's null terminator is intentional since the string being copied is null terminated")
  586. wcscpy_s(pDest, bufferLen, outputFile + pidStartPosition + /*length of {PID}*/ 5);
  587. outputFile = buffer;
  588. }
  589. char16 fileName[_MAX_PATH];
  590. char16 moduleName[_MAX_PATH];
  591. PlatformAgnostic::SystemInfo::GetBinaryLocation(moduleName, _MAX_PATH);
  592. _wsplitpath_s(moduleName, nullptr, 0, nullptr, 0, fileName, _MAX_PATH, nullptr, 0);
  593. if (_wcsicmp(fileName, _u("WWAHost")) == 0 ||
  594. _wcsicmp(fileName, _u("ByteCodeGenerator")) == 0 ||
  595. _wcsicmp(fileName, _u("spartan")) == 0 ||
  596. _wcsicmp(fileName, _u("spartan_edge")) == 0 ||
  597. _wcsnicmp(fileName, _u("MicrosoftEdge"), wcslen(_u("MicrosoftEdge"))) == 0)
  598. {
  599. // we need to output to %temp% directory in wwa. we don't have permission otherwise.
  600. if (GetEnvironmentVariable(_u("temp"), fileName, _MAX_PATH) != 0)
  601. {
  602. wcscat_s(fileName, _MAX_PATH, _u("\\"));
  603. const char16 * fileNameOnly = wcsrchr(outputFile, _u('\\'));
  604. // if outputFile is full path we just need filename, discard the path
  605. wcscat_s(fileName, _MAX_PATH, fileNameOnly == nullptr ? outputFile : fileNameOnly);
  606. }
  607. else
  608. {
  609. AssertMsg(FALSE, "Get temp environment failed");
  610. }
  611. outputFile = fileName;
  612. }
  613. FILE *fp;
  614. if ((fp = _wfsopen(outputFile, openMode, _SH_DENYWR)) != nullptr)
  615. {
  616. Output::SetOutputFile(fp);
  617. return S_OK;
  618. }
  619. AssertMsg(false, "Could not open file for logging output.");
  620. return E_FAIL;
  621. }