2 * Copyright (C) 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ExecutableAllocator.h"
29 #include "JSCInlines.h"
31 #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
32 #include "CodeProfiling.h"
33 #include <wtf/HashSet.h>
34 #include <wtf/MetaAllocator.h>
35 #include <wtf/NeverDestroyed.h>
36 #include <wtf/PageReservation.h>
37 #include <wtf/ThreadingPrimitives.h>
38 #include <wtf/VMTags.h>
41 // Uncomment to create an artificial executable memory usage limit. This limit
42 // is imperfect and is primarily useful for testing the VM's ability to handle
43 // out-of-executable-memory situations.
44 // #define EXECUTABLE_MEMORY_LIMIT 1000000
52 #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
54 class DemandExecutableAllocator
: public MetaAllocator
{
56 DemandExecutableAllocator()
57 : MetaAllocator(jitAllocationGranule
)
59 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
60 allocators().add(this);
61 // Don't preallocate any memory here.
64 virtual ~DemandExecutableAllocator()
67 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
68 allocators().remove(this);
70 for (unsigned i
= 0; i
< reservations
.size(); ++i
)
71 reservations
.at(i
).deallocate();
74 static size_t bytesAllocatedByAllAllocators()
77 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
78 for (HashSet
<DemandExecutableAllocator
*>::const_iterator allocator
= allocators().begin(); allocator
!= allocators().end(); ++allocator
)
79 total
+= (*allocator
)->bytesAllocated();
83 static size_t bytesCommittedByAllocactors()
86 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
87 for (HashSet
<DemandExecutableAllocator
*>::const_iterator allocator
= allocators().begin(); allocator
!= allocators().end(); ++allocator
)
88 total
+= (*allocator
)->bytesCommitted();
92 #if ENABLE(META_ALLOCATOR_PROFILE)
93 static void dumpProfileFromAllAllocators()
95 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
96 for (HashSet
<DemandExecutableAllocator
*>::const_iterator allocator
= allocators().begin(); allocator
!= allocators().end(); ++allocator
)
97 (*allocator
)->dumpProfile();
102 virtual void* allocateNewSpace(size_t& numPages
)
104 size_t newNumPages
= (((numPages
* pageSize() + JIT_ALLOCATOR_LARGE_ALLOC_SIZE
- 1) / JIT_ALLOCATOR_LARGE_ALLOC_SIZE
* JIT_ALLOCATOR_LARGE_ALLOC_SIZE
) + pageSize() - 1) / pageSize();
106 ASSERT(newNumPages
>= numPages
);
108 numPages
= newNumPages
;
110 #ifdef EXECUTABLE_MEMORY_LIMIT
111 if (bytesAllocatedByAllAllocators() >= EXECUTABLE_MEMORY_LIMIT
)
115 PageReservation reservation
= PageReservation::reserve(numPages
* pageSize(), OSAllocator::JSJITCodePages
, EXECUTABLE_POOL_WRITABLE
, true);
116 RELEASE_ASSERT(reservation
);
118 reservations
.append(reservation
);
120 return reservation
.base();
123 virtual void notifyNeedPage(void* page
)
125 OSAllocator::commit(page
, pageSize(), EXECUTABLE_POOL_WRITABLE
, true);
128 virtual void notifyPageIsFree(void* page
)
130 OSAllocator::decommit(page
, pageSize());
134 Vector
<PageReservation
, 16> reservations
;
135 static HashSet
<DemandExecutableAllocator
*>& allocators()
137 DEPRECATED_DEFINE_STATIC_LOCAL(HashSet
<DemandExecutableAllocator
*>, sAllocators
, ());
141 static std::mutex
& allocatorsMutex()
143 static NeverDestroyed
<std::mutex
> mutex
;
149 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
150 void ExecutableAllocator::initializeAllocator()
154 static DemandExecutableAllocator
* gAllocator
;
157 static inline DemandExecutableAllocator
* allocator()
163 void ExecutableAllocator::initializeAllocator()
166 gAllocator
= new DemandExecutableAllocator();
167 CodeProfiling::notifyAllocator(gAllocator
);
171 ExecutableAllocator::ExecutableAllocator(VM
&)
172 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
173 : m_allocator(std::make_unique
<DemandExecutableAllocator
>())
179 ExecutableAllocator::~ExecutableAllocator()
183 bool ExecutableAllocator::isValid() const
188 bool ExecutableAllocator::underMemoryPressure()
190 #ifdef EXECUTABLE_MEMORY_LIMIT
191 return DemandExecutableAllocator::bytesAllocatedByAllAllocators() > EXECUTABLE_MEMORY_LIMIT
/ 2;
197 double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage
)
200 #ifdef EXECUTABLE_MEMORY_LIMIT
201 size_t bytesAllocated
= DemandExecutableAllocator::bytesAllocatedByAllAllocators() + addedMemoryUsage
;
202 if (bytesAllocated
>= EXECUTABLE_MEMORY_LIMIT
)
203 bytesAllocated
= EXECUTABLE_MEMORY_LIMIT
;
204 result
= static_cast<double>(EXECUTABLE_MEMORY_LIMIT
) /
205 (EXECUTABLE_MEMORY_LIMIT
- bytesAllocated
);
207 UNUSED_PARAM(addedMemoryUsage
);
216 RefPtr
<ExecutableMemoryHandle
> ExecutableAllocator::allocate(VM
&, size_t sizeInBytes
, void* ownerUID
, JITCompilationEffort effort
)
218 RefPtr
<ExecutableMemoryHandle
> result
= allocator()->allocate(sizeInBytes
, ownerUID
);
219 RELEASE_ASSERT(result
|| effort
!= JITCompilationMustSucceed
);
223 size_t ExecutableAllocator::committedByteCount()
225 return DemandExecutableAllocator::bytesCommittedByAllocactors();
228 #if ENABLE(META_ALLOCATOR_PROFILE)
229 void ExecutableAllocator::dumpProfile()
231 DemandExecutableAllocator::dumpProfileFromAllAllocators();
235 #endif // ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
237 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
240 #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
243 void ExecutableAllocator::reprotectRegion(void* start
, size_t size
, ProtectionSetting setting
)
245 size_t pageSize
= WTF::pageSize();
247 // Calculate the start of the page containing this region,
248 // and account for this extra memory within size.
249 intptr_t startPtr
= reinterpret_cast<intptr_t>(start
);
250 intptr_t pageStartPtr
= startPtr
& ~(pageSize
- 1);
251 void* pageStart
= reinterpret_cast<void*>(pageStartPtr
);
252 size
+= (startPtr
- pageStartPtr
);
255 size
+= (pageSize
- 1);
256 size
&= ~(pageSize
- 1);
258 mprotect(pageStart
, size
, (setting
== Writable
) ? PROTECTION_FLAGS_RW
: PROTECTION_FLAGS_RX
);
265 #endif // HAVE(ASSEMBLER)