DynamicProfileStorage.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  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 "RuntimeLanguagePch.h"
  6. #ifdef DYNAMIC_PROFILE_STORAGE
  7. bool DynamicProfileStorage::initialized = false;
  8. bool DynamicProfileStorage::uninitialized = false;
  9. bool DynamicProfileStorage::enabled = false;
  10. bool DynamicProfileStorage::useCacheDir = false;
  11. bool DynamicProfileStorage::collectInfo = false;
  12. HANDLE DynamicProfileStorage::mutex = nullptr;
  13. char16 DynamicProfileStorage::cacheDrive[_MAX_DRIVE];
  14. char16 DynamicProfileStorage::cacheDir[_MAX_DIR];
  15. char16 DynamicProfileStorage::catalogFilename[_MAX_PATH];
  16. CriticalSection DynamicProfileStorage::cs;
  17. DynamicProfileStorage::InfoMap DynamicProfileStorage::infoMap(&NoCheckHeapAllocator::Instance);
  18. DynamicProfileStorage::TimeType DynamicProfileStorage::creationTime = DynamicProfileStorage::TimeType();
  19. int32 DynamicProfileStorage::lastOffset = 0;
  20. DWORD const DynamicProfileStorage::MagicNumber = 20100526;
  21. DWORD const DynamicProfileStorage::FileFormatVersion = 2;
  22. DWORD DynamicProfileStorage::nextFileId = 0;
  23. bool DynamicProfileStorage::locked = false;
  24. class DynamicProfileStorageReaderWriter
  25. {
  26. public:
  27. DynamicProfileStorageReaderWriter() : filename(nullptr), file(nullptr) {}
  28. ~DynamicProfileStorageReaderWriter();
  29. bool Init(char16 const * filename, char16 const * mode, bool deleteNonClosed, errno_t * err);
  30. template <typename T>
  31. bool Read(T * t);
  32. template <typename T>
  33. bool ReadArray(T * t, size_t len);
  34. _Success_(return) bool ReadUtf8String(__deref_out_z char16 ** str, __out DWORD * len);
  35. template <typename T>
  36. bool Write(T const& t);
  37. template <typename T>
  38. bool WriteArray(T * t, size_t len);
  39. bool WriteUtf8String(char16 const * str);
  40. bool Seek(int32 offset);
  41. bool SeekToEnd();
  42. int32 Size();
  43. void Close(bool deleteFile = false);
  44. private:
  45. char16 const * filename;
  46. FILE * file;
  47. bool deleteNonClosed;
  48. };
  49. DynamicProfileStorageReaderWriter::~DynamicProfileStorageReaderWriter()
  50. {
  51. if (file)
  52. {
  53. Close(deleteNonClosed);
  54. }
  55. }
  56. bool DynamicProfileStorageReaderWriter::Init(char16 const * filename, char16 const * mode, bool deleteNonClosed, errno_t * err = nullptr)
  57. {
  58. AssertOrFailFast(file == nullptr);
  59. errno_t e = _wfopen_s(&file, filename, mode);
  60. if (e != 0)
  61. {
  62. if (err)
  63. {
  64. *err = e;
  65. }
  66. return false;
  67. }
  68. this->filename = filename;
  69. this->deleteNonClosed = deleteNonClosed;
  70. return true;
  71. }
  72. template <typename T>
  73. bool DynamicProfileStorageReaderWriter::Read(T * t)
  74. {
  75. return ReadArray(t, 1);
  76. }
  77. template <typename T>
  78. bool DynamicProfileStorageReaderWriter::ReadArray(T * t, size_t len)
  79. {
  80. AssertOrFailFast(file);
  81. int32 pos = ftell(file);
  82. if (fread(t, sizeof(T), len, file) != len)
  83. {
  84. Output::Print(_u("ERROR: DynamicProfileStorage: '%s': File corrupted at %d\n"), filename, pos);
  85. Output::Flush();
  86. return false;
  87. }
  88. return true;
  89. }
  90. _Success_(return) bool DynamicProfileStorageReaderWriter::ReadUtf8String(__deref_out_z char16 ** str, __out DWORD * len)
  91. {
  92. DWORD urllen;
  93. if (!Read(&urllen))
  94. {
  95. return false;
  96. }
  97. utf8char_t* tempBuffer = NoCheckHeapNewArray(utf8char_t, urllen);
  98. if (tempBuffer == nullptr)
  99. {
  100. Output::Print(_u("ERROR: DynamicProfileStorage: Out of memory reading '%s'\n"), filename);
  101. Output::Flush();
  102. return false;
  103. }
  104. if (!ReadArray(tempBuffer, urllen))
  105. {
  106. HeapDeleteArray(urllen, tempBuffer);
  107. return false;
  108. }
  109. charcount_t length = utf8::ByteIndexIntoCharacterIndex(tempBuffer, urllen);
  110. char16 * name = NoCheckHeapNewArray(char16, length + 1);
  111. if (name == nullptr)
  112. {
  113. Output::Print(_u("ERROR: DynamicProfileStorage: Out of memory reading '%s'\n"), filename);
  114. Output::Flush();
  115. HeapDeleteArray(urllen, tempBuffer);
  116. return false;
  117. }
  118. utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(name, tempBuffer, tempBuffer + urllen);
  119. NoCheckHeapDeleteArray(urllen, tempBuffer);
  120. *str = name;
  121. *len = length;
  122. return true;
  123. }
  124. template <typename T>
  125. bool DynamicProfileStorageReaderWriter::Write(T const& t)
  126. {
  127. return WriteArray(&t, 1);
  128. }
  129. template <typename T>
  130. bool DynamicProfileStorageReaderWriter::WriteArray(T * t, size_t len)
  131. {
  132. AssertOrFailFast(file);
  133. if (fwrite(t, sizeof(T), len, file) != len)
  134. {
  135. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to write to file '%s'\n"), filename);
  136. Output::Flush();
  137. return false;
  138. }
  139. return true;
  140. }
  141. bool DynamicProfileStorageReaderWriter::WriteUtf8String(char16 const * str)
  142. {
  143. charcount_t len = static_cast<charcount_t>(wcslen(str));
  144. const size_t cbTempBuffer = UInt32Math::Mul<3>(len);
  145. utf8char_t * tempBuffer = NoCheckHeapNewArray(utf8char_t, cbTempBuffer);
  146. if (tempBuffer == nullptr)
  147. {
  148. Output::Print(_u("ERROR: DynamicProfileStorage: Out of memory writing to file '%s'\n"), filename);
  149. Output::Flush();
  150. return false;
  151. }
  152. DWORD cbNeeded = (DWORD)utf8::EncodeInto<utf8::Utf8EncodingKind::Cesu8>(tempBuffer, cbTempBuffer, str, len);
  153. bool success = Write(cbNeeded) && WriteArray(tempBuffer, cbNeeded);
  154. NoCheckHeapDeleteArray(cbTempBuffer, tempBuffer);
  155. return success;
  156. }
  157. bool DynamicProfileStorageReaderWriter::Seek(int32 offset)
  158. {
  159. AssertOrFailFast(file);
  160. return fseek(file, offset, SEEK_SET) == 0;
  161. }
  162. bool DynamicProfileStorageReaderWriter::SeekToEnd()
  163. {
  164. AssertOrFailFast(file);
  165. return fseek(file, 0, SEEK_END) == 0;
  166. }
  167. int32 DynamicProfileStorageReaderWriter::Size()
  168. {
  169. AssertOrFailFast(file);
  170. int32 current = ftell(file);
  171. SeekToEnd();
  172. int32 end = ftell(file);
  173. fseek(file, current, SEEK_SET);
  174. return end;
  175. }
  176. void DynamicProfileStorageReaderWriter::Close(bool deleteFile)
  177. {
  178. AssertOrFailFast(file);
  179. fflush(file);
  180. fclose(file);
  181. file = nullptr;
  182. if (deleteFile)
  183. {
  184. _wunlink(filename);
  185. }
  186. filename = nullptr;
  187. }
  188. void DynamicProfileStorage::StorageInfo::GetFilename(_Out_writes_z_(_MAX_PATH) char16 filename[_MAX_PATH]) const
  189. {
  190. char16 tempFile[_MAX_PATH];
  191. wcscpy_s(tempFile, _u("jsdpcache_file"));
  192. _itow_s(this->fileId, tempFile + _countof(_u("jsdpcache_file")) - 1, _countof(tempFile) - _countof(_u("jsdpcache_file")) + 1, 10);
  193. _wmakepath_s(filename, _MAX_PATH, cacheDrive, cacheDir, tempFile, _u(".dpd"));
  194. }
  195. char const * DynamicProfileStorage::StorageInfo::ReadRecord() const
  196. {
  197. char16 cacheFilename[_MAX_PATH];
  198. this->GetFilename(cacheFilename);
  199. DynamicProfileStorageReaderWriter reader;
  200. if (!reader.Init(cacheFilename, _u("rb"), false))
  201. {
  202. #if DBG_DUMP
  203. if (DynamicProfileStorage::DoTrace())
  204. {
  205. Output::Print(_u("TRACE: DynamicProfileStorage: Unable to open cache dir file '%s'"), cacheFilename);
  206. Output::Flush();
  207. }
  208. #endif
  209. return nullptr;
  210. }
  211. int32 size = reader.Size();
  212. char * record = AllocRecord(size);
  213. if (record == nullptr)
  214. {
  215. Output::Print(_u("ERROR: DynamicProfileStorage: Out of memory reading '%s'"), cacheFilename);
  216. Output::Flush();
  217. return nullptr;
  218. }
  219. if (!reader.ReadArray(GetRecordBuffer(record), size))
  220. {
  221. DeleteRecord(record);
  222. return nullptr;
  223. }
  224. return record;
  225. }
  226. bool DynamicProfileStorage::StorageInfo::WriteRecord(__in_ecount(sizeof(DWORD) + *record)char const * record) const
  227. {
  228. char16 cacheFilename[_MAX_PATH];
  229. this->GetFilename(cacheFilename);
  230. DynamicProfileStorageReaderWriter writer;
  231. if (!writer.Init(cacheFilename, _u("wcb"), true))
  232. {
  233. Output::Print(_u("ERROR: DynamicProfileStorage: Unable open record file '%s'"), cacheFilename);
  234. Output::Flush();
  235. return false;
  236. }
  237. if (!writer.WriteArray(GetRecordBuffer(record), GetRecordSize(record)))
  238. {
  239. return false;
  240. }
  241. // Success
  242. writer.Close();
  243. return true;
  244. }
  245. #if DBG_DUMP
  246. bool DynamicProfileStorage::DoTrace()
  247. {
  248. return Js::Configuration::Global.flags.Trace.IsEnabled(Js::DynamicProfileStoragePhase);
  249. }
  250. #endif
  251. char16 const * DynamicProfileStorage::GetMessageType()
  252. {
  253. if (!DynamicProfileStorage::DoCollectInfo())
  254. {
  255. return _u("WARNING");
  256. }
  257. #if DBG_DUMP
  258. if (DynamicProfileStorage::DoTrace())
  259. {
  260. return _u("TRACE");
  261. }
  262. #endif
  263. return nullptr;
  264. }
  265. bool DynamicProfileStorage::Initialize()
  266. {
  267. AssertOrFailFastMsg(!initialized, "Initialize called multiple times");
  268. if (initialized)
  269. {
  270. return true;
  271. }
  272. bool success = true;
  273. initialized = true;
  274. #ifdef FORCE_DYNAMIC_PROFILE_STORAGE
  275. enabled = true;
  276. collectInfo = true;
  277. if (!SetupCacheDir(nullptr))
  278. {
  279. success = false;
  280. }
  281. #else
  282. if (Js::Configuration::Global.flags.IsEnabled(Js::DynamicProfileCacheDirFlag))
  283. {
  284. enabled = true;
  285. collectInfo = true;
  286. if (!SetupCacheDir(Js::Configuration::Global.flags.DynamicProfileCacheDir))
  287. {
  288. success = false;
  289. }
  290. }
  291. #endif
  292. // If -DynamicProfileInput is specified, the file specified in -DynamicProfileCache
  293. // will not be imported and will be overwritten
  294. if (Js::Configuration::Global.flags.IsEnabled(Js::DynamicProfileInputFlag))
  295. {
  296. enabled = true;
  297. ClearCacheCatalog();
  298. // -DynamicProfileInput
  299. // Without other -DynamicProfile flags - enable in memory profile cache without exporting
  300. // With -DynamicProfileCache - override the dynamic profile cache file
  301. // With -DynamicProfileCacheDir - clear the dynamic profile cache directory
  302. if (Js::Configuration::Global.flags.DynamicProfileInput != nullptr)
  303. {
  304. // Error if we can't in the profile info if we are not using a cache file or directory.
  305. collectInfo = collectInfo || Js::Configuration::Global.flags.IsEnabled(Js::DynamicProfileCacheFlag);
  306. // Try to open the DynamicProfileInput.
  307. // If failure to open, retry at 100 ms intervals until a timeout.
  308. const uint32 MAX_DELAY = 2000; // delay at most 2 seconds
  309. const uint32 DELAY_INTERVAL = 100;
  310. const uint32 MAX_TRIES = MAX_DELAY / DELAY_INTERVAL;
  311. bool readSuccessful = false;
  312. for (uint32 i = 0; i < MAX_TRIES; i++)
  313. {
  314. readSuccessful = ImportFile(Js::Configuration::Global.flags.DynamicProfileInput, false);
  315. if (readSuccessful)
  316. {
  317. break;
  318. }
  319. Sleep(DELAY_INTERVAL);
  320. if (Js::Configuration::Global.flags.Verbose)
  321. {
  322. Output::Print(_u(" Retrying load of dynamic profile from '%s' (attempt %d)...\n"),
  323. (char16 const *)Js::Configuration::Global.flags.DynamicProfileInput, i + 1);
  324. Output::Flush();
  325. }
  326. }
  327. if (!readSuccessful)
  328. {
  329. // If file cannot be read, behave as if DynamicProfileInput == null.
  330. collectInfo = true;
  331. }
  332. }
  333. else
  334. {
  335. // Don't error if we can't find the profile info
  336. collectInfo = true;
  337. }
  338. }
  339. else if (Js::Configuration::Global.flags.IsEnabled(Js::DynamicProfileCacheFlag))
  340. {
  341. enabled = true;
  342. collectInfo = true;
  343. if (Js::Configuration::Global.flags.DynamicProfileCache)
  344. {
  345. if (!ImportFile(Js::Configuration::Global.flags.DynamicProfileCache, true))
  346. {
  347. success = false;
  348. }
  349. }
  350. }
  351. return success;
  352. }
  353. // We used to have problem with dynamic profile being corrupt and this is to verify it.
  354. // We don't see this any more so we will just disable it to speed up unittest
  355. #if 0
  356. #if DBG && defined(_M_AMD64)
  357. #define DYNAMIC_PROFILE_EXPORT_FILE_CHECK
  358. #endif
  359. #endif
  360. bool DynamicProfileStorage::Uninitialize()
  361. {
  362. AssertOrFailFastMsg(!uninitialized, "Uninitialize called multiple times");
  363. if (!initialized || uninitialized)
  364. {
  365. return true;
  366. }
  367. #ifdef DYNAMIC_PROFILE_EXPORT_FILE_CHECK
  368. bool exportFile = false;
  369. #endif
  370. uninitialized = true;
  371. bool success = true;
  372. if (Js::Configuration::Global.flags.DynamicProfileCache != nullptr)
  373. {
  374. AssertOrFailFast(enabled);
  375. if (!ExportFile(Js::Configuration::Global.flags.DynamicProfileCache))
  376. {
  377. success = false;
  378. }
  379. #ifdef DYNAMIC_PROFILE_EXPORT_FILE_CHECK
  380. exportFile = true;
  381. #endif
  382. }
  383. if (mutex != nullptr)
  384. {
  385. CloseHandle(mutex);
  386. }
  387. #ifdef DYNAMIC_PROFILE_EXPORT_FILE_CHECK
  388. uint32 oldCount = infoMap.Count();
  389. #endif
  390. ClearInfoMap(false);
  391. #ifdef DYNAMIC_PROFILE_EXPORT_FILE_CHECK
  392. if (exportFile)
  393. {
  394. HRESULT hr;
  395. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT
  396. {
  397. if (!ImportFile(Js::Configuration::Global.flags.DynamicProfileCache, false))
  398. {
  399. success = false;
  400. }
  401. Assert(oldCount == infoMap.Count());
  402. }
  403. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT(hr)
  404. ClearInfoMap(false);
  405. }
  406. #endif
  407. return success;
  408. }
  409. void DynamicProfileStorage::ClearInfoMap(bool deleteFileStorage)
  410. {
  411. uint recordCount = infoMap.Count();
  412. uint recordFreed = 0;
  413. for (uint i = 0; recordFreed < recordCount; i++)
  414. {
  415. char16 const * name = infoMap.GetKeyAt(i);
  416. if (name == nullptr)
  417. {
  418. continue;
  419. }
  420. NoCheckHeapDeleteArray(wcslen(name) + 1, name);
  421. StorageInfo const& info = infoMap.GetValueAt(i);
  422. if (info.isFileStorage)
  423. {
  424. AssertOrFailFast(useCacheDir);
  425. if (deleteFileStorage)
  426. {
  427. char16 filename[_MAX_PATH];
  428. info.GetFilename(filename);
  429. _wunlink(filename);
  430. }
  431. }
  432. else
  433. {
  434. DeleteRecord(info.record);
  435. }
  436. recordFreed++;
  437. }
  438. infoMap.Clear();
  439. }
  440. bool DynamicProfileStorage::ImportFile(__in_z char16 const * filename, bool allowNonExistingFile)
  441. {
  442. AssertOrFailFast(enabled);
  443. DynamicProfileStorageReaderWriter reader;
  444. errno_t e;
  445. if (!reader.Init(filename, _u("rb"), false, &e))
  446. {
  447. if (allowNonExistingFile)
  448. {
  449. return true;
  450. }
  451. else
  452. {
  453. if (Js::Configuration::Global.flags.Verbose)
  454. {
  455. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to open file '%s' to import (%d)\n"), filename, e);
  456. char16 error_string[256];
  457. _wcserror_s(error_string, e);
  458. Output::Print(_u("ERROR: For file '%s': %s (%d)\n"), filename, error_string, e);
  459. Output::Flush();
  460. }
  461. return false;
  462. }
  463. }
  464. DWORD magic;
  465. DWORD version;
  466. DWORD recordCount;
  467. if (!reader.Read(&magic)
  468. || !reader.Read(&version)
  469. || !reader.Read(&recordCount))
  470. {
  471. return false;
  472. }
  473. if (magic != MagicNumber)
  474. {
  475. Output::Print(_u("ERROR: DynamicProfileStorage: '%s' is not a dynamic profile data file"), filename);
  476. Output::Flush();
  477. return false;
  478. }
  479. if (version != FileFormatVersion)
  480. {
  481. if (allowNonExistingFile)
  482. {
  483. // Treat version mismatch as non-existent file
  484. return true;
  485. }
  486. Output::Print(_u("ERROR: DynamicProfileStorage: '%s' has format version %d; version %d expected"), filename,
  487. version, FileFormatVersion);
  488. Output::Flush();
  489. return false;
  490. }
  491. for (uint i = 0; i < recordCount; i++)
  492. {
  493. DWORD len;
  494. char16 * name;
  495. if (!reader.ReadUtf8String(&name, &len))
  496. {
  497. AssertOrFailFast(false);
  498. return false;
  499. }
  500. DWORD recordLen;
  501. if (!reader.Read(&recordLen))
  502. {
  503. AssertOrFailFast(false);
  504. return false;
  505. }
  506. char * record = AllocRecord(recordLen);
  507. if (record == nullptr)
  508. {
  509. Output::Print(_u("ERROR: DynamicProfileStorage: Out of memory importing '%s'\n"), filename);
  510. Output::Flush();
  511. NoCheckHeapDeleteArray(len + 1, name);
  512. return false;
  513. }
  514. if (!reader.ReadArray(GetRecordBuffer(record), recordLen))
  515. {
  516. NoCheckHeapDeleteArray(len + 1, name);
  517. DeleteRecord(record);
  518. AssertOrFailFast(false);
  519. return false;
  520. }
  521. SaveRecord(name, record);
  522. // Save record will make a copy of the name
  523. NoCheckHeapDeleteArray(len + 1, name);
  524. }
  525. #if DBG_DUMP
  526. if (DynamicProfileStorage::DoTrace())
  527. {
  528. Output::Print(_u("TRACE: DynamicProfileStorage: Imported file: '%s'\n"), filename);
  529. Output::Flush();
  530. }
  531. #endif
  532. AssertOrFailFastMsg(recordCount == (uint)infoMap.Count(), "failed to read all the records");
  533. return true;
  534. }
  535. bool DynamicProfileStorage::ExportFile(__in_z char16 const * filename)
  536. {
  537. AssertOrFailFast(enabled);
  538. if (useCacheDir && AcquireLock())
  539. {
  540. if (!LoadCacheCatalog()) // refresh the cache catalog
  541. {
  542. ReleaseLock();
  543. AssertOrFailFast(FALSE);
  544. return false;
  545. }
  546. }
  547. DynamicProfileStorageReaderWriter writer;
  548. if (!writer.Init(filename, _u("wcb"), true))
  549. {
  550. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to open file '%s' to export\n"), filename);
  551. Output::Flush();
  552. return false;
  553. }
  554. DWORD recordCount = infoMap.Count();
  555. if (!writer.Write(MagicNumber)
  556. || !writer.Write(FileFormatVersion)
  557. || !writer.Write(recordCount))
  558. {
  559. AssertOrFailFast(FALSE);
  560. return false;
  561. }
  562. for (uint i = 0; i < recordCount; i++)
  563. {
  564. char16 const * url = infoMap.GetKeyAt(i);
  565. if (url == nullptr)
  566. {
  567. AssertOrFailFast(false);
  568. continue;
  569. }
  570. StorageInfo const& info = infoMap.GetValueAt(i);
  571. char const * record;
  572. if (info.isFileStorage)
  573. {
  574. AssertOrFailFast(useCacheDir);
  575. record = info.ReadRecord();
  576. if (record == nullptr)
  577. {
  578. ReleaseLock();
  579. AssertOrFailFast(FALSE);
  580. return false;
  581. }
  582. }
  583. else
  584. {
  585. AssertOrFailFast(!useCacheDir);
  586. AssertOrFailFast(!locked);
  587. record = info.record;
  588. }
  589. DWORD recordSize = GetRecordSize(record);
  590. bool failed = (!writer.WriteUtf8String(url)
  591. || !writer.Write(recordSize)
  592. || !writer.WriteArray(GetRecordBuffer(record), recordSize));
  593. if (useCacheDir)
  594. {
  595. DeleteRecord(record);
  596. }
  597. if (failed)
  598. {
  599. if (useCacheDir)
  600. {
  601. ReleaseLock();
  602. }
  603. AssertOrFailFast(FALSE);
  604. return false;
  605. }
  606. }
  607. writer.Close();
  608. #if DBG_DUMP
  609. if (DynamicProfileStorage::DoTrace())
  610. {
  611. Output::Print(_u("TRACE: DynamicProfileStorage: Exported file: '%s'\n"), filename);
  612. Output::Flush();
  613. }
  614. #endif
  615. return true;
  616. }
  617. void DynamicProfileStorage::DisableCacheDir()
  618. {
  619. AssertOrFailFast(useCacheDir);
  620. ClearInfoMap(false);
  621. useCacheDir = false;
  622. #ifdef FORCE_DYNAMIC_PROFILE_STORAGE
  623. Js::Throw::FatalInternalError();
  624. #endif
  625. }
  626. bool DynamicProfileStorage::AcquireLock()
  627. {
  628. AssertOrFailFast(mutex != nullptr);
  629. AssertOrFailFast(!locked);
  630. DWORD ret = WaitForSingleObject(mutex, INFINITE);
  631. if (ret == WAIT_OBJECT_0 || ret == WAIT_ABANDONED)
  632. {
  633. #if DBG
  634. locked = true;
  635. #endif
  636. return true;
  637. }
  638. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to acquire mutex %d\n"), ret);
  639. Output::Flush();
  640. DisableCacheDir();
  641. return false;
  642. }
  643. bool DynamicProfileStorage::ReleaseLock()
  644. {
  645. AssertOrFailFast(locked);
  646. AssertOrFailFast(mutex != nullptr);
  647. #if DBG
  648. locked = false;
  649. #endif
  650. if (ReleaseMutex(mutex))
  651. {
  652. return true;
  653. }
  654. DisableCacheDir();
  655. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to release mutex"));
  656. Output::Flush();
  657. return false;
  658. }
  659. bool DynamicProfileStorage::SetupCacheDir(__in_z char16 const * dirname)
  660. {
  661. AssertOrFailFast(enabled);
  662. mutex = CreateMutex(NULL, FALSE, _u("JSDPCACHE"));
  663. if (mutex == nullptr)
  664. {
  665. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to create mutex"));
  666. Output::Flush();
  667. return false;
  668. }
  669. useCacheDir = true;
  670. if (!AcquireLock())
  671. {
  672. return false;
  673. }
  674. char16 tempPath[_MAX_PATH];
  675. if (dirname == nullptr)
  676. {
  677. uint32 len = GetTempPath(_MAX_PATH, tempPath);
  678. if (len >= _MAX_PATH || wcscat_s(tempPath, _u("jsdpcache")) != 0)
  679. {
  680. DisableCacheDir();
  681. Output::Print(_u("ERROR: DynamicProfileStorage: Can't setup cache directory: Unable to create directory\n"));
  682. Output::Flush();
  683. ReleaseLock();
  684. return false;
  685. }
  686. if (!CreateDirectory(tempPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
  687. {
  688. DisableCacheDir();
  689. Output::Print(_u("ERROR: DynamicProfileStorage: Can't setup cache directory: Unable to create directory\n"));
  690. Output::Flush();
  691. ReleaseLock();
  692. return false;
  693. }
  694. dirname = tempPath;
  695. }
  696. char16 cacheFile[_MAX_FNAME];
  697. char16 cacheExt[_MAX_EXT];
  698. _wsplitpath_s(dirname, cacheDrive, cacheDir, cacheFile, cacheExt);
  699. wcscat_s(cacheDir, cacheFile);
  700. wcscat_s(cacheDir, cacheExt);
  701. _wmakepath_s(catalogFilename, cacheDrive, cacheDir, _u("jsdpcache_master"), _u(".dpc"));
  702. bool succeed = LoadCacheCatalog();
  703. ReleaseLock();
  704. return succeed;
  705. }
  706. bool DynamicProfileStorage::CreateCacheCatalog()
  707. {
  708. AssertOrFailFast(enabled);
  709. AssertOrFailFast(useCacheDir);
  710. AssertOrFailFast(locked);
  711. nextFileId = 0;
  712. creationTime = GetCreationTime();
  713. DynamicProfileStorageReaderWriter catalogFile;
  714. if (!catalogFile.Init(catalogFilename, _u("wb"), true)
  715. || !catalogFile.Write(MagicNumber)
  716. || !catalogFile.Write(FileFormatVersion)
  717. || !catalogFile.Write(creationTime)
  718. || !catalogFile.Write(0)) // count
  719. {
  720. DisableCacheDir();
  721. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to create cache catalog\n"));
  722. Output::Flush();
  723. return false;
  724. }
  725. lastOffset = catalogFile.Size();
  726. ClearInfoMap(true);
  727. catalogFile.Close();
  728. #if DBG_DUMP
  729. if (DynamicProfileStorage::DoTrace())
  730. {
  731. Output::Print(_u("TRACE: DynamicProfileStorage: Cache directory catalog created: '%s'\n"), catalogFilename);
  732. Output::Flush();
  733. }
  734. #endif
  735. return true;
  736. }
  737. bool DynamicProfileStorage::AppendCacheCatalog(__in_z char16 const * url)
  738. {
  739. AssertOrFailFast(enabled);
  740. AssertOrFailFast(useCacheDir);
  741. AssertOrFailFast(locked);
  742. DWORD magic;
  743. DWORD version;
  744. DWORD count;
  745. DWORD time;
  746. DynamicProfileStorageReaderWriter catalogFile;
  747. if (!catalogFile.Init(catalogFilename, _u("rcb+"), false))
  748. {
  749. return CreateCacheCatalog();
  750. }
  751. if (!catalogFile.Seek(0)
  752. || !catalogFile.Read(&magic)
  753. || !catalogFile.Read(&version)
  754. || !catalogFile.Read(&time)
  755. || !catalogFile.Read(&count)
  756. || magic != MagicNumber
  757. || version < FileFormatVersion)
  758. {
  759. catalogFile.Close();
  760. #if DBG_DUMP
  761. if (DynamicProfileStorage::DoTrace())
  762. {
  763. Output::Print(_u("TRACE: DynamicProfileStorage: Overwriting file for cache directory catalog: '%s'\n"), catalogFilename);
  764. Output::Flush();
  765. }
  766. #endif
  767. return CreateCacheCatalog();
  768. }
  769. if (version > FileFormatVersion)
  770. {
  771. DisableCacheDir();
  772. Output::Print(_u("ERROR: DynamicProfileStorage: Existing cache catalog has a newer format\n"));
  773. Output::Flush();
  774. return false;
  775. }
  776. if (time != creationTime || count + 1 != nextFileId)
  777. {
  778. // This should not happen, as we are under lock from the LoadCacheCatalog
  779. DisableCacheDir();
  780. Output::Print(_u("ERROR: DynamicProfileStorage: Internal error, file modified under lock\n"));
  781. Output::Flush();
  782. return false;
  783. }
  784. if (!catalogFile.SeekToEnd() ||
  785. !catalogFile.WriteUtf8String(url) ||
  786. !catalogFile.Seek(3 * sizeof(DWORD)) ||
  787. !catalogFile.Write(nextFileId))
  788. {
  789. #if DBG_DUMP
  790. if (DynamicProfileStorage::DoTrace())
  791. {
  792. Output::Print(_u("TRACE: DynamicProfileStorage: Write failure. Cache directory catalog corrupted: '%s'\n"), catalogFilename);
  793. Output::Flush();
  794. }
  795. #endif
  796. catalogFile.Close();
  797. return CreateCacheCatalog();
  798. }
  799. lastOffset = catalogFile.Size();
  800. return true;
  801. }
  802. bool DynamicProfileStorage::LoadCacheCatalog()
  803. {
  804. AssertOrFailFast(enabled);
  805. AssertOrFailFast(useCacheDir);
  806. AssertOrFailFast(locked);
  807. DynamicProfileStorageReaderWriter catalogFile;
  808. DWORD magic;
  809. DWORD version;
  810. DWORD count;
  811. DWORD time;
  812. if (!catalogFile.Init(catalogFilename, _u("rb"), false))
  813. {
  814. return CreateCacheCatalog();
  815. }
  816. if (!catalogFile.Read(&magic)
  817. || !catalogFile.Read(&version)
  818. || !catalogFile.Read(&time)
  819. || !catalogFile.Read(&count)
  820. || magic != MagicNumber
  821. || version < FileFormatVersion)
  822. {
  823. catalogFile.Close();
  824. #if DBG_DUMP
  825. if (DynamicProfileStorage::DoTrace())
  826. {
  827. Output::Print(_u("TRACE: DynamicProfileStorage: Overwriting file for cache directory catalog: '%s'\n"), catalogFilename);
  828. Output::Flush();
  829. }
  830. #endif
  831. return CreateCacheCatalog();
  832. }
  833. if (version > FileFormatVersion)
  834. {
  835. DisableCacheDir();
  836. Output::Print(_u("ERROR: DynamicProfileStorage: Existing cache catalog has a newer format.\n"));
  837. Output::Flush();
  838. return false;
  839. }
  840. DWORD start = 0;
  841. AssertOrFailFast(useCacheDir);
  842. if (time == creationTime)
  843. {
  844. // We can reuse existing data
  845. start = infoMap.Count();
  846. AssertOrFailFast(count >= start);
  847. AssertOrFailFast(catalogFile.Size() >= lastOffset);
  848. if (count == nextFileId)
  849. {
  850. AssertOrFailFast(catalogFile.Size() == lastOffset);
  851. return true;
  852. }
  853. if (!catalogFile.Seek(lastOffset))
  854. {
  855. catalogFile.Close();
  856. Output::Print(_u("ERROR: DynamicProfileStorage: Unable to seek to last known offset\n"));
  857. Output::Flush();
  858. return CreateCacheCatalog();
  859. }
  860. }
  861. else if (creationTime != 0)
  862. {
  863. Output::Print(_u("WARNING: DynamicProfileStorage: Reloading full catalog\n"));
  864. Output::Flush();
  865. }
  866. for (DWORD i = start; i < count; i++)
  867. {
  868. DWORD len;
  869. char16 * url;
  870. if (!catalogFile.ReadUtf8String(&url, &len))
  871. {
  872. #if DBG_DUMP
  873. if (DynamicProfileStorage::DoTrace())
  874. {
  875. Output::Print(_u("TRACE: DynamicProfileStorage: Cache dir catalog file corrupted: '%s'\n"), catalogFilename);
  876. Output::Flush();
  877. }
  878. #endif
  879. // REVIEW: the file is corrupted, should we not flush the cache totally?
  880. catalogFile.Close();
  881. return CreateCacheCatalog();
  882. }
  883. StorageInfo * oldInfo;
  884. if (infoMap.TryGetReference(url, &oldInfo))
  885. {
  886. AssertOrFailFast(oldInfo->isFileStorage);
  887. oldInfo->fileId = i;
  888. }
  889. else
  890. {
  891. StorageInfo newInfo;
  892. newInfo.isFileStorage = true;
  893. newInfo.fileId = i;
  894. infoMap.Add(url, newInfo);
  895. }
  896. }
  897. #if DBG_DUMP
  898. if (creationTime == 0 && DynamicProfileStorage::DoTrace())
  899. {
  900. Output::Print(_u("TRACE: DynamicProfileStorage: Cache directory catalog loaded: '%s'\n"), catalogFilename);
  901. Output::Flush();
  902. }
  903. #endif
  904. nextFileId = count;
  905. creationTime = time;
  906. lastOffset = catalogFile.Size();
  907. return true;
  908. }
  909. void DynamicProfileStorage::ClearCacheCatalog()
  910. {
  911. AssertOrFailFast(enabled);
  912. if (useCacheDir)
  913. {
  914. if (!AcquireLock())
  915. {
  916. return;
  917. }
  918. bool success = CreateCacheCatalog();
  919. ReleaseLock();
  920. if (success)
  921. {
  922. #if DBG_DUMP
  923. if (DynamicProfileStorage::DoTrace())
  924. {
  925. Output::Print(_u("TRACE: DynamicProfileStorage: Cache dir clears\n"));
  926. Output::Flush();
  927. }
  928. #endif
  929. return;
  930. }
  931. }
  932. else
  933. {
  934. ClearInfoMap(false);
  935. }
  936. }
  937. void DynamicProfileStorage::SaveRecord(__in_z char16 const * filename, __in_ecount(sizeof(DWORD) + *record) char const * record)
  938. {
  939. AssertOrFailFast(enabled);
  940. AutoCriticalSection autocs(&cs);
  941. StorageInfo * info;
  942. if (useCacheDir && AcquireLock())
  943. {
  944. if (!LoadCacheCatalog()) // refresh the cache catalog
  945. {
  946. ReleaseLock();
  947. }
  948. }
  949. if (infoMap.TryGetReference(filename, &info))
  950. {
  951. if (!info->isFileStorage)
  952. {
  953. AssertOrFailFast(!useCacheDir);
  954. if (info->record != nullptr)
  955. {
  956. // Here it can be in GC and generated new record, and the GC call an be from
  957. // allocation that deserializing info->record. So not replacing the old record
  958. // since we might be loading data from it, and drop the new generated one.
  959. DeleteRecord(record);
  960. }
  961. else
  962. {
  963. info->record = record;
  964. }
  965. return;
  966. }
  967. AssertOrFailFast(useCacheDir);
  968. char16 cacheFilename[_MAX_PATH];
  969. info->GetFilename(cacheFilename);
  970. DynamicProfileStorageReaderWriter writer;
  971. if (info->WriteRecord(record))
  972. {
  973. // Success
  974. ReleaseLock();
  975. return;
  976. }
  977. // Fail, try to add it again
  978. info->fileId = nextFileId++;
  979. if (info->WriteRecord(record))
  980. {
  981. if (AppendCacheCatalog(filename))
  982. {
  983. ReleaseLock();
  984. return;
  985. }
  986. }
  987. else
  988. {
  989. DisableCacheDir();
  990. }
  991. // Can't add a new file. Disable and use memory mode
  992. AssertOrFailFast(!useCacheDir);
  993. ReleaseLock();
  994. }
  995. size_t len = wcslen(filename) + 1;
  996. char16 * newFilename = NoCheckHeapNewArray(char16, len);
  997. if (newFilename == nullptr)
  998. {
  999. // out of memory, don't save anything
  1000. AssertOrFailFastMsg(false, "OOM");
  1001. DeleteRecord(record);
  1002. if (useCacheDir)
  1003. {
  1004. ReleaseLock();
  1005. }
  1006. return;
  1007. }
  1008. wmemcpy_s(newFilename, len, filename, len);
  1009. StorageInfo newInfo;
  1010. if (useCacheDir)
  1011. {
  1012. newInfo.isFileStorage = true;
  1013. newInfo.fileId = nextFileId++;
  1014. if (newInfo.WriteRecord(record))
  1015. {
  1016. infoMap.Add(newFilename, newInfo);
  1017. if (AppendCacheCatalog(newFilename))
  1018. {
  1019. ReleaseLock();
  1020. return;
  1021. }
  1022. }
  1023. // Can't even add a record. Disable and use memory mode
  1024. DisableCacheDir();
  1025. ReleaseLock();
  1026. }
  1027. AssertOrFailFast(!useCacheDir);
  1028. AssertOrFailFast(!locked);
  1029. newInfo.isFileStorage = false;
  1030. newInfo.record = record;
  1031. infoMap.Add(newFilename, newInfo);
  1032. }
  1033. char * DynamicProfileStorage::AllocRecord(DWORD bufferSize)
  1034. {
  1035. AssertOrFailFast(enabled);
  1036. char * buffer = NoCheckHeapNewArray(char, bufferSize + sizeof(DWORD));
  1037. if (buffer != nullptr)
  1038. {
  1039. *(DWORD *)buffer = bufferSize;
  1040. }
  1041. return buffer;
  1042. }
  1043. DWORD DynamicProfileStorage::GetRecordSize(__in_ecount(sizeof(DWORD) + *buffer) char const * buffer)
  1044. {
  1045. AssertOrFailFast(enabled);
  1046. return *(DWORD *)buffer;
  1047. }
  1048. char const * DynamicProfileStorage::GetRecordBuffer(__in_ecount(sizeof(DWORD) + *buffer) char const * buffer)
  1049. {
  1050. AssertOrFailFast(enabled);
  1051. return buffer + sizeof(DWORD);
  1052. }
  1053. char * DynamicProfileStorage::GetRecordBuffer(__in_ecount(sizeof(DWORD) + *buffer) char * buffer)
  1054. {
  1055. AssertOrFailFast(enabled);
  1056. return buffer + sizeof(DWORD);
  1057. }
  1058. void DynamicProfileStorage::DeleteRecord(__in_ecount(sizeof(DWORD) + *buffer) char const * buffer)
  1059. {
  1060. AssertOrFailFast(enabled);
  1061. NoCheckHeapDeleteArray(GetRecordSize(buffer) + sizeof(DWORD), buffer);
  1062. }
  1063. #endif