DynamicProfileStorage.cpp 31 KB

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