ConfigParser.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  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. // EnumerationCompat
  214. // This setting allows disabling a couple of changes to enumeration:
  215. // - A change that causes deleted property indexes to be reused for new properties, thereby changing the order in which
  216. // properties are enumerated
  217. // - A change that creates a true snapshot of the type just before enumeration, and enumerating only those properties. A
  218. // property that was deleted before enumeration and is added back during enumeration will not be enumerated.
  219. // Values:
  220. // 0 - Default
  221. // 1 - Compatibility mode for enumeration order (disable changes described above)
  222. // This FCK does not apply to WWAs. WWAs should use the RC compat mode to disable these changes.
  223. dwValue = 0;
  224. dwSize = sizeof(dwValue);
  225. if (NOERROR == RegGetValueW(hk, nullptr, _u("EnumerationCompat"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  226. {
  227. if (dwValue == 1)
  228. {
  229. Js::Configuration::Global.flags.EnumerationCompat = true;
  230. }
  231. }
  232. #ifdef ENABLE_PROJECTION
  233. // FailFastIfDisconnectedDelegate
  234. // This setting allows enabling fail fast if the delegate invoked is disconnected
  235. // 0 - Default return the error RPC_E_DISCONNECTED if disconnected delegate is invoked
  236. // 1 - Fail fast if disconnected delegate
  237. dwValue = 0;
  238. dwSize = sizeof(dwValue);
  239. if (NOERROR == RegGetValueW(hk, nullptr, _u("FailFastIfDisconnectedDelegate"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  240. {
  241. if (dwValue == 1)
  242. {
  243. Js::Configuration::Global.flags.FailFastIfDisconnectedDelegate = true;
  244. }
  245. }
  246. #endif
  247. // ES6 feature control
  248. // This setting allows enabling\disabling es6 features
  249. // 0 - Enable ES6 flag - Also default behavior
  250. // 1 - Disable ES6 flag
  251. dwValue = 0;
  252. dwSize = sizeof(dwValue);
  253. if (NOERROR == RegGetValueW(hk, nullptr, _u("DisableES6"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  254. {
  255. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  256. if (dwValue == 1)
  257. {
  258. configFlags.Enable(Js::ES6Flag);
  259. configFlags.SetAsBoolean(Js::ES6Flag, false);
  260. }
  261. }
  262. // WebAssembly experimental feature control
  263. // 1 - Enable WebAssembly Experimental features
  264. dwValue = 0;
  265. dwSize = sizeof(dwValue);
  266. if (NOERROR == RegGetValueW(hk, nullptr, _u("EnableWasmExperimental"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  267. {
  268. if (dwValue == 1)
  269. {
  270. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  271. configFlags.Enable(Js::WasmExperimentalFlag);
  272. configFlags.SetAsBoolean(Js::WasmExperimentalFlag, true);
  273. }
  274. }
  275. // BgParse feature control
  276. // 0 - Disable BgParse
  277. // 1 - Enable BgParse
  278. dwValue = 0;
  279. dwSize = sizeof(dwValue);
  280. if (NOERROR == RegGetValueW(hk, nullptr, _u("EnableBgParse"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  281. {
  282. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  283. configFlags.Enable(Js::BgParseFlag);
  284. if (dwValue == 0)
  285. {
  286. configFlags.SetAsBoolean(Js::BgParseFlag, false);
  287. }
  288. else if (dwValue == 1)
  289. {
  290. configFlags.SetAsBoolean(Js::BgParseFlag, true);
  291. }
  292. #if ENABLE_DEBUG_CONFIG_OPTIONS
  293. Output::Print(_u("BgParse controlled by registry: %u\n"), dwValue);
  294. #endif
  295. }
  296. // Spectre mitigation feature control
  297. // This setting allows enabling\disabling spectre mitigations
  298. // 0 - Disable Spectre mitigations
  299. // 1 - Enable Spectre mitigations - Also default behavior
  300. dwValue = 0;
  301. dwSize = sizeof(dwValue);
  302. if (NOERROR == RegGetValueW(hk, nullptr, _u("MitigateSpectre"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  303. {
  304. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  305. configFlags.Enable(Js::MitigateSpectreFlag);
  306. if (dwValue == 0)
  307. {
  308. configFlags.SetAsBoolean(Js::MitigateSpectreFlag, false);
  309. }
  310. else if (dwValue == 1)
  311. {
  312. configFlags.SetAsBoolean(Js::MitigateSpectreFlag, true);
  313. }
  314. }
  315. #ifdef ENABLE_BASIC_TELEMETRY
  316. SetConfigStringFromRegistry(hk, _u("Telemetry"), _u("Discriminator1"), Js::Configuration::Global.flags.TelemetryDiscriminator1);
  317. SetConfigStringFromRegistry(hk, _u("Telemetry"), _u("Discriminator2"), Js::Configuration::Global.flags.TelemetryDiscriminator2);
  318. SetConfigStringFromRegistry(hk, _u("Telemetry"), _u("RunType"), Js::Configuration::Global.flags.TelemetryRunType);
  319. #endif
  320. #endif // _WIN32
  321. }
  322. #ifdef _WIN32
  323. void ConfigParser::SetConfigStringFromRegistry(_In_ HKEY hk, _In_ const char16* subKeyName, _In_ const char16* valName, _Inout_ Js::String& str)
  324. {
  325. const char16* regValue = nullptr;
  326. DWORD len = 0;
  327. ReadRegistryString(hk, subKeyName, valName, &regValue, &len);
  328. if (regValue != nullptr)
  329. {
  330. str = regValue;
  331. // Js::String makes a copy of buffer so delete here
  332. NoCheckHeapDeleteArray(len, regValue);
  333. }
  334. }
  335. /**
  336. * Read a string from the registry. Will return nullptr if string registry entry
  337. * doesn't exist, or if we can't allocate memory.
  338. * Will allocate a char16* buffer on the heap. Caller is responsible for freeing.
  339. */
  340. void ConfigParser::ReadRegistryString(_In_ HKEY hk, _In_ const char16* subKeyName, _In_ const char16* valName, _Out_ const char16** sz, _Out_ DWORD* length)
  341. {
  342. DWORD bufLength = 0;
  343. *length = 0;
  344. *sz = nullptr;
  345. // first read to get size of string
  346. DWORD result = RegGetValueW(hk, subKeyName, valName, RRF_RT_REG_SZ, nullptr, nullptr, &bufLength);
  347. if (NOERROR == result)
  348. {
  349. if (bufLength > 0)
  350. {
  351. byte* buf = NoCheckHeapNewArrayZ(byte, bufLength);
  352. if (buf != nullptr)
  353. {
  354. result = RegGetValueW(hk, subKeyName, valName, RRF_RT_REG_SZ, nullptr, buf, &bufLength);
  355. if (NOERROR == result)
  356. {
  357. // if successful, bufLength won't include null terminator so add 1
  358. *length = (bufLength / sizeof(char16)) + 1;
  359. *sz = reinterpret_cast<char16*>(buf);
  360. }
  361. else
  362. {
  363. NoCheckHeapDeleteArray(bufLength, buf);
  364. }
  365. }
  366. }
  367. }
  368. }
  369. #endif // _WIN32
  370. void ConfigParser::ParseConfig(HANDLE hmod, CmdLineArgsParser &parser, const char16* strCustomConfigFile)
  371. {
  372. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && CONFIG_PARSE_CONFIG_FILE
  373. Assert(!_hasReadConfig || strCustomConfigFile != nullptr);
  374. _hasReadConfig = true;
  375. const char16* configFileName = strCustomConfigFile;
  376. const char16* configFileExt = _u(""); /* in the custom config case,
  377. ext is expected to be passed
  378. in as part of the filename */
  379. if (configFileName == nullptr)
  380. {
  381. configFileName = _configFileName;
  382. configFileExt = _u(".config");
  383. }
  384. int err = 0;
  385. char16 modulename[_MAX_PATH];
  386. char16 filename[_MAX_PATH];
  387. GetModuleFileName((HMODULE)hmod, modulename, _MAX_PATH);
  388. char16 drive[_MAX_DRIVE];
  389. char16 dir[_MAX_DIR];
  390. _wsplitpath_s(modulename, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
  391. _wmakepath_s(filename, drive, dir, configFileName, configFileExt);
  392. FILE* configFile;
  393. #ifdef _WIN32
  394. if (_wfopen_s(&configFile, filename, _u("r, ccs=UNICODE")) != 0 || configFile == nullptr)
  395. {
  396. WCHAR configFileFullName[MAX_PATH];
  397. StringCchPrintf(configFileFullName, MAX_PATH, _u("%s%s"), configFileName, configFileExt);
  398. // try the one in the current working directory (Desktop)
  399. if (_wfullpath(filename, configFileFullName, _MAX_PATH) == nullptr)
  400. {
  401. return;
  402. }
  403. if (_wfopen_s(&configFile, filename, _u("r, ccs=UNICODE")) != 0 || configFile == nullptr)
  404. {
  405. return;
  406. }
  407. }
  408. #else
  409. // Two-pathed for a couple reasons
  410. // 1. PAL doesn't like the ccs option passed in.
  411. // 2. _wfullpath is not implemented in the PAL.
  412. // Instead, on xplat, we'll check the HOME directory to see if there is
  413. // a config file there that we can use
  414. if (_wfopen_s(&configFile, filename, _u("r")) != 0 || configFile == nullptr)
  415. {
  416. WCHAR homeDir[MAX_PATH];
  417. if (GetEnvironmentVariable(_u("HOME"), homeDir, MAX_PATH) == 0)
  418. {
  419. return;
  420. }
  421. WCHAR configFileFullName[MAX_PATH];
  422. StringCchPrintf(configFileFullName, MAX_PATH, _u("%s/%s%s"), homeDir, configFileName, configFileExt);
  423. if (_wfopen_s(&configFile, configFileFullName, _u("r")) != 0 || configFile == nullptr)
  424. {
  425. return;
  426. }
  427. }
  428. #endif
  429. char16 configBuffer[MaxTokenSize];
  430. int index = 0;
  431. #ifdef _WIN32
  432. #define ReadChar(file) fgetwc(file)
  433. #define UnreadChar(c, file) ungetwc(c, file)
  434. #define CharType wint_t
  435. #define EndChar WEOF
  436. #else
  437. #define ReadChar(file) fgetc(file)
  438. #define UnreadChar(c, file) ungetc(c, file)
  439. #define CharType int
  440. #define EndChar EOF
  441. #endif
  442. // We don't expect the token to overflow- if it does
  443. // the simplest thing to do would be to ignore the
  444. // read tokens
  445. // We could use _fwscanf_s here but the function
  446. // isn't implemented in the PAL and we'd have to deal with
  447. // wchar => char16 impedance mismatch.
  448. while (index < MaxTokenSize)
  449. {
  450. CharType curChar = ReadChar(configFile);
  451. if (this->_flags.rawInputFromConfigFileIndex < sizeof(this->_flags.rawInputFromConfigFile) / sizeof(this->_flags.rawInputFromConfigFile[0]))
  452. {
  453. this->_flags.rawInputFromConfigFile[this->_flags.rawInputFromConfigFileIndex++] = curChar;
  454. }
  455. if (curChar == EndChar || isspace(curChar) || curChar == 0)
  456. {
  457. configBuffer[index] = 0;
  458. // Parse only if there's something in configBuffer
  459. if (index > 0 && (err = parser.Parse(configBuffer)) != 0)
  460. {
  461. break;
  462. }
  463. while(curChar != EndChar && (isspace(curChar) || curChar == 0))
  464. {
  465. curChar = ReadChar(configFile);
  466. }
  467. if (curChar == EndChar)
  468. {
  469. break;
  470. }
  471. else
  472. {
  473. UnreadChar(curChar, configFile);
  474. }
  475. index = 0;
  476. }
  477. else
  478. {
  479. // The expectation is that non-ANSI characters
  480. // are not used in the config- otherwise it will
  481. // be interpreted incorrectly here
  482. configBuffer[index++] = (char16) curChar;
  483. }
  484. }
  485. #undef ReadChar
  486. #undef UnreadChar
  487. #undef CharType
  488. #undef EndChar
  489. fclose(configFile);
  490. if (err !=0)
  491. {
  492. return;
  493. }
  494. #endif
  495. }
  496. void ConfigParser::ProcessConfiguration(HANDLE hmod)
  497. {
  498. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  499. bool hasOutput = false;
  500. char16 modulename[_MAX_PATH];
  501. GetModuleFileName((HMODULE)hmod, modulename, _MAX_PATH);
  502. // Win32 specific console creation code
  503. // xplat-todo: Consider having this mechanism available on other
  504. // platforms
  505. // Not a pressing need since ChakraCore runs only in consoles by
  506. // default so we don't need to allocate a second console for this
  507. #if CONFIG_CONSOLE_AVAILABLE
  508. if (Js::Configuration::Global.flags.Console)
  509. {
  510. int fd;
  511. FILE *fp;
  512. // fail usually means there is an existing console. We don't really care.
  513. AllocConsole();
  514. fd = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), O_TEXT);
  515. fp = _wfdopen(fd, _u("w"));
  516. if (fp != nullptr)
  517. {
  518. *stdout = *fp;
  519. setvbuf(stdout, nullptr, _IONBF, 0);
  520. fd = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), O_TEXT);
  521. fp = _wfdopen(fd, _u("w"));
  522. if (fp != nullptr)
  523. {
  524. *stderr = *fp;
  525. setvbuf(stderr, nullptr, _IONBF, 0);
  526. char16 buffer[_MAX_PATH + 70];
  527. if (ConfigParserAPI::FillConsoleTitle(buffer, _MAX_PATH + 20, modulename))
  528. {
  529. SetConsoleTitle(buffer);
  530. }
  531. hasOutput = true;
  532. }
  533. }
  534. }
  535. #endif
  536. if (Js::Configuration::Global.flags.IsEnabled(Js::OutputFileFlag)
  537. && Js::Configuration::Global.flags.OutputFile != nullptr)
  538. {
  539. SetOutputFile(Js::Configuration::Global.flags.OutputFile, Js::Configuration::Global.flags.OutputFileOpenMode);
  540. hasOutput = true;
  541. }
  542. if (Js::Configuration::Global.flags.DebugWindow)
  543. {
  544. Output::UseDebuggerWindow();
  545. hasOutput = true;
  546. }
  547. #ifdef ENABLE_TRACE
  548. if (CONFIG_FLAG(InMemoryTrace))
  549. {
  550. Output::SetInMemoryLogger(
  551. Js::MemoryLogger::Create(::GetOutputAllocator1(),
  552. CONFIG_FLAG(InMemoryTraceBufferSize) * 3)); // With stack each trace is 3 entries (header, msg, stack).
  553. hasOutput = true;
  554. }
  555. #ifdef STACK_BACK_TRACE
  556. if (CONFIG_FLAG(TraceWithStack))
  557. {
  558. Output::SetStackTraceHelper(Js::StackTraceHelper::Create(::GetOutputAllocator2()));
  559. }
  560. #endif // STACK_BACK_TRACE
  561. #endif // ENABLE_TRACE
  562. if (hasOutput)
  563. {
  564. ConfigParserAPI::DisplayInitialOutput(modulename);
  565. Output::Print(_u("\n"));
  566. Js::Configuration::Global.flags.VerboseDump();
  567. Output::Flush();
  568. }
  569. if (Js::Configuration::Global.flags.ForceSerialized)
  570. {
  571. // Can't generate or execute byte code under forced serialize
  572. Js::Configuration::Global.flags.GenerateByteCodeBufferReturnsCantGenerate = true;
  573. Js::Configuration::Global.flags.ExecuteByteCodeBufferReturnsInvalidByteCode = true;
  574. }
  575. ForcedMemoryConstraint::Apply();
  576. #endif
  577. #ifdef MEMSPECT_TRACKING
  578. bool all = false;
  579. if (Js::Configuration::Global.flags.Memspect.IsEnabled(Js::AllPhase))
  580. {
  581. all = true;
  582. }
  583. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::RecyclerPhase))
  584. {
  585. RecyclerMemoryTracking::Activate();
  586. }
  587. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::PageAllocatorPhase))
  588. {
  589. PageTracking::Activate();
  590. }
  591. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::ArenaPhase))
  592. {
  593. ArenaMemoryTracking::Activate();
  594. }
  595. #endif
  596. }
  597. HRESULT ConfigParser::SetOutputFile(const WCHAR* outputFile, const WCHAR* openMode)
  598. {
  599. // If present, replace the {PID} token with the process ID
  600. const WCHAR* pidStr = nullptr;
  601. WCHAR buffer[_MAX_PATH];
  602. if ((pidStr = wcsstr(outputFile, _u("{PID}"))) != nullptr)
  603. {
  604. size_t pidStartPosition = pidStr - outputFile;
  605. WCHAR* pDest = buffer;
  606. size_t bufferLen = _MAX_PATH;
  607. // Copy the filename before the {PID} token
  608. wcsncpy_s(pDest, bufferLen, outputFile, pidStartPosition);
  609. pDest += pidStartPosition;
  610. bufferLen = bufferLen - pidStartPosition;
  611. // Copy the PID
  612. _itow_s(GetCurrentProcessId(), pDest, /*bufferSize=*/_MAX_PATH - pidStartPosition, /*radix=*/10);
  613. #pragma prefast(suppress: 26014, "ultow string length is smaller than 256")
  614. pDest += wcslen(pDest);
  615. bufferLen = bufferLen - wcslen(pDest);
  616. // Copy the rest of the string.
  617. #pragma prefast(suppress: 26014, "Overwriting pDset's null terminator is intentional since the string being copied is null terminated")
  618. wcscpy_s(pDest, bufferLen, outputFile + pidStartPosition + /*length of {PID}*/ 5);
  619. outputFile = buffer;
  620. }
  621. char16 fileName[_MAX_PATH];
  622. char16 moduleName[_MAX_PATH];
  623. PlatformAgnostic::SystemInfo::GetBinaryLocation(moduleName, _MAX_PATH);
  624. _wsplitpath_s(moduleName, nullptr, 0, nullptr, 0, fileName, _MAX_PATH, nullptr, 0);
  625. if (_wcsicmp(fileName, _u("WWAHost")) == 0 ||
  626. _wcsicmp(fileName, _u("ByteCodeGenerator")) == 0 ||
  627. _wcsicmp(fileName, _u("spartan")) == 0 ||
  628. _wcsicmp(fileName, _u("spartan_edge")) == 0 ||
  629. _wcsnicmp(fileName, _u("MicrosoftEdge"), wcslen(_u("MicrosoftEdge"))) == 0)
  630. {
  631. // we need to output to %temp% directory in wwa. we don't have permission otherwise.
  632. if (GetEnvironmentVariable(_u("temp"), fileName, _MAX_PATH) != 0)
  633. {
  634. wcscat_s(fileName, _MAX_PATH, _u("\\"));
  635. const char16 * fileNameOnly = wcsrchr(outputFile, _u('\\'));
  636. // if outputFile is full path we just need filename, discard the path
  637. wcscat_s(fileName, _MAX_PATH, fileNameOnly == nullptr ? outputFile : fileNameOnly);
  638. }
  639. else
  640. {
  641. AssertMsg(FALSE, "Get temp environment failed");
  642. }
  643. outputFile = fileName;
  644. }
  645. FILE *fp;
  646. if ((fp = _wfsopen(outputFile, openMode, _SH_DENYWR)) != nullptr)
  647. {
  648. Output::SetOutputFile(fp);
  649. return S_OK;
  650. }
  651. AssertMsg(false, "Could not open file for logging output.");
  652. return E_FAIL;
  653. }