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 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
38 #include <wtf/PassOwnPtr.h>
40 #include <wtf/ThreadingPrimitives.h>
41 #include <wtf/VMTags.h>
44 // Uncomment to create an artificial executable memory usage limit. This limit
45 // is imperfect and is primarily useful for testing the VM's ability to handle
46 // out-of-executable-memory situations.
47 // #define EXECUTABLE_MEMORY_LIMIT 1000000
55 #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
57 class DemandExecutableAllocator
: public MetaAllocator
{
59 DemandExecutableAllocator()
60 : MetaAllocator(jitAllocationGranule
)
62 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
63 allocators().add(this);
64 // Don't preallocate any memory here.
67 virtual ~DemandExecutableAllocator()
70 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
71 allocators().remove(this);
73 for (unsigned i
= 0; i
< reservations
.size(); ++i
)
74 reservations
.at(i
).deallocate();
77 static size_t bytesAllocatedByAllAllocators()
80 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
81 for (HashSet
<DemandExecutableAllocator
*>::const_iterator allocator
= allocators().begin(); allocator
!= allocators().end(); ++allocator
)
82 total
+= (*allocator
)->bytesAllocated();
86 static size_t bytesCommittedByAllocactors()
89 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
90 for (HashSet
<DemandExecutableAllocator
*>::const_iterator allocator
= allocators().begin(); allocator
!= allocators().end(); ++allocator
)
91 total
+= (*allocator
)->bytesCommitted();
95 #if ENABLE(META_ALLOCATOR_PROFILE)
96 static void dumpProfileFromAllAllocators()
98 std::lock_guard
<std::mutex
> lock(allocatorsMutex());
99 for (HashSet
<DemandExecutableAllocator
*>::const_iterator allocator
= allocators().begin(); allocator
!= allocators().end(); ++allocator
)
100 (*allocator
)->dumpProfile();
105 virtual void* allocateNewSpace(size_t& numPages
)
107 size_t newNumPages
= (((numPages
* pageSize() + JIT_ALLOCATOR_LARGE_ALLOC_SIZE
- 1) / JIT_ALLOCATOR_LARGE_ALLOC_SIZE
* JIT_ALLOCATOR_LARGE_ALLOC_SIZE
) + pageSize() - 1) / pageSize();
109 ASSERT(newNumPages
>= numPages
);
111 numPages
= newNumPages
;
113 #ifdef EXECUTABLE_MEMORY_LIMIT
114 if (bytesAllocatedByAllAllocators() >= EXECUTABLE_MEMORY_LIMIT
)
118 PageReservation reservation
= PageReservation::reserve(numPages
* pageSize(), OSAllocator::JSJITCodePages
, EXECUTABLE_POOL_WRITABLE
, true);
119 RELEASE_ASSERT(reservation
);
121 reservations
.append(reservation
);
123 return reservation
.base();
126 virtual void notifyNeedPage(void* page
)
128 OSAllocator::commit(page
, pageSize(), EXECUTABLE_POOL_WRITABLE
, true);
131 virtual void notifyPageIsFree(void* page
)
133 OSAllocator::decommit(page
, pageSize());
137 Vector
<PageReservation
, 16> reservations
;
138 static HashSet
<DemandExecutableAllocator
*>& allocators()
140 DEPRECATED_DEFINE_STATIC_LOCAL(HashSet
<DemandExecutableAllocator
*>, sAllocators
, ());
144 static std::mutex
& allocatorsMutex()
146 static NeverDestroyed
<std::mutex
> mutex
;
152 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
153 void ExecutableAllocator::initializeAllocator()
157 static DemandExecutableAllocator
* gAllocator
;
160 static inline DemandExecutableAllocator
* allocator()
166 void ExecutableAllocator::initializeAllocator()
169 gAllocator
= new DemandExecutableAllocator();
170 CodeProfiling::notifyAllocator(gAllocator
);
174 ExecutableAllocator::ExecutableAllocator(VM
&)
175 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
176 : m_allocator(adoptPtr(new DemandExecutableAllocator()))
182 ExecutableAllocator::~ExecutableAllocator()
186 bool ExecutableAllocator::isValid() const
191 bool ExecutableAllocator::underMemoryPressure()
193 #ifdef EXECUTABLE_MEMORY_LIMIT
194 return DemandExecutableAllocator::bytesAllocatedByAllAllocators() > EXECUTABLE_MEMORY_LIMIT
/ 2;
200 double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage
)
203 #ifdef EXECUTABLE_MEMORY_LIMIT
204 size_t bytesAllocated
= DemandExecutableAllocator::bytesAllocatedByAllAllocators() + addedMemoryUsage
;
205 if (bytesAllocated
>= EXECUTABLE_MEMORY_LIMIT
)
206 bytesAllocated
= EXECUTABLE_MEMORY_LIMIT
;
207 result
= static_cast<double>(EXECUTABLE_MEMORY_LIMIT
) /
208 (EXECUTABLE_MEMORY_LIMIT
- bytesAllocated
);
210 UNUSED_PARAM(addedMemoryUsage
);
219 PassRefPtr
<ExecutableMemoryHandle
> ExecutableAllocator::allocate(VM
&, size_t sizeInBytes
, void* ownerUID
, JITCompilationEffort effort
)
221 RefPtr
<ExecutableMemoryHandle
> result
= allocator()->allocate(sizeInBytes
, ownerUID
);
222 RELEASE_ASSERT(result
|| effort
!= JITCompilationMustSucceed
);
223 return result
.release();
226 size_t ExecutableAllocator::committedByteCount()
228 return DemandExecutableAllocator::bytesCommittedByAllocactors();
231 #if ENABLE(META_ALLOCATOR_PROFILE)
232 void ExecutableAllocator::dumpProfile()
234 DemandExecutableAllocator::dumpProfileFromAllAllocators();
238 #endif // ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
240 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
243 #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
246 void ExecutableAllocator::reprotectRegion(void* start
, size_t size
, ProtectionSetting setting
)
248 size_t pageSize
= WTF::pageSize();
250 // Calculate the start of the page containing this region,
251 // and account for this extra memory within size.
252 intptr_t startPtr
= reinterpret_cast<intptr_t>(start
);
253 intptr_t pageStartPtr
= startPtr
& ~(pageSize
- 1);
254 void* pageStart
= reinterpret_cast<void*>(pageStartPtr
);
255 size
+= (startPtr
- pageStartPtr
);
258 size
+= (pageSize
- 1);
259 size
&= ~(pageSize
- 1);
261 mprotect(pageStart
, size
, (setting
== Writable
) ? PROTECTION_FLAGS_RW
: PROTECTION_FLAGS_RX
);
268 #endif // HAVE(ASSEMBLER)