EmitBuffer.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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 "Backend.h"
  6. template class EmitBufferManager<FakeCriticalSection>;
  7. template class EmitBufferManager<CriticalSection>;
  8. //----------------------------------------------------------------------------
  9. // EmitBufferManager::EmitBufferManager
  10. // Constructor
  11. //----------------------------------------------------------------------------
  12. template <typename SyncObject>
  13. EmitBufferManager<SyncObject>::EmitBufferManager(ArenaAllocator * allocator, CustomHeap::CodePageAllocators * codePageAllocators,
  14. Js::ScriptContext * scriptContext, LPCWSTR name) :
  15. allocationHeap(allocator, codePageAllocators),
  16. allocator(allocator),
  17. allocations(nullptr),
  18. scriptContext(scriptContext)
  19. {
  20. #if DBG_DUMP
  21. this->totalBytesCode = 0;
  22. this->totalBytesLoopBody = 0;
  23. this->totalBytesAlignment = 0;
  24. this->totalBytesCommitted = 0;
  25. this->totalBytesReserved = 0;
  26. this->name = name;
  27. #endif
  28. }
  29. //----------------------------------------------------------------------------
  30. // EmitBufferManager::~EmitBufferManager()
  31. // Free up all the VirtualAlloced memory
  32. //----------------------------------------------------------------------------
  33. template <typename SyncObject>
  34. EmitBufferManager<SyncObject>::~EmitBufferManager()
  35. {
  36. Clear();
  37. }
  38. template <typename SyncObject>
  39. void
  40. EmitBufferManager<SyncObject>::Decommit()
  41. {
  42. FreeAllocations(false);
  43. }
  44. template <typename SyncObject>
  45. void
  46. EmitBufferManager<SyncObject>::Clear()
  47. {
  48. FreeAllocations(true);
  49. }
  50. template <typename SyncObject>
  51. void
  52. EmitBufferManager<SyncObject>::FreeAllocations(bool release)
  53. {
  54. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  55. #if DBG_DUMP
  56. if (!release && PHASE_STATS1(Js::EmitterPhase))
  57. {
  58. this->DumpAndResetStats(Js::Configuration::Global.flags.Filename);
  59. }
  60. #endif
  61. EmitBufferAllocation * allocation = this->allocations;
  62. while (allocation != nullptr)
  63. {
  64. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  65. if(CONFIG_FLAG(CheckEmitBufferPermissions))
  66. {
  67. CheckBufferPermissions(allocation);
  68. }
  69. #endif
  70. if (release)
  71. {
  72. this->allocationHeap.Free(allocation->allocation);
  73. }
  74. else if ((scriptContext != nullptr) && allocation->recorded)
  75. {
  76. // In case of ThunkEmitter the script context would be null and we don't want to track that as code size.
  77. this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted);
  78. allocation->recorded = false;
  79. }
  80. allocation = allocation->nextAllocation;
  81. }
  82. if (release)
  83. {
  84. this->allocations = nullptr;
  85. }
  86. else
  87. {
  88. this->allocationHeap.DecommitAll();
  89. }
  90. }
  91. template <typename SyncObject>
  92. bool EmitBufferManager<SyncObject>::IsInHeap(__in void* address)
  93. {
  94. AutoRealOrFakeCriticalSection<SyncObject> autocs(&this->criticalSection);
  95. return this->allocationHeap.IsInHeap(address);
  96. }
  97. class AutoCustomHeapPointer
  98. {
  99. public:
  100. AutoCustomHeapPointer(CustomHeap::Heap* allocationHeap, CustomHeap::Allocation* heapAllocation) :
  101. _allocationHeap(allocationHeap),
  102. _heapAllocation(heapAllocation)
  103. {
  104. }
  105. ~AutoCustomHeapPointer()
  106. {
  107. if (_heapAllocation)
  108. {
  109. _allocationHeap->Free(_heapAllocation);
  110. }
  111. }
  112. CustomHeap::Allocation* Detach()
  113. {
  114. CustomHeap::Allocation* allocation = _heapAllocation;
  115. Assert(allocation != nullptr);
  116. _heapAllocation = nullptr;
  117. return allocation;
  118. }
  119. private:
  120. CustomHeap::Allocation* _heapAllocation;
  121. CustomHeap::Heap* _allocationHeap;
  122. };
  123. //----------------------------------------------------------------------------
  124. // EmitBufferManager::NewAllocation
  125. // Create a new allocation
  126. //----------------------------------------------------------------------------
  127. template <typename SyncObject>
  128. EmitBufferAllocation *
  129. EmitBufferManager<SyncObject>::NewAllocation(size_t bytes, ushort pdataCount, ushort xdataSize, bool canAllocInPreReservedHeapPageSegment, bool isAnyJittedCode)
  130. {
  131. FAULTINJECT_MEMORY_THROW(L"JIT", bytes);
  132. Assert(this->criticalSection.IsLocked());
  133. bool isAllJITCodeInPreReservedRegion = true;
  134. CustomHeap::Allocation* heapAllocation = this->allocationHeap.Alloc(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, &isAllJITCodeInPreReservedRegion);
  135. if (!isAllJITCodeInPreReservedRegion)
  136. {
  137. this->scriptContext->GetThreadContext()->ResetIsAllJITCodeInPreReservedRegion();
  138. }
  139. if (heapAllocation == nullptr)
  140. {
  141. // This is used in interpreter scenario, thus we need to try to recover memory, if possible.
  142. // Can't simply throw as in JIT scenario, for which throw is what we want in order to give more mem to interpreter.
  143. JsUtil::ExternalApi::RecoverUnusedMemory();
  144. heapAllocation = this->allocationHeap.Alloc(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, &isAllJITCodeInPreReservedRegion);
  145. }
  146. if (heapAllocation == nullptr)
  147. {
  148. Js::Throw::OutOfMemory();
  149. }
  150. AutoCustomHeapPointer allocatedMemory(&this->allocationHeap, heapAllocation);
  151. VerboseHeapTrace(L"New allocation: 0x%p, size: %p\n", heapAllocation->address, heapAllocation->size);
  152. EmitBufferAllocation * allocation = AnewStruct(this->allocator, EmitBufferAllocation);
  153. allocation->bytesCommitted = heapAllocation->size;
  154. allocation->allocation = allocatedMemory.Detach();
  155. allocation->bytesUsed = 0;
  156. allocation->nextAllocation = this->allocations;
  157. allocation->recorded = false;
  158. this->allocations = allocation;
  159. #if DBG
  160. heapAllocation->isAllocationUsed = true;
  161. #endif
  162. #if DBG_DUMP
  163. this->totalBytesCommitted += heapAllocation->size;
  164. #endif
  165. return allocation;
  166. }
  167. template <typename SyncObject>
  168. bool
  169. EmitBufferManager<SyncObject>::FreeAllocation(void* address)
  170. {
  171. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  172. EmitBufferAllocation* previous = nullptr;
  173. EmitBufferAllocation* allocation = allocations;
  174. while(allocation != nullptr)
  175. {
  176. if (address >= allocation->allocation->address && address < (allocation->allocation->address + allocation->bytesUsed))
  177. {
  178. if (previous == nullptr)
  179. {
  180. this->allocations = allocation->nextAllocation;
  181. }
  182. else
  183. {
  184. previous->nextAllocation = allocation->nextAllocation;
  185. }
  186. if ((scriptContext != nullptr) && allocation->recorded)
  187. {
  188. this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted);
  189. }
  190. VerboseHeapTrace(L"Freeing 0x%p, allocation: 0x%p\n", address, allocation->allocation->address);
  191. this->allocationHeap.Free(allocation->allocation);
  192. this->allocator->Free(allocation, sizeof(EmitBufferAllocation));
  193. return true;
  194. }
  195. previous = allocation;
  196. allocation = allocation->nextAllocation;
  197. }
  198. return false;
  199. }
  200. //----------------------------------------------------------------------------
  201. // EmitBufferManager::FinalizeAllocation
  202. // Fill the rest of the page with debugger breakpoint.
  203. //----------------------------------------------------------------------------
  204. template <typename SyncObject>
  205. bool EmitBufferManager<SyncObject>::FinalizeAllocation(EmitBufferAllocation *allocation)
  206. {
  207. Assert(this->criticalSection.IsLocked());
  208. DWORD bytes = allocation->BytesFree();
  209. if(bytes > 0)
  210. {
  211. BYTE* buffer = nullptr;
  212. this->GetBuffer(allocation, bytes, &buffer);
  213. if (!this->CommitBuffer(allocation, buffer, 0, /*sourceBuffer=*/ nullptr, /*alignPad=*/ bytes))
  214. {
  215. return false;
  216. }
  217. #if DBG_DUMP
  218. this->totalBytesCode -= bytes;
  219. #endif
  220. }
  221. return true;
  222. }
  223. template <typename SyncObject>
  224. EmitBufferAllocation* EmitBufferManager<SyncObject>::GetBuffer(EmitBufferAllocation *allocation, __in size_t bytes, __deref_bcount(bytes) BYTE** ppBuffer)
  225. {
  226. Assert(this->criticalSection.IsLocked());
  227. Assert(allocation->BytesFree() >= bytes);
  228. // In case of ThunkEmitter the script context would be null and we don't want to track that as code size.
  229. if (scriptContext && !allocation->recorded)
  230. {
  231. this->scriptContext->GetThreadContext()->AddCodeSize(allocation->bytesCommitted);
  232. allocation->recorded = true;
  233. }
  234. // The codegen buffer is beyond the alignment section - hence, we pass this pointer.
  235. *ppBuffer = allocation->GetUnused();
  236. return allocation;
  237. }
  238. //----------------------------------------------------------------------------
  239. // EmitBufferManager::Allocate
  240. // Allocates an executable buffer with a certain alignment
  241. // NOTE: This buffer is not readable or writable. Use CommitBuffer
  242. // to modify this buffer one page at a time.
  243. //----------------------------------------------------------------------------
  244. template <typename SyncObject>
  245. EmitBufferAllocation* EmitBufferManager<SyncObject>::AllocateBuffer(__in size_t bytes, __deref_bcount(bytes) BYTE** ppBuffer, ushort pdataCount /*=0*/, ushort xdataSize /*=0*/, bool canAllocInPreReservedHeapPageSegment /*=false*/,
  246. bool isAnyJittedCode /* = false*/)
  247. {
  248. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  249. Assert(ppBuffer != nullptr);
  250. EmitBufferAllocation * allocation = this->NewAllocation(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode);
  251. GetBuffer(allocation, bytes, ppBuffer);
  252. #if DBG
  253. MEMORY_BASIC_INFORMATION memBasicInfo;
  254. size_t resultBytes = VirtualQuery(allocation->allocation->address, &memBasicInfo, sizeof(memBasicInfo));
  255. Assert(resultBytes != 0 && memBasicInfo.Protect == PAGE_EXECUTE);
  256. #endif
  257. return allocation;
  258. }
  259. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  260. template <typename SyncObject>
  261. bool EmitBufferManager<SyncObject>::CheckCommitFaultInjection()
  262. {
  263. if (Js::Configuration::Global.flags.ForceOOMOnEBCommit == 0)
  264. {
  265. return false;
  266. }
  267. commitCount++;
  268. if (Js::Configuration::Global.flags.ForceOOMOnEBCommit == -1)
  269. {
  270. Output::Print(L"Commit count: %d\n", commitCount);
  271. }
  272. else if (commitCount == Js::Configuration::Global.flags.ForceOOMOnEBCommit)
  273. {
  274. return true;
  275. }
  276. return false;
  277. }
  278. #endif
  279. template <typename SyncObject>
  280. bool EmitBufferManager<SyncObject>::ProtectBufferWithExecuteReadWriteForInterpreter(EmitBufferAllocation* allocation)
  281. {
  282. Assert(this->criticalSection.IsLocked());
  283. Assert(allocation != nullptr);
  284. return (this->allocationHeap.ProtectAllocationWithExecuteReadWrite(allocation->allocation) == TRUE);
  285. }
  286. // Returns true if we successfully commit the buffer
  287. // Returns false if we OOM
  288. template <typename SyncObject>
  289. bool EmitBufferManager<SyncObject>::CommitReadWriteBufferForInterpreter(EmitBufferAllocation* allocation, _In_reads_bytes_(bufferSize) BYTE* pBuffer, _In_ size_t bufferSize)
  290. {
  291. Assert(this->criticalSection.IsLocked());
  292. Assert(allocation != nullptr);
  293. allocation->bytesUsed += bufferSize;
  294. #ifdef DEBUG
  295. this->totalBytesCode += bufferSize;
  296. #endif
  297. VerboseHeapTrace(L"Setting execute permissions on 0x%p, allocation: 0x%p\n", pBuffer, allocation->allocation->address);
  298. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  299. if (CheckCommitFaultInjection())
  300. {
  301. return false;
  302. }
  303. #endif
  304. if (!this->allocationHeap.ProtectAllocationWithExecuteReadOnly(allocation->allocation))
  305. {
  306. return false;
  307. }
  308. FlushInstructionCache(AutoSystemInfo::Data.GetProcessHandle(), pBuffer, bufferSize);
  309. return true;
  310. }
  311. //----------------------------------------------------------------------------
  312. // EmitBufferManager::CommitBuffer
  313. // Aligns the buffer with DEBUG instructions.
  314. // Copies contents of source buffer to the destination buffer - at max of one page at a time.
  315. // This ensures that only 1 page is writable at any point of time.
  316. // Commit a buffer from the last AllocateBuffer call that is filled.
  317. //----------------------------------------------------------------------------
  318. template <typename SyncObject>
  319. bool
  320. EmitBufferManager<SyncObject>::CommitBuffer(EmitBufferAllocation* allocation, __out_bcount(bytes) BYTE* destBuffer, __in size_t bytes, __in_bcount(bytes) const BYTE* sourceBuffer, __in DWORD alignPad)
  321. {
  322. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  323. Assert(destBuffer != nullptr);
  324. Assert(allocation != nullptr);
  325. BYTE *currentDestBuffer = allocation->GetUnused();
  326. BYTE *bufferToFlush = currentDestBuffer;
  327. Assert(allocation->BytesFree() >= bytes + alignPad);
  328. size_t bytesLeft = bytes + alignPad;
  329. size_t sizeToFlush = bytesLeft;
  330. // Copy the contents and set the alignment pad
  331. while(bytesLeft != 0)
  332. {
  333. DWORD spaceInCurrentPage = AutoSystemInfo::PageSize - ((size_t)currentDestBuffer & (AutoSystemInfo::PageSize - 1));
  334. size_t bytesToChange = bytesLeft > spaceInCurrentPage ? spaceInCurrentPage : bytesLeft;
  335. // Buffer and the bytes that are marked RWX - these will eventually be marked as 'EXCEUTE' only.
  336. BYTE* readWriteBuffer = currentDestBuffer;
  337. size_t readWriteBytes = bytesToChange;
  338. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  339. if (CheckCommitFaultInjection())
  340. {
  341. return false;
  342. }
  343. #endif
  344. if (!this->allocationHeap.ProtectAllocationWithExecuteReadWrite(allocation->allocation, (char*)readWriteBuffer))
  345. {
  346. return false;
  347. }
  348. if (alignPad != 0)
  349. {
  350. DWORD alignBytes = alignPad < spaceInCurrentPage ? alignPad : spaceInCurrentPage;
  351. CustomHeap::FillDebugBreak(currentDestBuffer, alignBytes);
  352. alignPad -= alignBytes;
  353. currentDestBuffer += alignBytes;
  354. allocation->bytesUsed += alignBytes;
  355. bytesLeft -= alignBytes;
  356. bytesToChange -= alignBytes;
  357. #if DBG_DUMP
  358. this->totalBytesAlignment += alignBytes;
  359. #endif
  360. }
  361. // If there are bytes still left to be copied then we should do the copy.
  362. if(bytesToChange > 0)
  363. {
  364. AssertMsg(alignPad == 0, "If we are copying right now - we should be done with setting alignment.");
  365. memcpy_s(currentDestBuffer, allocation->BytesFree(), sourceBuffer, bytesToChange);
  366. currentDestBuffer += bytesToChange;
  367. sourceBuffer += bytesToChange;
  368. allocation->bytesUsed += bytesToChange;
  369. bytesLeft -= bytesToChange;
  370. }
  371. Assert(readWriteBuffer + readWriteBytes == currentDestBuffer);
  372. if (!this->allocationHeap.ProtectAllocationWithExecuteReadOnly(allocation->allocation, (char*)readWriteBuffer))
  373. {
  374. return false;
  375. }
  376. }
  377. FlushInstructionCache(AutoSystemInfo::Data.GetProcessHandle(), bufferToFlush, sizeToFlush);
  378. #if DBG_DUMP
  379. this->totalBytesCode += bytes;
  380. #endif
  381. //Finish the current EmitBufferAllocation
  382. return FinalizeAllocation(allocation);
  383. }
  384. template <typename SyncObject>
  385. void
  386. EmitBufferManager<SyncObject>::CompletePreviousAllocation(EmitBufferAllocation* allocation)
  387. {
  388. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  389. if (allocation != nullptr)
  390. {
  391. allocation->bytesUsed = allocation->bytesCommitted;
  392. }
  393. }
  394. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  395. template <typename SyncObject>
  396. void
  397. EmitBufferManager<SyncObject>::CheckBufferPermissions(EmitBufferAllocation *allocation)
  398. {
  399. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  400. if(allocation->bytesCommitted == 0)
  401. return;
  402. MEMORY_BASIC_INFORMATION memInfo;
  403. BYTE *buffer = (BYTE*) allocation->allocation->address;
  404. SIZE_T size = allocation->bytesCommitted;
  405. while(1)
  406. {
  407. SIZE_T result = VirtualQuery(buffer, &memInfo, sizeof(memInfo));
  408. if(result == 0)
  409. {
  410. // VirtualQuery failed. This is not an expected condition, but it would be benign for the purposes of this check. Seems
  411. // to occur occasionally on process shutdown.
  412. break;
  413. }
  414. else if(memInfo.Protect == PAGE_EXECUTE_READWRITE)
  415. {
  416. Output::Print(L"ERROR: Found PAGE_EXECUTE_READWRITE page!\n");
  417. #ifdef DEBUG
  418. AssertMsg(FALSE, "Page was marked PAGE_EXECUTE_READWRITE");
  419. #else
  420. Fatal();
  421. #endif
  422. }
  423. // Figure out if we need to continue the query. The returned size might be larger than the size we requested,
  424. // for instance if more pages were allocated directly afterward, with the same permissions.
  425. if(memInfo.RegionSize >= size)
  426. {
  427. break;
  428. }
  429. // recalculate size for next iteration
  430. buffer += memInfo.RegionSize;
  431. size -= memInfo.RegionSize;
  432. if(size <= 0)
  433. {
  434. AssertMsg(FALSE, "Last VirtualQuery left us with unmatched regions");
  435. break;
  436. }
  437. }
  438. }
  439. #endif
  440. #if DBG_DUMP
  441. template <typename SyncObject>
  442. void
  443. EmitBufferManager<SyncObject>::DumpAndResetStats(wchar_t const * filename)
  444. {
  445. if (this->totalBytesCommitted != 0)
  446. {
  447. size_t wasted = this->totalBytesCommitted - this->totalBytesCode - this->totalBytesAlignment;
  448. Output::Print(L"Stats for %s: %s \n", name, filename);
  449. Output::Print(L" Total code size : %10d (%6.2f%% of committed)\n", this->totalBytesCode,
  450. (float)this->totalBytesCode * 100 / this->totalBytesCommitted);
  451. Output::Print(L" Total LoopBody code : %10d\n", this->totalBytesLoopBody);
  452. Output::Print(L" Total alignment size : %10d (%6.2f%% of committed)\n", this->totalBytesAlignment,
  453. (float)this->totalBytesAlignment * 100 / this->totalBytesCommitted);
  454. Output::Print(L" Total wasted size : %10d (%6.2f%% of committed)\n", wasted,
  455. (float)wasted * 100 / this->totalBytesCommitted);
  456. Output::Print(L" Total committed size : %10d (%6.2f%% of reserved)\n", this->totalBytesCommitted,
  457. (float)this->totalBytesCommitted * 100 / this->totalBytesReserved);
  458. Output::Print(L" Total reserved size : %10d\n", this->totalBytesReserved);
  459. }
  460. this->totalBytesCode = 0;
  461. this->totalBytesLoopBody = 0;
  462. this->totalBytesAlignment = 0;
  463. this->totalBytesCommitted = 0;
  464. this->totalBytesReserved = 0;
  465. }
  466. #endif