| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "CommonMemoryPch.h"
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- // Not enabled in ChakraCore
- #include "MemProtectHeap.h"
- #endif
- // Initialization order
- // AB AutoSystemInfo
- // AD PerfCounter
- // AE PerfCounterSet
- // AM Output/Configuration
- // AN MemProtectHeap
- // AP DbgHelpSymbolManager
- // AQ CFGLogger
- // AR LeakReport
- // AS JavascriptDispatch/RecyclerObjectDumper
- // AT HeapAllocator/RecyclerHeuristic
- // AU RecyclerWriteBarrierManager
- #pragma warning(disable:4075) // initializers put in unrecognized initialization area on purpose
- #pragma init_seg(".CRT$XCAT")
- #ifdef HEAP_TRACK_ALLOC
- CriticalSection HeapAllocator::cs;
- #endif
- #ifdef CHECK_MEMORY_LEAK
- MemoryLeakCheck MemoryLeakCheck::leakCheck;
- #endif
- HeapAllocator HeapAllocator::Instance;
- NoThrowHeapAllocator NoThrowHeapAllocator::Instance;
- NoCheckHeapAllocator NoCheckHeapAllocator::Instance;
- HANDLE NoCheckHeapAllocator::processHeap = nullptr;
- template <bool noThrow>
- char * HeapAllocator::AllocT(size_t byteSize)
- {
- #ifdef HEAP_TRACK_ALLOC
- size_t requestedBytes = byteSize;
- byteSize = AllocSizeMath::Add(requestedBytes, ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT));
- TrackAllocData allocData;
- ClearTrackAllocInfo(&allocData);
- #elif defined(HEAP_PERF_COUNTERS)
- size_t requestedBytes = byteSize;
- byteSize = AllocSizeMath::Add(requestedBytes, ::Math::Align<size_t>(sizeof(size_t), MEMORY_ALLOCATION_ALIGNMENT));
- #endif
- if (noThrow)
- {
- FAULTINJECT_MEMORY_NOTHROW(_u("Heap"), byteSize);
- }
- else
- {
- FAULTINJECT_MEMORY_THROW(_u("Heap"), byteSize);
- }
- char * buffer;
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- if (DoUseMemProtectHeap())
- {
- void * memory = MemProtectHeapRootAlloc(memProtectHeapHandle, byteSize);
- if (memory == nullptr)
- {
- if (noThrow)
- {
- return nullptr;
- }
- Js::Throw::OutOfMemory();
- }
- buffer = (char *)memory;
- }
- else
- #endif
- {
- if (CONFIG_FLAG(PrivateHeap))
- {
- buffer = (char *)HeapAlloc(GetPrivateHeap(), 0, byteSize);
- }
- else
- {
- buffer = (char *)malloc(byteSize);
- }
- }
- if (!noThrow && buffer == nullptr)
- {
- Js::Throw::OutOfMemory();
- }
- #if defined(HEAP_TRACK_ALLOC) || defined(HEAP_PERF_COUNTERS)
- if (!noThrow || buffer != nullptr)
- {
- #ifdef HEAP_TRACK_ALLOC
- cs.Enter();
- data.LogAlloc((HeapAllocRecord *)buffer, requestedBytes, allocData);
- cs.Leave();
- buffer += ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT);
- #else
- *(size_t *)buffer = requestedBytes;
- buffer += ::Math::Align<size_t>(sizeof(size_t), MEMORY_ALLOCATION_ALIGNMENT);
- #endif
- HEAP_PERF_COUNTER_INC(LiveObject);
- HEAP_PERF_COUNTER_ADD(LiveObjectSize, requestedBytes);
- }
- #endif
- return buffer;
- }
- template char * HeapAllocator::AllocT<true>(size_t byteSize);
- template char * HeapAllocator::AllocT<false>(size_t byteSize);
- void HeapAllocator::Free(void * buffer, size_t byteSize)
- {
- #ifdef HEAP_TRACK_ALLOC
- if (buffer != nullptr)
- {
- HeapAllocRecord * record = (HeapAllocRecord *)(((char *)buffer) - ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT));
- Assert(byteSize == (size_t)-1 || record->size == byteSize);
- HEAP_PERF_COUNTER_DEC(LiveObject);
- HEAP_PERF_COUNTER_SUB(LiveObjectSize, record->size);
- cs.Enter();
- data.LogFree(record);
- cs.Leave();
- buffer = record;
- #if DBG
- memset(buffer, DbgMemFill, record->size + ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT));
- #endif
- }
- #elif defined(HEAP_PERF_COUNTERS)
- if (buffer != nullptr)
- {
- HEAP_PERF_COUNTER_DEC(LiveObject);
- size_t * allocSize = (size_t *)(((char *)buffer) - ::Math::Align<size_t>(sizeof(size_t), MEMORY_ALLOCATION_ALIGNMENT));
- HEAP_PERF_COUNTER_SUB(LiveObjectSize, *allocSize);
- buffer = allocSize;
- }
- #endif
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- if (DoUseMemProtectHeap())
- {
- HRESULT hr = MemProtectHeapUnrootAndZero(memProtectHeapHandle, buffer);
- Assert(SUCCEEDED(hr));
- return;
- }
- #endif
- if (CONFIG_FLAG(PrivateHeap))
- {
- HeapFree(GetPrivateHeap(), 0, buffer);
- }
- else
- {
- free(buffer);
- }
- }
- void HeapAllocator::InitPrivateHeap()
- {
- if (this->m_privateHeap == nullptr)
- {
- this->m_privateHeap = HeapCreate(0, 0, 0); // no options, default initial size, no max size
- }
- }
- void HeapAllocator::DestroyPrivateHeap()
- {
- if (this->m_privateHeap != nullptr)
- {
- // xplat-todo: PAL no HeapDestroy?
- #ifdef _WIN32
- BOOL success = HeapDestroy(this->m_privateHeap);
- Assert(success);
- #endif
- this->m_privateHeap = nullptr;
- }
- }
- HANDLE HeapAllocator::GetPrivateHeap()
- {
- InitPrivateHeap(); // will initialize PrivateHeap if not already initialized
- return this->m_privateHeap;
- }
- #ifdef TRACK_ALLOC
- #ifdef HEAP_TRACK_ALLOC
- THREAD_LOCAL TrackAllocData HeapAllocator::nextAllocData;
- #endif
- HeapAllocator * HeapAllocator::TrackAllocInfo(TrackAllocData const& data)
- {
- #ifdef HEAP_TRACK_ALLOC
- Assert(nextAllocData.IsEmpty());
- nextAllocData = data;
- #endif
- return this;
- }
- void HeapAllocator::ClearTrackAllocInfo(TrackAllocData* data/* = NULL*/)
- {
- #ifdef HEAP_TRACK_ALLOC
- Assert(!nextAllocData.IsEmpty());
- if (data)
- {
- *data = nextAllocData;
- }
- nextAllocData.Clear();
- #endif
- }
- #endif
- #ifdef HEAP_TRACK_ALLOC
- //static
- bool HeapAllocator::CheckLeaks()
- {
- return Instance.data.CheckLeaks();
- }
- #endif // HEAP_TRACK_ALLOC
- char * NoThrowHeapAllocator::AllocZero(size_t byteSize)
- {
- return HeapAllocator::Instance.NoThrowAllocZero(byteSize);
- }
- char * NoThrowHeapAllocator::Alloc(size_t byteSize)
- {
- return HeapAllocator::Instance.NoThrowAlloc(byteSize);
- }
- void NoThrowHeapAllocator::Free(void * buffer, size_t byteSize)
- {
- HeapAllocator::Instance.Free(buffer, byteSize);
- }
- #ifdef TRACK_ALLOC
- NoThrowHeapAllocator * NoThrowHeapAllocator::TrackAllocInfo(TrackAllocData const& data)
- {
- HeapAllocator::Instance.TrackAllocInfo(data);
- return this;
- }
- #endif // TRACK_ALLOC
- #ifdef TRACK_ALLOC
- void NoThrowHeapAllocator::ClearTrackAllocInfo(TrackAllocData* data /*= NULL*/)
- {
- HeapAllocator::Instance.ClearTrackAllocInfo(data);
- }
- #endif // TRACK_ALLOC
- HeapAllocator * HeapAllocator::GetNoMemProtectInstance()
- {
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- // Used only in Chakra, no need to use CUSTOM_CONFIG_FLAG
- if (CONFIG_FLAG(MemProtectHeap))
- {
- return &NoMemProtectInstance;
- }
- #endif
- return &Instance;
- }
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- HeapAllocator HeapAllocator::NoMemProtectInstance(false);
- bool HeapAllocator::DoUseMemProtectHeap()
- {
- if (!allocMemProtect)
- {
- return false;
- }
- if (memProtectHeapHandle != nullptr)
- {
- return true;
- }
- DebugOnly(bool wasUsed = isUsed);
- isUsed = true;
- // Flag is used only in Chakra, no need to use CUSTOM_CONFIG_FLAG
- if (CONFIG_FLAG(MemProtectHeap))
- {
- Assert(!wasUsed);
- if (FAILED(MemProtectHeapCreate(&memProtectHeapHandle, MemProtectHeapCreateFlags_ProtectCurrentStack)))
- {
- Assert(false);
- }
- return true;
- }
- return false;
- }
- void HeapAllocator::FinishMemProtectHeapCollect()
- {
- if (memProtectHeapHandle)
- {
- MemProtectHeapCollect(memProtectHeapHandle, MemProtectHeap_ForceFinishCollect);
- DebugOnly(MemProtectHeapSetDisableConcurrentThreadExitedCheck(memProtectHeapHandle));
- }
- }
- NoThrowNoMemProtectHeapAllocator NoThrowNoMemProtectHeapAllocator::Instance;
- char * NoThrowNoMemProtectHeapAllocator::AllocZero(size_t byteSize)
- {
- return HeapAllocator::GetNoMemProtectInstance()->NoThrowAllocZero(byteSize);
- }
- char * NoThrowNoMemProtectHeapAllocator::Alloc(size_t byteSize)
- {
- return HeapAllocator::GetNoMemProtectInstance()->NoThrowAlloc(byteSize);
- }
- void NoThrowNoMemProtectHeapAllocator::Free(void * buffer, size_t byteSize)
- {
- HeapAllocator::GetNoMemProtectInstance()->Free(buffer, byteSize);
- }
- #ifdef TRACK_ALLOC
- NoThrowNoMemProtectHeapAllocator * NoThrowNoMemProtectHeapAllocator::TrackAllocInfo(TrackAllocData const& data)
- {
- HeapAllocator::GetNoMemProtectInstance()->TrackAllocInfo(data);
- return this;
- }
- #endif // TRACK_ALLOC
- #ifdef TRACK_ALLOC
- void NoThrowNoMemProtectHeapAllocator::ClearTrackAllocInfo(TrackAllocData* data /*= NULL*/)
- {
- HeapAllocator::GetNoMemProtectInstance()->ClearTrackAllocInfo(data);
- }
- #endif // TRACK_ALLOC
- #endif
- HeapAllocator::HeapAllocator(bool useAllocMemProtect)
- : m_privateHeap(nullptr)
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- , isUsed(false)
- , memProtectHeapHandle(nullptr)
- , allocMemProtect(useAllocMemProtect)
- #endif
- {
- if (CONFIG_FLAG(PrivateHeap))
- {
- this->InitPrivateHeap();
- }
- }
- HeapAllocator::~HeapAllocator()
- {
- #ifdef HEAP_TRACK_ALLOC
- bool hasFakeHeapLeak = false;
- auto fakeHeapLeak = [&]()
- {
- // REVIEW: Okay to use global flags?
- if (Js::Configuration::Global.flags.ForceMemoryLeak && !hasFakeHeapLeak)
- {
- AUTO_HANDLED_EXCEPTION_TYPE(ExceptionType_DisableCheck);
- struct FakeMemory { int f; };
- HeapNewStruct(FakeMemory);
- hasFakeHeapLeak = true;
- }
- };
- #ifdef LEAK_REPORT
- // REVIEW: Okay to use global flags?
- if (Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag))
- {
- fakeHeapLeak();
- LeakReport::StartSection(_u("Heap Leaks"));
- LeakReport::StartRedirectOutput();
- bool leaked = !HeapAllocator::CheckLeaks();
- LeakReport::EndRedirectOutput();
- LeakReport::EndSection();
- LeakReport::Print(_u("--------------------------------------------------------------------------------\n"));
- if (leaked)
- {
- LeakReport::Print(_u("Heap Leaked Object: %d bytes (%d objects)\n"),
- data.outstandingBytes, data.allocCount - data.deleteCount);
- }
- }
- #endif // LEAK_REPORT
- #ifdef CHECK_MEMORY_LEAK
- // REVIEW: Okay to use global flags?
- if (Js::Configuration::Global.flags.CheckMemoryLeak)
- {
- fakeHeapLeak();
- Output::CaptureStart();
- Output::Print(_u("-------------------------------------------------------------------------------------\n"));
- Output::Print(_u("Heap Leaks\n"));
- Output::Print(_u("-------------------------------------------------------------------------------------\n"));
- if (!HeapAllocator::CheckLeaks())
- {
- Output::Print(_u("-------------------------------------------------------------------------------------\n"));
- Output::Print(_u("Heap Leaked Object: %d bytes (%d objects)\n"),
- data.outstandingBytes, data.allocCount - data.deleteCount);
- char16 * buffer = Output::CaptureEnd();
- MemoryLeakCheck::AddLeakDump(buffer, data.outstandingBytes, data.allocCount - data.deleteCount);
- }
- else
- {
- free(Output::CaptureEnd());
- }
- }
- #endif // CHECK_MEMORY_LEAK
- #endif // HEAP_TRACK_ALLOC
- // destroy private heap after leak check
- if (CONFIG_FLAG(PrivateHeap))
- {
- this->DestroyPrivateHeap();
- }
- #ifdef INTERNAL_MEM_PROTECT_HEAP_ALLOC
- if (memProtectHeapHandle != nullptr)
- {
- MemProtectHeapDestroy(memProtectHeapHandle);
- }
- #endif // INTERNAL_MEM_PROTECT_HEAP_ALLOC
- }
- #ifdef HEAP_TRACK_ALLOC
- void
- HeapAllocatorData::LogAlloc(HeapAllocRecord * record, size_t requestedBytes, TrackAllocData const& data)
- {
- record->prev = nullptr;
- record->size = requestedBytes;
- record->data = this;
- record->next = head;
- record->allocId = allocCount;
- record->allocData = data;
- if (head != nullptr)
- {
- head->prev = record;
- }
- head = record;
- outstandingBytes += requestedBytes;
- allocCount++;
- #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
- #ifdef STACK_BACK_TRACE
- // REVIEW: Okay to use global flags?
- if (Js::Configuration::Global.flags.LeakStackTrace)
- {
- // Allocation done before the flags is parse doesn't get a stack trace
- record->stacktrace = StackBackTrace::Capture(&NoCheckHeapAllocator::Instance, 1, StackTraceDepth);
- }
- else
- {
- record->stacktrace = nullptr;
- }
- #endif
- #endif
- }
- void
- HeapAllocatorData::LogFree(HeapAllocRecord * record)
- {
- Assert(record->data == this);
- // This is an expensive check for double free
- #if 0
- HeapAllocRecord * curr = head;
- while (curr != nullptr)
- {
- if (curr == record)
- {
- break;
- }
- curr = curr->next;
- }
- Assert(curr != nullptr);
- #endif
- if (record->next != nullptr)
- {
- record->next->prev = record->prev;
- }
- if (record->prev == nullptr)
- {
- head = record->next;
- }
- else
- {
- record->prev->next = record->next;
- }
- deleteCount++;
- outstandingBytes -= record->size;
- #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
- #ifdef STACK_BACK_TRACE
- if (record->stacktrace != nullptr)
- {
- record->stacktrace->Delete(&NoCheckHeapAllocator::Instance);
- }
- #endif
- #endif
- }
- bool
- HeapAllocatorData::CheckLeaks()
- {
- bool needPause = false;
- if (allocCount != deleteCount)
- {
- needPause = true;
- HeapAllocRecord * current = head;
- while (current != nullptr)
- {
- Output::Print(_u("%S%s"), current->allocData.GetTypeInfo()->name(),
- current->allocData.GetCount() == (size_t)-1? _u("") : _u("[]"));
- Output::SkipToColumn(50);
- Output::Print(_u("- %p - %10d bytes\n"),
- ((char*)current) + ::Math::Align<size_t>(sizeof(HeapAllocRecord), MEMORY_ALLOCATION_ALIGNMENT),
- current->size);
- #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
- #ifdef STACK_BACK_TRACE
- // REVIEW: Okay to use global flags?
- if (Js::Configuration::Global.flags.LeakStackTrace && current->stacktrace)
- {
- // Allocation done before the flags is parse doesn't get a stack trace
- Output::Print(_u(" Allocation Stack:\n"));
- current->stacktrace->Print();
- }
- #endif
- #endif
- current = current->next;
- }
- }
- else if (outstandingBytes != 0)
- {
- needPause = true;
- Output::Print(_u("Unbalanced new/delete size: %d\n"), outstandingBytes);
- }
- Output::Flush();
- #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && !DBG
- // REVIEW: Okay to use global flags?
- if (needPause && Js::Configuration::Global.flags.Console)
- {
- //This is not defined for WinCE
- HANDLE handle = GetStdHandle( STD_INPUT_HANDLE );
- FlushConsoleInputBuffer(handle);
- Output::Print(_u("Press any key to continue...\n"));
- Output::Flush();
- WaitForSingleObject(handle, INFINITE);
- }
- #endif
- return allocCount == deleteCount && outstandingBytes == 0;
- }
- #endif
- #ifdef CHECK_MEMORY_LEAK
- MemoryLeakCheck::~MemoryLeakCheck()
- {
- if (head != nullptr)
- {
- if (enableOutput)
- {
- Output::Print(_u("FATAL ERROR: Memory Leak Detected\n"));
- }
- LeakRecord * current = head;
- do
- {
- if (enableOutput)
- {
- Output::PrintBuffer(current->dump, wcslen(current->dump));
- }
- LeakRecord * prev = current;
- current = current->next;
- free((void *)prev->dump);
- NoCheckHeapDelete(prev);
- }
- while (current != nullptr);
- if (enableOutput)
- {
- Output::Print(_u("-------------------------------------------------------------------------------------\n"));
- Output::Print(_u("Total leaked: %d bytes (%d objects)\n"), leakedBytes, leakedCount);
- Output::Flush();
- }
- #ifdef GENERATE_DUMP
- if (enableOutput)
- {
- Js::Throw::GenerateDump(Js::Configuration::Global.flags.DumpOnCrash, true, true);
- }
- #endif
- }
- }
- void
- MemoryLeakCheck::AddLeakDump(char16 const * dump, size_t bytes, size_t count)
- {
- AutoCriticalSection autocs(&leakCheck.cs);
- LeakRecord * record = NoCheckHeapNewStruct(LeakRecord);
- record->dump = dump;
- record->next = nullptr;
- if (leakCheck.tail == nullptr)
- {
- leakCheck.head = record;
- leakCheck.tail = record;
- }
- else
- {
- leakCheck.tail->next = record;
- leakCheck.tail = record;
- }
- leakCheck.leakedBytes += bytes;
- leakCheck.leakedCount += count;
- }
- #endif
|