]>
Commit | Line | Data |
---|---|---|
ba379fdc | 1 | /* |
ed1e77d3 | 2 | * Copyright (C) 2009, 2015 Apple Inc. All rights reserved. |
ba379fdc A |
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" | |
ba379fdc A |
27 | #include "ExecutableAllocator.h" |
28 | ||
81345200 A |
29 | #include "JSCInlines.h" |
30 | ||
4e4e5a6f | 31 | #if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) |
ba379fdc | 32 | |
6fe7ccc8 | 33 | #include "CodeProfiling.h" |
ed1e77d3 | 34 | #include "ExecutableAllocationFuzz.h" |
4e4e5a6f | 35 | #include <errno.h> |
ed1e77d3 | 36 | #if !PLATFORM(WIN) |
ba379fdc | 37 | #include <unistd.h> |
ed1e77d3 | 38 | #endif |
6fe7ccc8 | 39 | #include <wtf/MetaAllocator.h> |
14957cd0 | 40 | #include <wtf/PageReservation.h> |
ba379fdc A |
41 | #include <wtf/VMTags.h> |
42 | ||
93a37866 A |
43 | #if OS(DARWIN) |
44 | #include <sys/mman.h> | |
45 | #endif | |
46 | ||
14957cd0 A |
47 | #if OS(LINUX) |
48 | #include <stdio.h> | |
49 | #endif | |
50 | ||
ba379fdc A |
51 | using namespace WTF; |
52 | ||
53 | namespace JSC { | |
14957cd0 | 54 | |
93a37866 | 55 | uintptr_t startOfFixedExecutableMemoryPool; |
ba379fdc | 56 | |
6fe7ccc8 | 57 | class FixedVMPoolExecutableAllocator : public MetaAllocator { |
93a37866 | 58 | WTF_MAKE_FAST_ALLOCATED; |
b80e6193 | 59 | public: |
6fe7ccc8 | 60 | FixedVMPoolExecutableAllocator() |
93a37866 | 61 | : MetaAllocator(jitAllocationGranule) // round up all allocations to 32 bytes |
b80e6193 | 62 | { |
ed1e77d3 A |
63 | size_t reservationSize; |
64 | if (Options::jitMemoryReservationSize()) | |
65 | reservationSize = Options::jitMemoryReservationSize(); | |
66 | else | |
67 | reservationSize = fixedExecutableMemoryPoolSize; | |
68 | m_reservation = PageReservation::reserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true); | |
6fe7ccc8 | 69 | if (m_reservation) { |
ed1e77d3 | 70 | ASSERT(m_reservation.size() == reservationSize); |
6fe7ccc8 | 71 | addFreshFreeSpace(m_reservation.base(), m_reservation.size()); |
93a37866 A |
72 | |
73 | startOfFixedExecutableMemoryPool = reinterpret_cast<uintptr_t>(m_reservation.base()); | |
ba379fdc | 74 | } |
b80e6193 | 75 | } |
93a37866 A |
76 | |
77 | virtual ~FixedVMPoolExecutableAllocator(); | |
6fe7ccc8 A |
78 | |
79 | protected: | |
81345200 | 80 | virtual void* allocateNewSpace(size_t&) override |
b80e6193 | 81 | { |
6fe7ccc8 A |
82 | // We're operating in a fixed pool, so new allocation is always prohibited. |
83 | return 0; | |
b80e6193 | 84 | } |
6fe7ccc8 | 85 | |
81345200 | 86 | virtual void notifyNeedPage(void* page) override |
b80e6193 | 87 | { |
93a37866 | 88 | #if USE(MADV_FREE_FOR_JIT_MEMORY) |
6fe7ccc8 A |
89 | UNUSED_PARAM(page); |
90 | #else | |
91 | m_reservation.commit(page, pageSize()); | |
b80e6193 | 92 | #endif |
b80e6193 | 93 | } |
6fe7ccc8 | 94 | |
81345200 | 95 | virtual void notifyPageIsFree(void* page) override |
6fe7ccc8 | 96 | { |
93a37866 | 97 | #if USE(MADV_FREE_FOR_JIT_MEMORY) |
6fe7ccc8 A |
98 | for (;;) { |
99 | int result = madvise(page, pageSize(), MADV_FREE); | |
100 | if (!result) | |
101 | return; | |
102 | ASSERT(result == -1); | |
103 | if (errno != EAGAIN) { | |
93a37866 | 104 | RELEASE_ASSERT_NOT_REACHED(); // In debug mode, this should be a hard failure. |
6fe7ccc8 | 105 | 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. |
ba379fdc | 106 | } |
b80e6193 | 107 | } |
b80e6193 | 108 | #else |
6fe7ccc8 | 109 | m_reservation.decommit(page, pageSize()); |
4e4e5a6f | 110 | #endif |
ba379fdc | 111 | } |
b80e6193 | 112 | |
14957cd0 | 113 | private: |
14957cd0 | 114 | PageReservation m_reservation; |
ba379fdc A |
115 | }; |
116 | ||
6fe7ccc8 | 117 | static FixedVMPoolExecutableAllocator* allocator; |
14957cd0 | 118 | |
6fe7ccc8 A |
119 | void ExecutableAllocator::initializeAllocator() |
120 | { | |
121 | ASSERT(!allocator); | |
122 | allocator = new FixedVMPoolExecutableAllocator(); | |
123 | CodeProfiling::notifyAllocator(allocator); | |
124 | } | |
14957cd0 | 125 | |
93a37866 | 126 | ExecutableAllocator::ExecutableAllocator(VM&) |
14957cd0 | 127 | { |
6fe7ccc8 A |
128 | ASSERT(allocator); |
129 | } | |
14957cd0 | 130 | |
6fe7ccc8 | 131 | ExecutableAllocator::~ExecutableAllocator() |
ba379fdc | 132 | { |
ba379fdc A |
133 | } |
134 | ||
93a37866 A |
135 | FixedVMPoolExecutableAllocator::~FixedVMPoolExecutableAllocator() |
136 | { | |
137 | m_reservation.deallocate(); | |
138 | } | |
139 | ||
4e4e5a6f A |
140 | bool ExecutableAllocator::isValid() const |
141 | { | |
6fe7ccc8 | 142 | return !!allocator->bytesReserved(); |
4e4e5a6f A |
143 | } |
144 | ||
14957cd0 | 145 | bool ExecutableAllocator::underMemoryPressure() |
ba379fdc | 146 | { |
6fe7ccc8 A |
147 | MetaAllocator::Statistics statistics = allocator->currentStatistics(); |
148 | return statistics.bytesAllocated > statistics.bytesReserved / 2; | |
ba379fdc A |
149 | } |
150 | ||
6fe7ccc8 | 151 | double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage) |
ba379fdc | 152 | { |
6fe7ccc8 A |
153 | MetaAllocator::Statistics statistics = allocator->currentStatistics(); |
154 | ASSERT(statistics.bytesAllocated <= statistics.bytesReserved); | |
155 | size_t bytesAllocated = statistics.bytesAllocated + addedMemoryUsage; | |
ed1e77d3 A |
156 | size_t bytesAvailable = static_cast<size_t>( |
157 | statistics.bytesReserved * (1 - executablePoolReservationFraction)); | |
158 | if (bytesAllocated >= bytesAvailable) | |
159 | bytesAllocated = bytesAvailable; | |
6fe7ccc8 | 160 | double result = 1.0; |
ed1e77d3 | 161 | size_t divisor = bytesAvailable - bytesAllocated; |
6fe7ccc8 | 162 | if (divisor) |
ed1e77d3 | 163 | result = static_cast<double>(bytesAvailable) / divisor; |
6fe7ccc8 A |
164 | if (result < 1.0) |
165 | result = 1.0; | |
166 | return result; | |
b80e6193 A |
167 | } |
168 | ||
ed1e77d3 | 169 | RefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM&, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort) |
b80e6193 | 170 | { |
ed1e77d3 A |
171 | if (effort != JITCompilationCanFail && Options::reportMustSucceedExecutableAllocations()) { |
172 | dataLog("Allocating ", sizeInBytes, " bytes of executable memory with JITCompilationMustSucceed.\n"); | |
173 | WTFReportBacktrace(); | |
174 | } | |
175 | ||
176 | if (effort == JITCompilationCanFail | |
177 | && doExecutableAllocationFuzzingIfEnabled() == PretendToFailExecutableAllocation) | |
178 | return nullptr; | |
179 | ||
180 | if (effort == JITCompilationCanFail) { | |
181 | // Don't allow allocations if we are down to reserve. | |
182 | MetaAllocator::Statistics statistics = allocator->currentStatistics(); | |
183 | size_t bytesAllocated = statistics.bytesAllocated + sizeInBytes; | |
184 | size_t bytesAvailable = static_cast<size_t>( | |
185 | statistics.bytesReserved * (1 - executablePoolReservationFraction)); | |
186 | if (bytesAllocated > bytesAvailable) | |
187 | return nullptr; | |
188 | } | |
189 | ||
6fe7ccc8 A |
190 | RefPtr<ExecutableMemoryHandle> result = allocator->allocate(sizeInBytes, ownerUID); |
191 | if (!result) { | |
ed1e77d3 A |
192 | if (effort != JITCompilationCanFail) { |
193 | dataLog("Ran out of executable memory while allocating ", sizeInBytes, " bytes.\n"); | |
194 | CRASH(); | |
195 | } | |
196 | return nullptr; | |
6fe7ccc8 | 197 | } |
ed1e77d3 | 198 | return result; |
ba379fdc A |
199 | } |
200 | ||
6fe7ccc8 A |
201 | size_t ExecutableAllocator::committedByteCount() |
202 | { | |
203 | return allocator->bytesCommitted(); | |
204 | } | |
205 | ||
206 | #if ENABLE(META_ALLOCATOR_PROFILE) | |
207 | void ExecutableAllocator::dumpProfile() | |
208 | { | |
209 | allocator->dumpProfile(); | |
210 | } | |
211 | #endif | |
212 | ||
ba379fdc A |
213 | } |
214 | ||
14957cd0 | 215 | |
6fe7ccc8 | 216 | #endif // ENABLE(EXECUTABLE_ALLOCATOR_FIXED) |