1/* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "ExecutableAllocator.h" 28 29#include "JSCInlines.h" 30 31#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) 32 33#include "CodeProfiling.h" 34#include <errno.h> 35#include <unistd.h> 36#include <wtf/MetaAllocator.h> 37#include <wtf/PageReservation.h> 38#include <wtf/VMTags.h> 39 40#if OS(DARWIN) 41#include <sys/mman.h> 42#endif 43 44#if OS(LINUX) 45#include <stdio.h> 46#endif 47 48#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1090 49// MADV_FREE_REUSABLE does not work for JIT memory on older OSes so use MADV_FREE in that case. 50#define WTF_USE_MADV_FREE_FOR_JIT_MEMORY 1 51#endif 52 53using namespace WTF; 54 55namespace JSC { 56 57uintptr_t startOfFixedExecutableMemoryPool; 58 59class FixedVMPoolExecutableAllocator : public MetaAllocator { 60 WTF_MAKE_FAST_ALLOCATED; 61public: 62 FixedVMPoolExecutableAllocator() 63 : MetaAllocator(jitAllocationGranule) // round up all allocations to 32 bytes 64 { 65 m_reservation = PageReservation::reserveWithGuardPages(fixedExecutableMemoryPoolSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true); 66 if (m_reservation) { 67 ASSERT(m_reservation.size() == fixedExecutableMemoryPoolSize); 68 addFreshFreeSpace(m_reservation.base(), m_reservation.size()); 69 70 startOfFixedExecutableMemoryPool = reinterpret_cast<uintptr_t>(m_reservation.base()); 71 } 72 } 73 74 virtual ~FixedVMPoolExecutableAllocator(); 75 76protected: 77 virtual void* allocateNewSpace(size_t&) override 78 { 79 // We're operating in a fixed pool, so new allocation is always prohibited. 80 return 0; 81 } 82 83 virtual void notifyNeedPage(void* page) override 84 { 85#if USE(MADV_FREE_FOR_JIT_MEMORY) 86 UNUSED_PARAM(page); 87#else 88 m_reservation.commit(page, pageSize()); 89#endif 90 } 91 92 virtual void notifyPageIsFree(void* page) override 93 { 94#if USE(MADV_FREE_FOR_JIT_MEMORY) 95 for (;;) { 96 int result = madvise(page, pageSize(), MADV_FREE); 97 if (!result) 98 return; 99 ASSERT(result == -1); 100 if (errno != EAGAIN) { 101 RELEASE_ASSERT_NOT_REACHED(); // In debug mode, this should be a hard failure. 102 break; // In release mode, we should just ignore the error - not returning memory to the OS is better than crashing, especially since we _will_ be able to reuse the memory internally anyway. 103 } 104 } 105#else 106 m_reservation.decommit(page, pageSize()); 107#endif 108 } 109 110private: 111 PageReservation m_reservation; 112}; 113 114static FixedVMPoolExecutableAllocator* allocator; 115 116void ExecutableAllocator::initializeAllocator() 117{ 118 ASSERT(!allocator); 119 allocator = new FixedVMPoolExecutableAllocator(); 120 CodeProfiling::notifyAllocator(allocator); 121} 122 123ExecutableAllocator::ExecutableAllocator(VM&) 124{ 125 ASSERT(allocator); 126} 127 128ExecutableAllocator::~ExecutableAllocator() 129{ 130} 131 132FixedVMPoolExecutableAllocator::~FixedVMPoolExecutableAllocator() 133{ 134 m_reservation.deallocate(); 135} 136 137bool ExecutableAllocator::isValid() const 138{ 139 return !!allocator->bytesReserved(); 140} 141 142bool ExecutableAllocator::underMemoryPressure() 143{ 144 MetaAllocator::Statistics statistics = allocator->currentStatistics(); 145 return statistics.bytesAllocated > statistics.bytesReserved / 2; 146} 147 148double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage) 149{ 150 MetaAllocator::Statistics statistics = allocator->currentStatistics(); 151 ASSERT(statistics.bytesAllocated <= statistics.bytesReserved); 152 size_t bytesAllocated = statistics.bytesAllocated + addedMemoryUsage; 153 if (bytesAllocated >= statistics.bytesReserved) 154 bytesAllocated = statistics.bytesReserved; 155 double result = 1.0; 156 size_t divisor = statistics.bytesReserved - bytesAllocated; 157 if (divisor) 158 result = static_cast<double>(statistics.bytesReserved) / divisor; 159 if (result < 1.0) 160 result = 1.0; 161 return result; 162} 163 164PassRefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM& vm, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort) 165{ 166 RefPtr<ExecutableMemoryHandle> result = allocator->allocate(sizeInBytes, ownerUID); 167 if (!result) { 168 if (effort == JITCompilationCanFail) 169 return result; 170 releaseExecutableMemory(vm); 171 result = allocator->allocate(sizeInBytes, ownerUID); 172 RELEASE_ASSERT(result); 173 } 174 return result.release(); 175} 176 177size_t ExecutableAllocator::committedByteCount() 178{ 179 return allocator->bytesCommitted(); 180} 181 182#if ENABLE(META_ALLOCATOR_PROFILE) 183void ExecutableAllocator::dumpProfile() 184{ 185 allocator->dumpProfile(); 186} 187#endif 188 189} 190 191 192#endif // ENABLE(EXECUTABLE_ALLOCATOR_FIXED) 193