blob: bb0e77712461bde33900295c5cf169b0aed3c519 [file] [edit]
/*
* Copyright (c) 2005, 2007 Google Inc. All rights reserved.
* Copyright (C) 2005-2018 Apple Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <wtf/FastMalloc.h>
#include <bmalloc/bmalloc.h>
#include <string.h>
#include <string_view>
#include <wtf/Atomics.h>
#include <wtf/CheckedArithmetic.h>
#include <wtf/PageBlock.h>
#include <wtf/SequesteredImmortalHeap.h>
#include <wtf/SequesteredMalloc.h>
#if OS(WINDOWS)
#include <windows.h>
#include <psapi.h>
#else
#if HAVE(RESOURCE_H)
#include <sys/resource.h>
#endif // HAVE(RESOURCE_H)
#endif
#if OS(HAIKU)
#include <OS.h>
#endif
#if ENABLE(MALLOC_HEAP_BREAKDOWN)
#include <wtf/Atomics.h>
#include <wtf/HashMap.h>
#include <wtf/Lock.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/SetForScope.h>
#include <wtf/StackShot.h>
#if PLATFORM(COCOA)
#include <notify.h>
#endif
#endif
#if defined(PAS_PRESERVE_MOST)
static_assert(std::string_view { _STRINGIFY(PRESERVE_MOST) } == std::string_view { _STRINGIFY(PAS_PRESERVE_MOST) },
"PRESERVE_MOST and PAS_PRESERVE_MOST disagree; this is catastrophic for performance");
#endif
namespace WTF {
#if !defined(NDEBUG)
namespace {
// We do not use std::numeric_limits<size_t>::max() here due to the edge case in VC++.
// https://bugs.webkit.org/show_bug.cgi?id=173720
static size_t maxSingleAllocationSize = SIZE_MAX;
};
void fastSetMaxSingleAllocationSize(size_t size)
{
maxSingleAllocationSize = size;
}
#if ASSERT_ENABLED
#define ASSERT_IS_WITHIN_LIMIT(size) do { \
size_t size__ = (size); \
ASSERT_WITH_MESSAGE((size__) <= maxSingleAllocationSize, "Requested size (%zu) exceeds max single allocation size set for testing (%zu)", (size__), maxSingleAllocationSize); \
} while (false)
#else
#define ASSERT_IS_WITHIN_LIMIT(size)
#endif // ASSERT_ENABLED
#define FAIL_IF_EXCEEDS_LIMIT(size) do { \
if ((size) > maxSingleAllocationSize) [[unlikely]] \
return nullptr; \
} while (false)
#else // !defined(NDEBUG)
#define ASSERT_IS_WITHIN_LIMIT(size)
#define FAIL_IF_EXCEEDS_LIMIT(size)
#endif // !defined(NDEBUG)
WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
char* fastStrDup(const char* src)
{
size_t len = strlen(src) + 1;
char* dup = static_cast<char*>(fastMalloc(len));
memcpy(dup, src, len);
return dup;
}
void* fastMemDup(const void* mem, size_t bytes)
{
if (!mem || !bytes)
return nullptr;
void* result = fastMalloc(bytes);
memcpy(result, mem, bytes);
return result;
}
char* fastCompactStrDup(const char* src)
{
size_t len = strlen(src) + 1;
char* dup = static_cast<char*>(fastCompactMalloc(len));
memcpy(dup, src, len);
return dup;
}
void* fastCompactMemDup(const void* mem, size_t bytes)
{
if (!mem || !bytes)
return nullptr;
void* result = fastCompactMalloc(bytes);
memcpy(result, mem, bytes);
return result;
}
WTF_ALLOW_UNSAFE_BUFFER_USAGE_END
} // namespace WTF
namespace WTF {
#define TRACK_MALLOC_CALLSTACK 0
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
static ThreadSpecificKey avoidRecordingCountKey { InvalidThreadSpecificKey };
class AvoidRecordingScope {
public:
AvoidRecordingScope();
~AvoidRecordingScope();
static uintptr_t avoidRecordingCount()
{
return std::bit_cast<uintptr_t>(threadSpecificGet(avoidRecordingCountKey));
}
};
AvoidRecordingScope::AvoidRecordingScope()
{
static std::once_flag onceKey;
std::call_once(onceKey, [] {
// The value stored in TLS is initially 0.
threadSpecificKeyCreate(&avoidRecordingCountKey, [](void*) { });
});
threadSpecificSet(avoidRecordingCountKey, std::bit_cast<void*>(avoidRecordingCount() + 1));
}
AvoidRecordingScope::~AvoidRecordingScope()
{
threadSpecificSet(avoidRecordingCountKey, std::bit_cast<void*>(avoidRecordingCount() - 1));
}
class MallocCallTracker {
public:
MallocCallTracker();
void recordMalloc(void*, size_t);
void recordRealloc(void* oldAddress, void* newAddress, size_t);
void recordFree(void*);
void dumpStats();
static MallocCallTracker& singleton();
private:
struct MallocSiteData {
StackShot stack;
size_t size;
MallocSiteData(size_t stackSize, size_t allocationSize)
: stack(stackSize)
, size(allocationSize)
{
}
};
Lock m_lock;
HashMap<void*, std::unique_ptr<MallocSiteData>> m_addressMallocSiteData WTF_GUARDED_BY_LOCK(m_lock);
};
MallocCallTracker& MallocCallTracker::singleton()
{
AvoidRecordingScope avoidRecording;
static NeverDestroyed<MallocCallTracker> tracker;
return tracker;
}
MallocCallTracker::MallocCallTracker()
{
int token;
notify_register_dispatch("com.apple.WebKit.dumpUntrackedMallocs", &token, mainDispatchQueueSingleton(), ^(int) {
MallocCallTracker::singleton().dumpStats();
});
}
void MallocCallTracker::recordMalloc(void* address, size_t allocationSize)
{
AvoidRecordingScope avoidRecording;
// Intentionally using std::make_unique not to use FastMalloc for data structure tracking FastMalloc.
const size_t stackSize = 10;
auto siteData = std::make_unique<MallocSiteData>(stackSize, allocationSize);
Locker locker { m_lock };
auto addResult = m_addressMallocSiteData.add(address, WTF::move(siteData));
UNUSED_PARAM(addResult);
}
void MallocCallTracker::recordRealloc(void* oldAddress, void* newAddress, size_t newSize)
{
AvoidRecordingScope avoidRecording;
Locker locker { m_lock };
auto it = m_addressMallocSiteData.find(oldAddress);
if (it == m_addressMallocSiteData.end()) {
ASSERT_NOT_REACHED();
return;
}
it->value->size = newSize;
if (oldAddress != newAddress) {
auto value = WTF::move(it->value);
m_addressMallocSiteData.remove(it);
auto addResult = m_addressMallocSiteData.add(newAddress, WTF::move(value));
ASSERT_UNUSED(addResult, addResult.isNewEntry);
}
}
void MallocCallTracker::recordFree(void* address)
{
AvoidRecordingScope avoidRecording;
Locker locker { m_lock };
bool removed = m_addressMallocSiteData.remove(address);
UNUSED_PARAM(removed);
}
void MallocCallTracker::dumpStats()
{
AvoidRecordingScope avoidRecording;
{
Locker locker { m_lock };
// Build a hash of stack to address vector
struct MallocSiteTotals {
Vector<MallocSiteData*> siteData;
size_t count { 0 };
size_t totalSize { 0 };
};
size_t totalUntrackedSize = 0;
size_t totalUntrackedCount = 0;
HashMap<unsigned, std::unique_ptr<MallocSiteTotals>> callSiteToMallocData;
for (const auto& it : m_addressMallocSiteData) {
auto result = callSiteToMallocData.ensure(it.value->stack.hash(), [] () {
// Intentionally using std::make_unique not to use FastMalloc for data structure tracking FastMalloc.
return std::make_unique<MallocSiteTotals>();
});
auto& siteTotal = result.iterator->value;
siteTotal->siteData.append(it.value.get());
++siteTotal->count;
siteTotal->totalSize += it.value->size;
totalUntrackedSize += it.value->size;
++totalUntrackedCount;
}
Vector<unsigned> stackHashes;
auto stackKeys = callSiteToMallocData.keys();
for (auto key : stackKeys)
stackHashes.append(key);
// Sort by reverse total size.
std::ranges::sort(stackHashes, [&] (unsigned a, unsigned b) {
const auto& aSiteTotals = callSiteToMallocData.get(a);
const auto& bSiteTotals = callSiteToMallocData.get(b);
return aSiteTotals->totalSize > bSiteTotals->totalSize;
});
WTFLogAlways("Total untracked bytes: %lu (%lu allocations)\n", totalUntrackedSize, totalUntrackedCount);
const size_t numStacksToDump = 100;
for (size_t i = 0; i < std::min(numStacksToDump, stackHashes.size()); ++i) {
const auto& mallocDataForStack = callSiteToMallocData.get(stackHashes[i]);
WTFLogAlways("Total allocation size: %lu (%lu allocations)\n", mallocDataForStack->totalSize, mallocDataForStack->count);
// FIXME: Add a way to remove some entries in StackShot in a programable way.
// https://bugs.webkit.org/show_bug.cgi?id=205701
const size_t framesToSkip = 6;
WTFPrintBacktrace(mallocDataForStack->siteData[0]->stack.span().subspan(framesToSkip));
WTFLogAlways("\n");
}
}
}
void fastMallocDumpMallocStats()
{
MallocCallTracker::singleton().dumpStats();
}
#else
void fastMallocDumpMallocStats()
{
}
#endif
bool isFastMallocEnabled()
{
return bmalloc::api::isEnabled();
}
PRESERVE_MOST void* fastMalloc(size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::malloc(size, bmalloc::CompactAllocationMode::NonCompact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
void* fastZeroedMalloc(size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::zeroedMalloc(size, bmalloc::CompactAllocationMode::NonCompact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
TryMallocReturnValue tryFastZeroedMalloc(size_t size)
{
FAIL_IF_EXCEEDS_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryZeroedMalloc(size, bmalloc::CompactAllocationMode::NonCompact);
BPROFILE_TRY_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
void* fastCalloc(size_t numElements, size_t elementSize)
{
ASSERT_IS_WITHIN_LIMIT(numElements * elementSize);
Checked<size_t> checkedSize = elementSize;
checkedSize *= numElements;
void* result = fastZeroedMalloc(checkedSize);
if (!result)
CRASH();
BPROFILE_ALLOCATION(NON_JS_CELL, result, checkedSize);
return result;
}
void* fastRealloc(void* object, size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::realloc(object, size, bmalloc::CompactAllocationMode::NonCompact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordRealloc(object, result, size);
#endif
BPROFILE_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
PRESERVE_MOST void fastFree(void* object)
{
bmalloc::api::free(object);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordFree(object);
#endif
}
void fastFreeCallback(void* object)
{
fastFree(object);
}
size_t fastMallocSize(const void* p)
{
#if BENABLE(MALLOC_SIZE)
return bmalloc::api::mallocSize(p);
#else
// FIXME: This is incorrect; best fix is probably to remove this function.
// Caller currently are all using this for assertion, not to actually check
// the size of the allocation, so maybe we can come up with something for that.
UNUSED_PARAM(p);
return 1;
#endif
}
size_t fastMallocGoodSize(size_t size)
{
#if BENABLE(MALLOC_GOOD_SIZE)
return bmalloc::api::mallocGoodSize(size);
#else
return size;
#endif
}
PRESERVE_MOST void* fastAlignedMalloc(size_t alignment, size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::memalign(alignment, size, bmalloc::CompactAllocationMode::NonCompact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
PRESERVE_MOST void* tryFastAlignedMalloc(size_t alignment, size_t size)
{
FAIL_IF_EXCEEDS_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryMemalign(alignment, size, bmalloc::CompactAllocationMode::NonCompact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_TRY_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
PRESERVE_MOST TryMallocReturnValue tryFastMalloc(size_t size)
{
FAIL_IF_EXCEEDS_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryMalloc(size, bmalloc::CompactAllocationMode::NonCompact);
BPROFILE_TRY_ALLOCATION(NON_JS_CELL, result, size);
return result;
}
TryMallocReturnValue tryFastCalloc(size_t numElements, size_t elementSize)
{
FAIL_IF_EXCEEDS_LIMIT(numElements * elementSize);
CheckedSize checkedSize = elementSize;
checkedSize *= numElements;
if (checkedSize.hasOverflowed())
return nullptr;
return tryFastZeroedMalloc(checkedSize);
}
TryMallocReturnValue tryFastRealloc(void* object, size_t newSize)
{
FAIL_IF_EXCEEDS_LIMIT(newSize);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryRealloc(object, newSize, bmalloc::CompactAllocationMode::NonCompact);
BPROFILE_TRY_ALLOCATION(NON_JS_CELL, result, newSize);
return result;
}
PRESERVE_MOST void* fastCompactMalloc(size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::malloc(size, bmalloc::CompactAllocationMode::Compact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(COMPACTIBLE, result, size);
return result;
}
void* fastCompactZeroedMalloc(size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::zeroedMalloc(size, bmalloc::CompactAllocationMode::Compact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(COMPACTIBLE, result, size);
return result;
}
TryMallocReturnValue tryFastCompactZeroedMalloc(size_t size)
{
FAIL_IF_EXCEEDS_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
return bmalloc::api::tryZeroedMalloc(size, bmalloc::CompactAllocationMode::Compact);
}
void* fastCompactCalloc(size_t numElements, size_t elementSize)
{
ASSERT_IS_WITHIN_LIMIT(numElements * elementSize);
Checked<size_t> checkedSize = elementSize;
checkedSize *= numElements;
void* result = fastZeroedMalloc(checkedSize);
if (!result)
CRASH();
BPROFILE_ALLOCATION(COMPACTIBLE, result, elementSize);
return result;
}
void* fastCompactRealloc(void* object, size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::realloc(object, size, bmalloc::CompactAllocationMode::Compact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordRealloc(object, result, size);
#endif
BPROFILE_ALLOCATION(COMPACTIBLE, result, size);
return result;
}
PRESERVE_MOST void* fastCompactAlignedMalloc(size_t alignment, size_t size)
{
ASSERT_IS_WITHIN_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::memalign(alignment, size, bmalloc::CompactAllocationMode::Compact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(COMPACTIBLE, result, size);
return result;
}
PRESERVE_MOST void* tryFastCompactAlignedMalloc(size_t alignment, size_t size)
{
FAIL_IF_EXCEEDS_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryMemalign(alignment, size, bmalloc::CompactAllocationMode::Compact);
#if ENABLE(MALLOC_HEAP_BREAKDOWN) && TRACK_MALLOC_CALLSTACK
if (!AvoidRecordingScope::avoidRecordingCount())
MallocCallTracker::singleton().recordMalloc(result, size);
#endif
BPROFILE_ALLOCATION(COMPACTIBLE, result, size);
return result;
}
PRESERVE_MOST TryMallocReturnValue tryFastCompactMalloc(size_t size)
{
FAIL_IF_EXCEEDS_LIMIT(size);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryMalloc(size, bmalloc::CompactAllocationMode::Compact);
BPROFILE_ALLOCATION(COMPACTIBLE, result, size);
return result;
}
TryMallocReturnValue tryFastCompactCalloc(size_t numElements, size_t elementSize)
{
FAIL_IF_EXCEEDS_LIMIT(numElements * elementSize);
CheckedSize checkedSize = elementSize;
checkedSize *= numElements;
if (checkedSize.hasOverflowed())
return nullptr;
return tryFastCompactZeroedMalloc(checkedSize);
}
TryMallocReturnValue tryFastCompactRealloc(void* object, size_t newSize)
{
FAIL_IF_EXCEEDS_LIMIT(newSize);
assertMallocRestrictionForCurrentThreadScope();
void* result = bmalloc::api::tryRealloc(object, newSize, bmalloc::CompactAllocationMode::Compact);
BPROFILE_ALLOCATION(COMPACTIBLE, result, newSize);
return result;
}
void releaseFastMallocFreeMemoryForThisThread()
{
bmalloc::api::scavengeThisThread();
}
void releaseFastMallocFreeMemory()
{
bmalloc::api::scavenge();
#if USE(PROTECTED_JIT)
if (isSequesteredArenaMallocEnabled())
SequesteredImmortalHeap::instance().reclaimIdleGranulesOnce();
#endif
}
FastMallocStatistics fastMallocStatistics()
{
// FIXME: Can bmalloc itself report the stats instead of relying on the OS?
FastMallocStatistics statistics;
statistics.freeListBytes = 0;
statistics.reservedVMBytes = 0;
#if OS(WINDOWS)
PROCESS_MEMORY_COUNTERS resourceUsage;
GetProcessMemoryInfo(GetCurrentProcess(), &resourceUsage, sizeof(resourceUsage));
statistics.committedVMBytes = resourceUsage.PeakWorkingSetSize;
#elif OS(HAIKU)
ssize_t cookie = nullptr;
statistics.committedVMBytes = 0;
area_info info;
while (get_next_area_info(B_CURRENT_TEAM, &cookie, &info) == B_OK)
statistics.committedVMBytes += info.ram_size;
#elif HAVE(RESOURCE_H)
struct rusage resourceUsage;
getrusage(RUSAGE_SELF, &resourceUsage);
#if OS(DARWIN)
statistics.committedVMBytes = resourceUsage.ru_maxrss;
#else
statistics.committedVMBytes = resourceUsage.ru_maxrss * 1024;
#endif // OS(DARWIN)
#endif // OS(WINDOWS)
return statistics;
}
void fastCommitAlignedMemory(void* ptr, size_t size)
{
bmalloc::api::commitAlignedPhysical(ptr, size);
}
void fastDecommitAlignedMemory(void* ptr, size_t size)
{
bmalloc::api::decommitAlignedPhysical(ptr, size);
}
void fastEnableMiniMode(bool forceMiniMode)
{
bmalloc::api::enableMiniMode(forceMiniMode);
}
void fastDisableScavenger()
{
bmalloc::api::disableScavenger();
}
#if HAVE(QOS_CLASSES)
void fastSetScavengerThreadQOSClass(unsigned qosClass)
{
bmalloc::api::setScavengerThreadQOSClass(static_cast<qos_class_t>(qosClass));
}
#endif
void forceEnablePGM(uint16_t guardMallocRate)
{
bmalloc::api::forceEnablePGM(guardMallocRate);
}
} // namespace WTF
namespace Gigacage {
void* tryAlignedMalloc(Kind kind, size_t alignment, size_t size)
{
void* result = bmalloc::api::tryMemalign(alignment, size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_TRY_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void* tryMalloc(Kind kind, size_t size)
{
void* result = bmalloc::api::tryMalloc(size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_TRY_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void* malloc(Kind kind, size_t size)
{
void* result = bmalloc::api::malloc(size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void* tryZeroedMalloc(Kind kind, size_t size)
{
void* result = bmalloc::api::tryZeroedMalloc(size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_TRY_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void* zeroedMalloc(Kind kind, size_t size)
{
void* result = bmalloc::api::zeroedMalloc(size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void* tryRealloc(Kind kind, void* pointer, size_t size)
{
void* result = bmalloc::api::tryRealloc(pointer, size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_TRY_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void free(Kind kind, void* p)
{
if (!p)
return;
RELEASE_ASSERT(isCaged(kind, p));
bmalloc::api::free(p, bmalloc::heapKind(kind));
WTF::compilerFence();
}
void* tryAllocateZeroedVirtualPages(Kind kind, size_t size)
{
void* result = bmalloc::api::tryLargeZeroedMemalignVirtual(WTF::pageSize(), size, bmalloc::CompactAllocationMode::Compact, bmalloc::heapKind(kind));
BPROFILE_TRY_ALLOCATION(GIGACAGE, kind, result, size);
WTF::compilerFence();
return result;
}
void freeVirtualPages(Kind kind, void* basePtr, size_t size)
{
if (!basePtr)
return;
RELEASE_ASSERT(isCaged(kind, basePtr));
bmalloc::api::freeLargeVirtual(basePtr, size, bmalloc::heapKind(kind));
WTF::compilerFence();
}
void* tryMallocArray(Kind kind, size_t numElements, size_t elementSize)
{
WTF::CheckedSize checkedSize = elementSize;
checkedSize *= numElements;
if (checkedSize.hasOverflowed())
return nullptr;
return tryMalloc(kind, checkedSize);
}
void* mallocArray(Kind kind, size_t numElements, size_t elementSize)
{
void* result = tryMallocArray(kind, numElements, elementSize);
RELEASE_ASSERT(result);
return result;
}
} // namespace Gigacage