HeapAllocator.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  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 "CommonMemoryPch.h"
  6. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  7. // Not enabled in ChakraCore
  8. #include "MemProtectHeap.h"
  9. #endif
  10. // Initialization order
  11. // AB AutoSystemInfo
  12. // AD PerfCounter
  13. // AE PerfCounterSet
  14. // AM Output/Configuration
  15. // AN MemProtectHeap
  16. // AP DbgHelpSymbolManager
  17. // AQ CFGLogger
  18. // AR LeakReport
  19. // AS JavascriptDispatch/RecyclerObjectDumper
  20. // AT HeapAllocator/RecyclerHeuristic
  21. // AU RecyclerWriteBarrierManager
  22. #pragma warning(disable:4075) // initializers put in unrecognized initialization area on purpose
  23. #pragma init_seg(".CRT$XCAT")
  24. #ifdef HEAP_TRACK_ALLOC
  25. CriticalSection HeapAllocator::cs;
  26. #endif
  27. #ifdef CHECK_MEMORY_LEAK
  28. MemoryLeakCheck MemoryLeakCheck::leakCheck;
  29. #endif
  30. HeapAllocator HeapAllocator::Instance;
  31. NoThrowHeapAllocator NoThrowHeapAllocator::Instance;
  32. NoCheckHeapAllocator NoCheckHeapAllocator::Instance;
  33. HANDLE NoCheckHeapAllocator::processHeap = nullptr;
  34. template <bool noThrow>
  35. char * HeapAllocator::AllocT(size_t byteSize)
  36. {
  37. #ifdef HEAP_TRACK_ALLOC
  38. size_t requestedBytes = byteSize;
  39. byteSize = AllocSizeMath::Add(requestedBytes, ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT));
  40. TrackAllocData allocData;
  41. ClearTrackAllocInfo(&allocData);
  42. #elif defined(HEAP_PERF_COUNTERS)
  43. size_t requestedBytes = byteSize;
  44. byteSize = AllocSizeMath::Add(requestedBytes, ::Math::Align<size_t>(sizeof(size_t), MEMORY_ALLOCATION_ALIGNMENT));
  45. #endif
  46. if (noThrow)
  47. {
  48. FAULTINJECT_MEMORY_NOTHROW(_u("Heap"), byteSize);
  49. }
  50. else
  51. {
  52. FAULTINJECT_MEMORY_THROW(_u("Heap"), byteSize);
  53. }
  54. char * buffer;
  55. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  56. if (DoUseMemProtectHeap())
  57. {
  58. void * memory = MemProtectHeapRootAlloc(memProtectHeapHandle, byteSize);
  59. if (memory == nullptr)
  60. {
  61. if (noThrow)
  62. {
  63. return nullptr;
  64. }
  65. Js::Throw::OutOfMemory();
  66. }
  67. buffer = (char *)memory;
  68. }
  69. else
  70. #endif
  71. {
  72. if (CONFIG_FLAG(PrivateHeap))
  73. {
  74. buffer = (char *)HeapAlloc(GetPrivateHeap(), 0, byteSize);
  75. }
  76. else
  77. {
  78. buffer = (char *)malloc(byteSize);
  79. }
  80. }
  81. if (!noThrow && buffer == nullptr)
  82. {
  83. Js::Throw::OutOfMemory();
  84. }
  85. #if defined(HEAP_TRACK_ALLOC) || defined(HEAP_PERF_COUNTERS)
  86. if (!noThrow || buffer != nullptr)
  87. {
  88. #ifdef HEAP_TRACK_ALLOC
  89. cs.Enter();
  90. data.LogAlloc((HeapAllocRecord *)buffer, requestedBytes, allocData);
  91. cs.Leave();
  92. buffer += ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT);
  93. #else
  94. *(size_t *)buffer = requestedBytes;
  95. buffer += ::Math::Align<size_t>(sizeof(size_t), MEMORY_ALLOCATION_ALIGNMENT);
  96. #endif
  97. HEAP_PERF_COUNTER_INC(LiveObject);
  98. HEAP_PERF_COUNTER_ADD(LiveObjectSize, requestedBytes);
  99. }
  100. #endif
  101. return buffer;
  102. }
  103. template char * HeapAllocator::AllocT<true>(size_t byteSize);
  104. template char * HeapAllocator::AllocT<false>(size_t byteSize);
  105. void HeapAllocator::Free(void * buffer, size_t byteSize)
  106. {
  107. #ifdef HEAP_TRACK_ALLOC
  108. if (buffer != nullptr)
  109. {
  110. HeapAllocRecord * record = (HeapAllocRecord *)(((char *)buffer) - ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT));
  111. Assert(byteSize == (size_t)-1 || record->size == byteSize);
  112. HEAP_PERF_COUNTER_DEC(LiveObject);
  113. HEAP_PERF_COUNTER_SUB(LiveObjectSize, record->size);
  114. cs.Enter();
  115. data.LogFree(record);
  116. cs.Leave();
  117. buffer = record;
  118. #if DBG
  119. memset(buffer, DbgMemFill, record->size + ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT));
  120. #endif
  121. }
  122. #elif defined(HEAP_PERF_COUNTERS)
  123. if (buffer != nullptr)
  124. {
  125. HEAP_PERF_COUNTER_DEC(LiveObject);
  126. size_t * allocSize = (size_t *)(((char *)buffer) - ::Math::Align<size_t>(sizeof(size_t), MEMORY_ALLOCATION_ALIGNMENT));
  127. HEAP_PERF_COUNTER_SUB(LiveObjectSize, *allocSize);
  128. buffer = allocSize;
  129. }
  130. #endif
  131. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  132. if (DoUseMemProtectHeap())
  133. {
  134. HRESULT hr = MemProtectHeapUnrootAndZero(memProtectHeapHandle, buffer);
  135. Assert(SUCCEEDED(hr));
  136. return;
  137. }
  138. #endif
  139. if (CONFIG_FLAG(PrivateHeap))
  140. {
  141. HeapFree(GetPrivateHeap(), 0, buffer);
  142. }
  143. else
  144. {
  145. free(buffer);
  146. }
  147. }
  148. void HeapAllocator::InitPrivateHeap()
  149. {
  150. if (this->m_privateHeap == nullptr)
  151. {
  152. this->m_privateHeap = HeapCreate(0, 0, 0); // no options, default initial size, no max size
  153. }
  154. }
  155. void HeapAllocator::DestroyPrivateHeap()
  156. {
  157. if (this->m_privateHeap != nullptr)
  158. {
  159. // xplat-todo: PAL no HeapDestroy?
  160. #ifdef _WIN32
  161. BOOL success = HeapDestroy(this->m_privateHeap);
  162. Assert(success);
  163. #endif
  164. this->m_privateHeap = nullptr;
  165. }
  166. }
  167. HANDLE HeapAllocator::GetPrivateHeap()
  168. {
  169. InitPrivateHeap(); // will initialize PrivateHeap if not already initialized
  170. return this->m_privateHeap;
  171. }
  172. #ifdef TRACK_ALLOC
  173. #ifdef HEAP_TRACK_ALLOC
  174. THREAD_LOCAL TrackAllocData HeapAllocator::nextAllocData;
  175. #endif
  176. HeapAllocator * HeapAllocator::TrackAllocInfo(TrackAllocData const& data)
  177. {
  178. #ifdef HEAP_TRACK_ALLOC
  179. Assert(nextAllocData.IsEmpty());
  180. nextAllocData = data;
  181. #endif
  182. return this;
  183. }
  184. void HeapAllocator::ClearTrackAllocInfo(TrackAllocData* data/* = NULL*/)
  185. {
  186. #ifdef HEAP_TRACK_ALLOC
  187. Assert(!nextAllocData.IsEmpty());
  188. if (data)
  189. {
  190. *data = nextAllocData;
  191. }
  192. nextAllocData.Clear();
  193. #endif
  194. }
  195. #endif
  196. #ifdef HEAP_TRACK_ALLOC
  197. //static
  198. bool HeapAllocator::CheckLeaks()
  199. {
  200. return Instance.data.CheckLeaks();
  201. }
  202. #endif // HEAP_TRACK_ALLOC
  203. char * NoThrowHeapAllocator::AllocZero(size_t byteSize)
  204. {
  205. return HeapAllocator::Instance.NoThrowAllocZero(byteSize);
  206. }
  207. char * NoThrowHeapAllocator::Alloc(size_t byteSize)
  208. {
  209. return HeapAllocator::Instance.NoThrowAlloc(byteSize);
  210. }
  211. void NoThrowHeapAllocator::Free(void * buffer, size_t byteSize)
  212. {
  213. HeapAllocator::Instance.Free(buffer, byteSize);
  214. }
  215. #ifdef TRACK_ALLOC
  216. NoThrowHeapAllocator * NoThrowHeapAllocator::TrackAllocInfo(TrackAllocData const& data)
  217. {
  218. HeapAllocator::Instance.TrackAllocInfo(data);
  219. return this;
  220. }
  221. #endif // TRACK_ALLOC
  222. #ifdef TRACK_ALLOC
  223. void NoThrowHeapAllocator::ClearTrackAllocInfo(TrackAllocData* data /*= NULL*/)
  224. {
  225. HeapAllocator::Instance.ClearTrackAllocInfo(data);
  226. }
  227. #endif // TRACK_ALLOC
  228. HeapAllocator * HeapAllocator::GetNoMemProtectInstance()
  229. {
  230. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  231. // Used only in Chakra, no need to use CUSTOM_CONFIG_FLAG
  232. if (CONFIG_FLAG(MemProtectHeap))
  233. {
  234. return &NoMemProtectInstance;
  235. }
  236. #endif
  237. return &Instance;
  238. }
  239. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  240. HeapAllocator HeapAllocator::NoMemProtectInstance(false);
  241. bool HeapAllocator::DoUseMemProtectHeap()
  242. {
  243. if (!allocMemProtect)
  244. {
  245. return false;
  246. }
  247. if (memProtectHeapHandle != nullptr)
  248. {
  249. return true;
  250. }
  251. DebugOnly(bool wasUsed = isUsed);
  252. isUsed = true;
  253. // Flag is used only in Chakra, no need to use CUSTOM_CONFIG_FLAG
  254. if (CONFIG_FLAG(MemProtectHeap))
  255. {
  256. Assert(!wasUsed);
  257. if (FAILED(MemProtectHeapCreate(&memProtectHeapHandle, MemProtectHeapCreateFlags_ProtectCurrentStack)))
  258. {
  259. Assert(false);
  260. }
  261. return true;
  262. }
  263. return false;
  264. }
  265. void HeapAllocator::FinishMemProtectHeapCollect()
  266. {
  267. if (memProtectHeapHandle)
  268. {
  269. MemProtectHeapCollect(memProtectHeapHandle, MemProtectHeap_ForceFinishCollect);
  270. DebugOnly(MemProtectHeapSetDisableConcurrentThreadExitedCheck(memProtectHeapHandle));
  271. }
  272. }
  273. NoThrowNoMemProtectHeapAllocator NoThrowNoMemProtectHeapAllocator::Instance;
  274. char * NoThrowNoMemProtectHeapAllocator::AllocZero(size_t byteSize)
  275. {
  276. return HeapAllocator::GetNoMemProtectInstance()->NoThrowAllocZero(byteSize);
  277. }
  278. char * NoThrowNoMemProtectHeapAllocator::Alloc(size_t byteSize)
  279. {
  280. return HeapAllocator::GetNoMemProtectInstance()->NoThrowAlloc(byteSize);
  281. }
  282. void NoThrowNoMemProtectHeapAllocator::Free(void * buffer, size_t byteSize)
  283. {
  284. HeapAllocator::GetNoMemProtectInstance()->Free(buffer, byteSize);
  285. }
  286. #ifdef TRACK_ALLOC
  287. NoThrowNoMemProtectHeapAllocator * NoThrowNoMemProtectHeapAllocator::TrackAllocInfo(TrackAllocData const& data)
  288. {
  289. HeapAllocator::GetNoMemProtectInstance()->TrackAllocInfo(data);
  290. return this;
  291. }
  292. #endif // TRACK_ALLOC
  293. #ifdef TRACK_ALLOC
  294. void NoThrowNoMemProtectHeapAllocator::ClearTrackAllocInfo(TrackAllocData* data /*= NULL*/)
  295. {
  296. HeapAllocator::GetNoMemProtectInstance()->ClearTrackAllocInfo(data);
  297. }
  298. #endif // TRACK_ALLOC
  299. #endif
  300. HeapAllocator::HeapAllocator(bool useAllocMemProtect)
  301. : m_privateHeap(nullptr)
  302. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  303. , isUsed(false)
  304. , memProtectHeapHandle(nullptr)
  305. , allocMemProtect(useAllocMemProtect)
  306. #endif
  307. {
  308. if (CONFIG_FLAG(PrivateHeap))
  309. {
  310. this->InitPrivateHeap();
  311. }
  312. }
  313. HeapAllocator::~HeapAllocator()
  314. {
  315. #ifdef HEAP_TRACK_ALLOC
  316. bool hasFakeHeapLeak = false;
  317. auto fakeHeapLeak = [&]()
  318. {
  319. // REVIEW: Okay to use global flags?
  320. if (Js::Configuration::Global.flags.ForceMemoryLeak && !hasFakeHeapLeak)
  321. {
  322. AUTO_HANDLED_EXCEPTION_TYPE(ExceptionType_DisableCheck);
  323. struct FakeMemory { int f; };
  324. HeapNewStruct(FakeMemory);
  325. hasFakeHeapLeak = true;
  326. }
  327. };
  328. #ifdef LEAK_REPORT
  329. // REVIEW: Okay to use global flags?
  330. if (Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag))
  331. {
  332. fakeHeapLeak();
  333. LeakReport::StartSection(_u("Heap Leaks"));
  334. LeakReport::StartRedirectOutput();
  335. bool leaked = !HeapAllocator::CheckLeaks();
  336. LeakReport::EndRedirectOutput();
  337. LeakReport::EndSection();
  338. LeakReport::Print(_u("--------------------------------------------------------------------------------\n"));
  339. if (leaked)
  340. {
  341. LeakReport::Print(_u("Heap Leaked Object: %d bytes (%d objects)\n"),
  342. data.outstandingBytes, data.allocCount - data.deleteCount);
  343. }
  344. }
  345. #endif // LEAK_REPORT
  346. #ifdef CHECK_MEMORY_LEAK
  347. // REVIEW: Okay to use global flags?
  348. if (Js::Configuration::Global.flags.CheckMemoryLeak)
  349. {
  350. fakeHeapLeak();
  351. Output::CaptureStart();
  352. Output::Print(_u("-------------------------------------------------------------------------------------\n"));
  353. Output::Print(_u("Heap Leaks\n"));
  354. Output::Print(_u("-------------------------------------------------------------------------------------\n"));
  355. if (!HeapAllocator::CheckLeaks())
  356. {
  357. Output::Print(_u("-------------------------------------------------------------------------------------\n"));
  358. Output::Print(_u("Heap Leaked Object: %d bytes (%d objects)\n"),
  359. data.outstandingBytes, data.allocCount - data.deleteCount);
  360. char16 * buffer = Output::CaptureEnd();
  361. MemoryLeakCheck::AddLeakDump(buffer, data.outstandingBytes, data.allocCount - data.deleteCount);
  362. }
  363. else
  364. {
  365. free(Output::CaptureEnd());
  366. }
  367. }
  368. #endif // CHECK_MEMORY_LEAK
  369. #endif // HEAP_TRACK_ALLOC
  370. // destroy private heap after leak check
  371. if (CONFIG_FLAG(PrivateHeap))
  372. {
  373. this->DestroyPrivateHeap();
  374. }
  375. #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
  376. if (memProtectHeapHandle != nullptr)
  377. {
  378. MemProtectHeapDestroy(memProtectHeapHandle);
  379. }
  380. #endif // INTERNAL_MEM_PROTECT_HEAP_ALLOC
  381. }
  382. #ifdef HEAP_TRACK_ALLOC
  383. void
  384. HeapAllocatorData::LogAlloc(HeapAllocRecord * record, size_t requestedBytes, TrackAllocData const& data)
  385. {
  386. record->prev = nullptr;
  387. record->size = requestedBytes;
  388. record->data = this;
  389. record->next = head;
  390. record->allocId = allocCount;
  391. record->allocData = data;
  392. if (head != nullptr)
  393. {
  394. head->prev = record;
  395. }
  396. head = record;
  397. outstandingBytes += requestedBytes;
  398. allocCount++;
  399. #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
  400. #ifdef STACK_BACK_TRACE
  401. // REVIEW: Okay to use global flags?
  402. if (Js::Configuration::Global.flags.LeakStackTrace)
  403. {
  404. // Allocation done before the flags is parse doesn't get a stack trace
  405. record->stacktrace = StackBackTrace::Capture(&NoCheckHeapAllocator::Instance, 1, StackTraceDepth);
  406. }
  407. else
  408. {
  409. record->stacktrace = nullptr;
  410. }
  411. #endif
  412. #endif
  413. }
  414. void
  415. HeapAllocatorData::LogFree(HeapAllocRecord * record)
  416. {
  417. Assert(record->data == this);
  418. // This is an expensive check for double free
  419. #if 0
  420. HeapAllocRecord * curr = head;
  421. while (curr != nullptr)
  422. {
  423. if (curr == record)
  424. {
  425. break;
  426. }
  427. curr = curr->next;
  428. }
  429. Assert(curr != nullptr);
  430. #endif
  431. if (record->next != nullptr)
  432. {
  433. record->next->prev = record->prev;
  434. }
  435. if (record->prev == nullptr)
  436. {
  437. head = record->next;
  438. }
  439. else
  440. {
  441. record->prev->next = record->next;
  442. }
  443. deleteCount++;
  444. outstandingBytes -= record->size;
  445. #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
  446. #ifdef STACK_BACK_TRACE
  447. if (record->stacktrace != nullptr)
  448. {
  449. record->stacktrace->Delete(&NoCheckHeapAllocator::Instance);
  450. }
  451. #endif
  452. #endif
  453. }
  454. bool
  455. HeapAllocatorData::CheckLeaks()
  456. {
  457. bool needPause = false;
  458. if (allocCount != deleteCount)
  459. {
  460. needPause = true;
  461. HeapAllocRecord * current = head;
  462. while (current != nullptr)
  463. {
  464. Output::Print(_u("%S%s"), current->allocData.GetTypeInfo()->name(),
  465. current->allocData.GetCount() == (size_t)-1? _u("") : _u("[]"));
  466. Output::SkipToColumn(50);
  467. Output::Print(_u("- %p - %10d bytes\n"),
  468. ((char*)current) + ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT),
  469. current->size);
  470. #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
  471. #ifdef STACK_BACK_TRACE
  472. // REVIEW: Okay to use global flags?
  473. if (Js::Configuration::Global.flags.LeakStackTrace && current->stacktrace)
  474. {
  475. // Allocation done before the flags is parse doesn't get a stack trace
  476. Output::Print(_u(" Allocation Stack:\n"));
  477. current->stacktrace->Print();
  478. }
  479. #endif
  480. #endif
  481. current = current->next;
  482. }
  483. }
  484. else if (outstandingBytes != 0)
  485. {
  486. needPause = true;
  487. Output::Print(_u("Unbalanced new/delete size: %d\n"), outstandingBytes);
  488. }
  489. Output::Flush();
  490. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && !DBG
  491. // REVIEW: Okay to use global flags?
  492. if (needPause && Js::Configuration::Global.flags.Console)
  493. {
  494. //This is not defined for WinCE
  495. HANDLE handle = GetStdHandle( STD_INPUT_HANDLE );
  496. FlushConsoleInputBuffer(handle);
  497. Output::Print(_u("Press any key to continue...\n"));
  498. Output::Flush();
  499. WaitForSingleObject(handle, INFINITE);
  500. }
  501. #endif
  502. return allocCount == deleteCount && outstandingBytes == 0;
  503. }
  504. #endif
  505. #ifdef CHECK_MEMORY_LEAK
  506. MemoryLeakCheck::~MemoryLeakCheck()
  507. {
  508. if (head != nullptr)
  509. {
  510. if (enableOutput)
  511. {
  512. Output::Print(_u("FATAL ERROR: Memory Leak Detected\n"));
  513. }
  514. LeakRecord * current = head;
  515. do
  516. {
  517. if (enableOutput)
  518. {
  519. Output::PrintBuffer(current->dump, wcslen(current->dump));
  520. }
  521. LeakRecord * prev = current;
  522. current = current->next;
  523. free((void *)prev->dump);
  524. NoCheckHeapDelete(prev);
  525. }
  526. while (current != nullptr);
  527. if (enableOutput)
  528. {
  529. Output::Print(_u("-------------------------------------------------------------------------------------\n"));
  530. Output::Print(_u("Total leaked: %d bytes (%d objects)\n"), leakedBytes, leakedCount);
  531. Output::Flush();
  532. }
  533. #ifdef GENERATE_DUMP
  534. if (enableOutput)
  535. {
  536. Js::Throw::GenerateDump(Js::Configuration::Global.flags.DumpOnCrash, true, true);
  537. }
  538. #endif
  539. }
  540. }
  541. void
  542. MemoryLeakCheck::AddLeakDump(char16 const * dump, size_t bytes, size_t count)
  543. {
  544. AutoCriticalSection autocs(&leakCheck.cs);
  545. LeakRecord * record = NoCheckHeapNewStruct(LeakRecord);
  546. record->dump = dump;
  547. record->next = nullptr;
  548. if (leakCheck.tail == nullptr)
  549. {
  550. leakCheck.head = record;
  551. leakCheck.tail = record;
  552. }
  553. else
  554. {
  555. leakCheck.tail->next = record;
  556. leakCheck.tail = record;
  557. }
  558. leakCheck.leakedBytes += bytes;
  559. leakCheck.leakedCount += count;
  560. }
  561. #endif