1 | // Copyright 2012 the V8 project authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef V8_ALLOCATION_H_ |
6 | #define V8_ALLOCATION_H_ |
7 | |
8 | #include "include/v8-platform.h" |
9 | #include "src/base/address-region.h" |
10 | #include "src/base/compiler-specific.h" |
11 | #include "src/base/platform/platform.h" |
12 | #include "src/globals.h" |
13 | #include "src/v8.h" |
14 | |
15 | namespace v8 { |
16 | namespace internal { |
17 | |
18 | class Isolate; |
19 | |
20 | // This file defines memory allocation functions. If a first attempt at an |
21 | // allocation fails, these functions call back into the embedder, then attempt |
22 | // the allocation a second time. The embedder callback must not reenter V8. |
23 | |
24 | // Called when allocation routines fail to allocate, even with a possible retry. |
25 | // This function should not return, but should terminate the current processing. |
26 | [[noreturn]] V8_EXPORT_PRIVATE void FatalProcessOutOfMemory( |
27 | Isolate* isolate, const char* message); |
28 | |
29 | // Superclass for classes managed with new & delete. |
30 | class V8_EXPORT_PRIVATE Malloced { |
31 | public: |
32 | void* operator new(size_t size) { return New(size); } |
33 | void operator delete(void* p) { Delete(p); } |
34 | |
35 | static void* New(size_t size); |
36 | static void Delete(void* p); |
37 | }; |
38 | |
39 | template <typename T> |
40 | T* NewArray(size_t size) { |
41 | T* result = new (std::nothrow) T[size]; |
42 | if (result == nullptr) { |
43 | V8::GetCurrentPlatform()->OnCriticalMemoryPressure(); |
44 | result = new (std::nothrow) T[size]; |
45 | if (result == nullptr) FatalProcessOutOfMemory(nullptr, "NewArray" ); |
46 | } |
47 | return result; |
48 | } |
49 | |
50 | template <typename T, typename = typename std::enable_if< |
51 | base::is_trivially_copyable<T>::value>::type> |
52 | T* NewArray(size_t size, T default_val) { |
53 | T* result = reinterpret_cast<T*>(NewArray<uint8_t>(sizeof(T) * size)); |
54 | for (size_t i = 0; i < size; ++i) result[i] = default_val; |
55 | return result; |
56 | } |
57 | |
58 | template <typename T> |
59 | void DeleteArray(T* array) { |
60 | delete[] array; |
61 | } |
62 | |
63 | |
64 | // The normal strdup functions use malloc. These versions of StrDup |
65 | // and StrNDup uses new and calls the FatalProcessOutOfMemory handler |
66 | // if allocation fails. |
67 | V8_EXPORT_PRIVATE char* StrDup(const char* str); |
68 | char* StrNDup(const char* str, int n); |
69 | |
70 | |
71 | // Allocation policy for allocating in the C free store using malloc |
72 | // and free. Used as the default policy for lists. |
73 | class FreeStoreAllocationPolicy { |
74 | public: |
75 | V8_INLINE void* New(size_t size) { return Malloced::New(size); } |
76 | V8_INLINE static void Delete(void* p) { Malloced::Delete(p); } |
77 | }; |
78 | |
79 | // Performs a malloc, with retry logic on failure. Returns nullptr on failure. |
80 | // Call free to release memory allocated with this function. |
81 | void* AllocWithRetry(size_t size); |
82 | |
83 | V8_EXPORT_PRIVATE void* AlignedAlloc(size_t size, size_t alignment); |
84 | void AlignedFree(void *ptr); |
85 | |
86 | // Returns platfrom page allocator instance. Guaranteed to be a valid pointer. |
87 | V8_EXPORT_PRIVATE v8::PageAllocator* GetPlatformPageAllocator(); |
88 | |
89 | // Sets the given page allocator as the platform page allocator and returns |
90 | // the current one. This function *must* be used only for testing purposes. |
91 | // It is not thread-safe and the testing infrastructure should ensure that |
92 | // the tests do not modify the value simultaneously. |
93 | V8_EXPORT_PRIVATE v8::PageAllocator* SetPlatformPageAllocatorForTesting( |
94 | v8::PageAllocator* page_allocator); |
95 | |
96 | // Gets the page granularity for AllocatePages and FreePages. Addresses returned |
97 | // by AllocatePages and AllocatePage are aligned to this size. |
98 | V8_EXPORT_PRIVATE size_t AllocatePageSize(); |
99 | |
100 | // Gets the granularity at which the permissions and release calls can be made. |
101 | V8_EXPORT_PRIVATE size_t CommitPageSize(); |
102 | |
103 | // Sets the random seed so that GetRandomMmapAddr() will generate repeatable |
104 | // sequences of random mmap addresses. |
105 | V8_EXPORT_PRIVATE void SetRandomMmapSeed(int64_t seed); |
106 | |
107 | // Generate a random address to be used for hinting allocation calls. |
108 | V8_EXPORT_PRIVATE void* GetRandomMmapAddr(); |
109 | |
110 | // Allocates memory. Permissions are set according to the access argument. |
111 | // |address| is a hint. |size| and |alignment| must be multiples of |
112 | // AllocatePageSize(). Returns the address of the allocated memory, with the |
113 | // specified size and alignment, or nullptr on failure. |
114 | V8_EXPORT_PRIVATE |
115 | V8_WARN_UNUSED_RESULT void* AllocatePages(v8::PageAllocator* page_allocator, |
116 | void* address, size_t size, |
117 | size_t alignment, |
118 | PageAllocator::Permission access); |
119 | |
120 | // Frees memory allocated by a call to AllocatePages. |address| and |size| must |
121 | // be multiples of AllocatePageSize(). Returns true on success, otherwise false. |
122 | V8_EXPORT_PRIVATE |
123 | V8_WARN_UNUSED_RESULT bool FreePages(v8::PageAllocator* page_allocator, |
124 | void* address, const size_t size); |
125 | |
126 | // Releases memory that is no longer needed. The range specified by |address| |
127 | // and |size| must be an allocated memory region. |size| and |new_size| must be |
128 | // multiples of CommitPageSize(). Memory from |new_size| to |size| is released. |
129 | // Released memory is left in an undefined state, so it should not be accessed. |
130 | // Returns true on success, otherwise false. |
131 | V8_EXPORT_PRIVATE |
132 | V8_WARN_UNUSED_RESULT bool ReleasePages(v8::PageAllocator* page_allocator, |
133 | void* address, size_t size, |
134 | size_t new_size); |
135 | |
136 | // Sets permissions according to |access|. |address| and |size| must be |
137 | // multiples of CommitPageSize(). Setting permission to kNoAccess may |
138 | // cause the memory contents to be lost. Returns true on success, otherwise |
139 | // false. |
140 | V8_EXPORT_PRIVATE |
141 | V8_WARN_UNUSED_RESULT bool SetPermissions(v8::PageAllocator* page_allocator, |
142 | void* address, size_t size, |
143 | PageAllocator::Permission access); |
144 | inline bool SetPermissions(v8::PageAllocator* page_allocator, Address address, |
145 | size_t size, PageAllocator::Permission access) { |
146 | return SetPermissions(page_allocator, reinterpret_cast<void*>(address), size, |
147 | access); |
148 | } |
149 | |
150 | // Convenience function that allocates a single system page with read and write |
151 | // permissions. |address| is a hint. Returns the base address of the memory and |
152 | // the page size via |allocated| on success. Returns nullptr on failure. |
153 | V8_EXPORT_PRIVATE |
154 | V8_WARN_UNUSED_RESULT byte* AllocatePage(v8::PageAllocator* page_allocator, |
155 | void* address, size_t* allocated); |
156 | |
157 | // Function that may release reserved memory regions to allow failed allocations |
158 | // to succeed. |length| is the amount of memory needed. Returns |true| if memory |
159 | // could be released, false otherwise. |
160 | V8_EXPORT_PRIVATE bool OnCriticalMemoryPressure(size_t length); |
161 | |
162 | // Represents and controls an area of reserved memory. |
163 | class V8_EXPORT_PRIVATE VirtualMemory final { |
164 | public: |
165 | // Empty VirtualMemory object, controlling no reserved memory. |
166 | VirtualMemory() = default; |
167 | |
168 | // Reserves virtual memory containing an area of the given size that is |
169 | // aligned per |alignment| rounded up to the |page_allocator|'s allocate page |
170 | // size. The |size| must be aligned with |page_allocator|'s commit page size. |
171 | // This may not be at the position returned by address(). |
172 | VirtualMemory(v8::PageAllocator* page_allocator, size_t size, void* hint, |
173 | size_t alignment = 1); |
174 | |
175 | // Construct a virtual memory by assigning it some already mapped address |
176 | // and size. |
177 | VirtualMemory(v8::PageAllocator* page_allocator, Address address, size_t size) |
178 | : page_allocator_(page_allocator), region_(address, size) { |
179 | DCHECK_NOT_NULL(page_allocator); |
180 | DCHECK(IsAligned(address, page_allocator->AllocatePageSize())); |
181 | DCHECK(IsAligned(size, page_allocator->CommitPageSize())); |
182 | } |
183 | |
184 | // Releases the reserved memory, if any, controlled by this VirtualMemory |
185 | // object. |
186 | ~VirtualMemory(); |
187 | |
188 | // Move constructor. |
189 | VirtualMemory(VirtualMemory&& other) V8_NOEXCEPT { TakeControl(&other); } |
190 | |
191 | // Move assignment operator. |
192 | VirtualMemory& operator=(VirtualMemory&& other) V8_NOEXCEPT { |
193 | TakeControl(&other); |
194 | return *this; |
195 | } |
196 | |
197 | // Returns whether the memory has been reserved. |
198 | bool IsReserved() const { return region_.begin() != kNullAddress; } |
199 | |
200 | // Initialize or resets an embedded VirtualMemory object. |
201 | void Reset(); |
202 | |
203 | v8::PageAllocator* page_allocator() { return page_allocator_; } |
204 | |
205 | const base::AddressRegion& region() const { return region_; } |
206 | |
207 | // Returns the start address of the reserved memory. |
208 | // If the memory was reserved with an alignment, this address is not |
209 | // necessarily aligned. The user might need to round it up to a multiple of |
210 | // the alignment to get the start of the aligned block. |
211 | Address address() const { |
212 | DCHECK(IsReserved()); |
213 | return region_.begin(); |
214 | } |
215 | |
216 | Address end() const { |
217 | DCHECK(IsReserved()); |
218 | return region_.end(); |
219 | } |
220 | |
221 | // Returns the size of the reserved memory. The returned value is only |
222 | // meaningful when IsReserved() returns true. |
223 | // If the memory was reserved with an alignment, this size may be larger |
224 | // than the requested size. |
225 | size_t size() const { return region_.size(); } |
226 | |
227 | // Sets permissions according to the access argument. address and size must be |
228 | // multiples of CommitPageSize(). Returns true on success, otherwise false. |
229 | bool SetPermissions(Address address, size_t size, |
230 | PageAllocator::Permission access); |
231 | |
232 | // Releases memory after |free_start|. Returns the number of bytes released. |
233 | size_t Release(Address free_start); |
234 | |
235 | // Frees all memory. |
236 | void Free(); |
237 | |
238 | // Assign control of the reserved region to a different VirtualMemory object. |
239 | // The old object is no longer functional (IsReserved() returns false). |
240 | void TakeControl(VirtualMemory* from); |
241 | |
242 | bool InVM(Address address, size_t size) { |
243 | return region_.contains(address, size); |
244 | } |
245 | |
246 | private: |
247 | // Page allocator that controls the virtual memory. |
248 | v8::PageAllocator* page_allocator_ = nullptr; |
249 | base::AddressRegion region_; |
250 | |
251 | DISALLOW_COPY_AND_ASSIGN(VirtualMemory); |
252 | }; |
253 | |
254 | } // namespace internal |
255 | } // namespace v8 |
256 | |
257 | #endif // V8_ALLOCATION_H_ |
258 | |