ConfigParser.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  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. // Asmjs feature control
  263. // This setting allows enabling\disabling asmjs compilation
  264. // 0 - Disable Asmjs phase - Also default behavior
  265. // 1 - Enable Asmjs phase
  266. dwValue = 0;
  267. dwSize = sizeof(dwValue);
  268. if (NOERROR == RegGetValueW(hk, nullptr, _u("EnableAsmjs"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  269. {
  270. if (dwValue == 1)
  271. {
  272. Js::Configuration::Global.flags.Asmjs = true;
  273. }
  274. }
  275. // Spectre mitigation feature control
  276. // This setting allows enabling\disabling spectre mitigations
  277. // 0 - Disable Spectre mitigations
  278. // 1 - Enable Spectre mitigations - Also default behavior
  279. dwValue = 0;
  280. dwSize = sizeof(dwValue);
  281. if (NOERROR == RegGetValueW(hk, nullptr, _u("MitigateSpectre"), RRF_RT_DWORD, nullptr, (LPBYTE)&dwValue, &dwSize))
  282. {
  283. Js::ConfigFlagsTable &configFlags = Js::Configuration::Global.flags;
  284. configFlags.Enable(Js::MitigateSpectreFlag);
  285. if (dwValue == 0)
  286. {
  287. configFlags.SetAsBoolean(Js::MitigateSpectreFlag, false);
  288. }
  289. else if (dwValue == 1)
  290. {
  291. configFlags.SetAsBoolean(Js::MitigateSpectreFlag, true);
  292. }
  293. }
  294. #endif // _WIN32
  295. }
  296. void ConfigParser::ParseConfig(HANDLE hmod, CmdLineArgsParser &parser)
  297. {
  298. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && CONFIG_PARSE_CONFIG_FILE
  299. Assert(!_hasReadConfig);
  300. _hasReadConfig = true;
  301. int err = 0;
  302. char16 modulename[_MAX_PATH];
  303. char16 filename[_MAX_PATH];
  304. GetModuleFileName((HMODULE)hmod, modulename, _MAX_PATH);
  305. char16 drive[_MAX_DRIVE];
  306. char16 dir[_MAX_DIR];
  307. _wsplitpath_s(modulename, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
  308. _wmakepath_s(filename, drive, dir, _configFileName, _u(".config"));
  309. FILE* configFile;
  310. #ifdef _WIN32
  311. if (_wfopen_s(&configFile, filename, _u("r, ccs=UNICODE")) != 0 || configFile == nullptr)
  312. {
  313. WCHAR configFileFullName[MAX_PATH];
  314. StringCchPrintf(configFileFullName, MAX_PATH, _u("%s.config"), _configFileName);
  315. // try the one in the current working directory (Desktop)
  316. if (_wfullpath(filename, configFileFullName, _MAX_PATH) == nullptr)
  317. {
  318. return;
  319. }
  320. if (_wfopen_s(&configFile, filename, _u("r, ccs=UNICODE")) != 0 || configFile == nullptr)
  321. {
  322. return;
  323. }
  324. }
  325. #else
  326. // Two-pathed for a couple reasons
  327. // 1. PAL doesn't like the ccs option passed in.
  328. // 2. _wfullpath is not implemented in the PAL.
  329. // Instead, on xplat, we'll check the HOME directory to see if there is
  330. // a config file there that we can use
  331. if (_wfopen_s(&configFile, filename, _u("r")) != 0 || configFile == nullptr)
  332. {
  333. WCHAR homeDir[MAX_PATH];
  334. if (GetEnvironmentVariable(_u("HOME"), homeDir, MAX_PATH) == 0)
  335. {
  336. return;
  337. }
  338. WCHAR configFileFullName[MAX_PATH];
  339. StringCchPrintf(configFileFullName, MAX_PATH, _u("%s/%s.config"), homeDir, _configFileName);
  340. if (_wfopen_s(&configFile, configFileFullName, _u("r")) != 0 || configFile == nullptr)
  341. {
  342. return;
  343. }
  344. }
  345. #endif
  346. char16 configBuffer[MaxTokenSize];
  347. int index = 0;
  348. #ifdef _WIN32
  349. #define ReadChar(file) fgetwc(file)
  350. #define UnreadChar(c, file) ungetwc(c, file)
  351. #define CharType wint_t
  352. #define EndChar WEOF
  353. #else
  354. #define ReadChar(file) fgetc(file)
  355. #define UnreadChar(c, file) ungetc(c, file)
  356. #define CharType int
  357. #define EndChar EOF
  358. #endif
  359. // We don't expect the token to overflow- if it does
  360. // the simplest thing to do would be to ignore the
  361. // read tokens
  362. // We could use _fwscanf_s here but the function
  363. // isn't implemented in the PAL and we'd have to deal with
  364. // wchar => char16 impedance mismatch.
  365. while (index < MaxTokenSize)
  366. {
  367. CharType curChar = ReadChar(configFile);
  368. if (curChar == EndChar || isspace(curChar))
  369. {
  370. configBuffer[index] = 0;
  371. if ((err = parser.Parse(configBuffer)) != 0)
  372. {
  373. break;
  374. }
  375. while(curChar != EndChar && isspace(curChar))
  376. {
  377. curChar = ReadChar(configFile);
  378. }
  379. if (curChar == EndChar)
  380. {
  381. break;
  382. }
  383. else
  384. {
  385. UnreadChar(curChar, configFile);
  386. }
  387. index = 0;
  388. }
  389. else
  390. {
  391. // The expectation is that non-ANSI characters
  392. // are not used in the config- otherwise it will
  393. // be interpreted incorrectly here
  394. configBuffer[index++] = (char16) curChar;
  395. }
  396. }
  397. #undef ReadChar
  398. #undef UnreadChar
  399. #undef CharType
  400. #undef EndChar
  401. fclose(configFile);
  402. if (err !=0)
  403. {
  404. return;
  405. }
  406. #endif
  407. }
  408. void ConfigParser::ProcessConfiguration(HANDLE hmod)
  409. {
  410. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  411. bool hasOutput = false;
  412. char16 modulename[_MAX_PATH];
  413. GetModuleFileName((HMODULE)hmod, modulename, _MAX_PATH);
  414. // Win32 specific console creation code
  415. // xplat-todo: Consider having this mechanism available on other
  416. // platforms
  417. // Not a pressing need since ChakraCore runs only in consoles by
  418. // default so we don't need to allocate a second console for this
  419. #if CONFIG_CONSOLE_AVAILABLE
  420. if (Js::Configuration::Global.flags.Console)
  421. {
  422. int fd;
  423. FILE *fp;
  424. // fail usually means there is an existing console. We don't really care.
  425. AllocConsole();
  426. fd = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), O_TEXT);
  427. fp = _wfdopen(fd, _u("w"));
  428. if (fp != nullptr)
  429. {
  430. *stdout = *fp;
  431. setvbuf(stdout, nullptr, _IONBF, 0);
  432. fd = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), O_TEXT);
  433. fp = _wfdopen(fd, _u("w"));
  434. if (fp != nullptr)
  435. {
  436. *stderr = *fp;
  437. setvbuf(stderr, nullptr, _IONBF, 0);
  438. char16 buffer[_MAX_PATH + 70];
  439. if (ConfigParserAPI::FillConsoleTitle(buffer, _MAX_PATH + 20, modulename))
  440. {
  441. SetConsoleTitle(buffer);
  442. }
  443. hasOutput = true;
  444. }
  445. }
  446. }
  447. #endif
  448. if (Js::Configuration::Global.flags.IsEnabled(Js::OutputFileFlag)
  449. && Js::Configuration::Global.flags.OutputFile != nullptr)
  450. {
  451. SetOutputFile(Js::Configuration::Global.flags.OutputFile, Js::Configuration::Global.flags.OutputFileOpenMode);
  452. hasOutput = true;
  453. }
  454. if (Js::Configuration::Global.flags.DebugWindow)
  455. {
  456. Output::UseDebuggerWindow();
  457. hasOutput = true;
  458. }
  459. #ifdef ENABLE_TRACE
  460. if (CONFIG_FLAG(InMemoryTrace))
  461. {
  462. Output::SetInMemoryLogger(
  463. Js::MemoryLogger::Create(::GetOutputAllocator1(),
  464. CONFIG_FLAG(InMemoryTraceBufferSize) * 3)); // With stack each trace is 3 entries (header, msg, stack).
  465. hasOutput = true;
  466. }
  467. #ifdef STACK_BACK_TRACE
  468. if (CONFIG_FLAG(TraceWithStack))
  469. {
  470. Output::SetStackTraceHelper(Js::StackTraceHelper::Create(::GetOutputAllocator2()));
  471. }
  472. #endif // STACK_BACK_TRACE
  473. #endif // ENABLE_TRACE
  474. if (hasOutput)
  475. {
  476. ConfigParserAPI::DisplayInitialOutput(modulename);
  477. Output::Print(_u("\n"));
  478. Js::Configuration::Global.flags.VerboseDump();
  479. Output::Flush();
  480. }
  481. if (Js::Configuration::Global.flags.ForceSerialized)
  482. {
  483. // Can't generate or execute byte code under forced serialize
  484. Js::Configuration::Global.flags.GenerateByteCodeBufferReturnsCantGenerate = true;
  485. Js::Configuration::Global.flags.ExecuteByteCodeBufferReturnsInvalidByteCode = true;
  486. }
  487. ForcedMemoryConstraint::Apply();
  488. #endif
  489. #ifdef MEMSPECT_TRACKING
  490. bool all = false;
  491. if (Js::Configuration::Global.flags.Memspect.IsEnabled(Js::AllPhase))
  492. {
  493. all = true;
  494. }
  495. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::RecyclerPhase))
  496. {
  497. RecyclerMemoryTracking::Activate();
  498. }
  499. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::PageAllocatorPhase))
  500. {
  501. PageTracking::Activate();
  502. }
  503. if (all || Js::Configuration::Global.flags.Memspect.IsEnabled(Js::ArenaPhase))
  504. {
  505. ArenaMemoryTracking::Activate();
  506. }
  507. #endif
  508. }
  509. HRESULT ConfigParser::SetOutputFile(const WCHAR* outputFile, const WCHAR* openMode)
  510. {
  511. // If present, replace the {PID} token with the process ID
  512. const WCHAR* pidStr = nullptr;
  513. WCHAR buffer[_MAX_PATH];
  514. if ((pidStr = wcsstr(outputFile, _u("{PID}"))) != nullptr)
  515. {
  516. size_t pidStartPosition = pidStr - outputFile;
  517. WCHAR* pDest = buffer;
  518. size_t bufferLen = _MAX_PATH;
  519. // Copy the filename before the {PID} token
  520. wcsncpy_s(pDest, bufferLen, outputFile, pidStartPosition);
  521. pDest += pidStartPosition;
  522. bufferLen = bufferLen - pidStartPosition;
  523. // Copy the PID
  524. _itow_s(GetCurrentProcessId(), pDest, /*bufferSize=*/_MAX_PATH - pidStartPosition, /*radix=*/10);
  525. #pragma prefast(suppress: 26014, "ultow string length is smaller than 256")
  526. pDest += wcslen(pDest);
  527. bufferLen = bufferLen - wcslen(pDest);
  528. // Copy the rest of the string.
  529. #pragma prefast(suppress: 26014, "Overwriting pDset's null terminator is intentional since the string being copied is null terminated")
  530. wcscpy_s(pDest, bufferLen, outputFile + pidStartPosition + /*length of {PID}*/ 5);
  531. outputFile = buffer;
  532. }
  533. char16 fileName[_MAX_PATH];
  534. char16 moduleName[_MAX_PATH];
  535. GetModuleFileName(0, moduleName, _MAX_PATH);
  536. _wsplitpath_s(moduleName, nullptr, 0, nullptr, 0, fileName, _MAX_PATH, nullptr, 0);
  537. if (_wcsicmp(fileName, _u("WWAHost")) == 0 ||
  538. _wcsicmp(fileName, _u("ByteCodeGenerator")) == 0 ||
  539. _wcsicmp(fileName, _u("spartan")) == 0 ||
  540. _wcsicmp(fileName, _u("spartan_edge")) == 0 ||
  541. _wcsnicmp(fileName, _u("MicrosoftEdge"), wcslen(_u("MicrosoftEdge"))) == 0)
  542. {
  543. // we need to output to %temp% directory in wwa. we don't have permission otherwise.
  544. if (GetEnvironmentVariable(_u("temp"), fileName, _MAX_PATH) != 0)
  545. {
  546. wcscat_s(fileName, _MAX_PATH, _u("\\"));
  547. const char16 * fileNameOnly = wcsrchr(outputFile, _u('\\'));
  548. // if outputFile is full path we just need filename, discard the path
  549. wcscat_s(fileName, _MAX_PATH, fileNameOnly == nullptr ? outputFile : fileNameOnly);
  550. }
  551. else
  552. {
  553. AssertMsg(FALSE, "Get temp environment failed");
  554. }
  555. outputFile = fileName;
  556. }
  557. FILE *fp;
  558. if ((fp = _wfsopen(outputFile, openMode, _SH_DENYWR)) != nullptr)
  559. {
  560. Output::SetOutputFile(fp);
  561. return S_OK;
  562. }
  563. AssertMsg(false, "Could not open file for logging output.");
  564. return E_FAIL;
  565. }