1 // Copyright (c) 2005, 2007, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 // Author: Sanjay Ghemawat
36 #elif HAVE(INTTYPES_H)
39 #include <sys/types.h>
49 #include "Assertions.h"
50 #include "TCSystemAlloc.h"
51 #include "TCSpinLock.h"
52 #include "UnusedParam.h"
55 static const int cProtFlags = PROT_READ | PROT_WRITE
56 #if ENABLE(ASSEMBLER) && (PLATFORM(GTK) || (PLATFORM(MAC) && PLATFORM(X86_64)) || PLATFORM(QT))
63 #define MAP_ANONYMOUS MAP_ANON
66 // Structure for discovering alignment
73 static SpinLock spinlock = SPINLOCK_INITIALIZER;
75 // Page size is initialized on demand
76 static size_t pagesize = 0;
78 // Configuration parameters.
80 // if use_devmem is true, either use_sbrk or use_mmap must also be true.
81 // For 2.2 kernels, it looks like the sbrk address space (500MBish) and
82 // the mmap address space (1300MBish) are disjoint, so we need both allocators
83 // to get as much virtual memory as possible.
85 static bool use_devmem = false;
89 static bool use_sbrk = false;
93 static bool use_mmap = true;
96 #if HAVE(VIRTUALALLOC)
97 static bool use_VirtualAlloc = true;
100 // Flags to keep us from retrying allocators that failed.
101 static bool devmem_failure = false;
102 static bool sbrk_failure = false;
103 static bool mmap_failure = false;
104 static bool VirtualAlloc_failure = false;
107 DEFINE_int32(malloc_devmem_start, 0,
108 "Physical memory starting location in MB for /dev/mem allocation."
109 " Setting this to 0 disables /dev/mem allocation");
110 DEFINE_int32(malloc_devmem_limit, 0,
111 "Physical memory limit location in MB for /dev/mem allocation."
112 " Setting this to 0 means no limit.");
114 static const int32_t FLAGS_malloc_devmem_start = 0;
115 static const int32_t FLAGS_malloc_devmem_limit = 0;
120 static void* TrySbrk(size_t size, size_t *actual_size, size_t alignment) {
121 size = ((size + alignment - 1) / alignment) * alignment;
123 // could theoretically return the "extra" bytes here, but this
124 // is simple and correct.
128 void* result = sbrk(size);
129 if (result == reinterpret_cast<void*>(-1)) {
135 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
136 if ((ptr & (alignment-1)) == 0) return result;
138 // Try to get more memory for alignment
139 size_t extra = alignment - (ptr & (alignment-1));
140 void* r2 = sbrk(extra);
141 if (reinterpret_cast<uintptr_t>(r2) == (ptr + size)) {
142 // Contiguous with previous result
143 return reinterpret_cast<void*>(ptr + extra);
146 // Give up and ask for "size + alignment - 1" bytes so
147 // that we can find an aligned region within it.
148 result = sbrk(size + alignment - 1);
149 if (result == reinterpret_cast<void*>(-1)) {
153 ptr = reinterpret_cast<uintptr_t>(result);
154 if ((ptr & (alignment-1)) != 0) {
155 ptr += alignment - (ptr & (alignment-1));
157 return reinterpret_cast<void*>(ptr);
160 #endif /* HAVE(SBRK) */
164 static void* TryMmap(size_t size, size_t *actual_size, size_t alignment) {
165 // Enforce page alignment
166 if (pagesize == 0) pagesize = getpagesize();
167 if (alignment < pagesize) alignment = pagesize;
168 size = ((size + alignment - 1) / alignment) * alignment;
170 // could theoretically return the "extra" bytes here, but this
171 // is simple and correct.
175 // Ask for extra memory if alignment > pagesize
177 if (alignment > pagesize) {
178 extra = alignment - pagesize;
180 void* result = mmap(NULL, size + extra,
182 MAP_PRIVATE|MAP_ANONYMOUS,
184 if (result == reinterpret_cast<void*>(MAP_FAILED)) {
189 // Adjust the return memory so it is aligned
190 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
192 if ((ptr & (alignment - 1)) != 0) {
193 adjust = alignment - (ptr & (alignment - 1));
196 // Return the unused memory to the system
198 munmap(reinterpret_cast<void*>(ptr), adjust);
200 if (adjust < extra) {
201 munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
205 return reinterpret_cast<void*>(ptr);
208 #endif /* HAVE(MMAP) */
210 #if HAVE(VIRTUALALLOC)
212 static void* TryVirtualAlloc(size_t size, size_t *actual_size, size_t alignment) {
213 // Enforce page alignment
215 SYSTEM_INFO system_info;
216 GetSystemInfo(&system_info);
217 pagesize = system_info.dwPageSize;
220 if (alignment < pagesize) alignment = pagesize;
221 size = ((size + alignment - 1) / alignment) * alignment;
223 // could theoretically return the "extra" bytes here, but this
224 // is simple and correct.
228 // Ask for extra memory if alignment > pagesize
230 if (alignment > pagesize) {
231 extra = alignment - pagesize;
233 void* result = VirtualAlloc(NULL, size + extra,
234 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
235 PAGE_EXECUTE_READWRITE);
237 if (result == NULL) {
238 VirtualAlloc_failure = true;
242 // Adjust the return memory so it is aligned
243 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
245 if ((ptr & (alignment - 1)) != 0) {
246 adjust = alignment - (ptr & (alignment - 1));
249 // Return the unused memory to the system - we'd like to release but the best we can do
250 // is decommit, since Windows only lets you free the whole allocation.
252 VirtualFree(reinterpret_cast<void*>(ptr), adjust, MEM_DECOMMIT);
254 if (adjust < extra) {
255 VirtualFree(reinterpret_cast<void*>(ptr + adjust + size), extra-adjust, MEM_DECOMMIT);
259 return reinterpret_cast<void*>(ptr);
262 #endif /* HAVE(MMAP) */
265 static void* TryDevMem(size_t size, size_t *actual_size, size_t alignment) {
266 static bool initialized = false;
267 static off_t physmem_base; // next physical memory address to allocate
268 static off_t physmem_limit; // maximum physical address allowed
269 static int physmem_fd; // file descriptor for /dev/mem
271 // Check if we should use /dev/mem allocation. Note that it may take
272 // a while to get this flag initialized, so meanwhile we fall back to
273 // the next allocator. (It looks like 7MB gets allocated before
274 // this flag gets initialized -khr.)
275 if (FLAGS_malloc_devmem_start == 0) {
276 // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to
277 // try us again next time.
282 physmem_fd = open("/dev/mem", O_RDWR);
283 if (physmem_fd < 0) {
284 devmem_failure = true;
287 physmem_base = FLAGS_malloc_devmem_start*1024LL*1024LL;
288 physmem_limit = FLAGS_malloc_devmem_limit*1024LL*1024LL;
292 // Enforce page alignment
293 if (pagesize == 0) pagesize = getpagesize();
294 if (alignment < pagesize) alignment = pagesize;
295 size = ((size + alignment - 1) / alignment) * alignment;
297 // could theoretically return the "extra" bytes here, but this
298 // is simple and correct.
302 // Ask for extra memory if alignment > pagesize
304 if (alignment > pagesize) {
305 extra = alignment - pagesize;
308 // check to see if we have any memory left
309 if (physmem_limit != 0 && physmem_base + size + extra > physmem_limit) {
310 devmem_failure = true;
313 void *result = mmap(0, size + extra, cProtFlags,
314 MAP_SHARED, physmem_fd, physmem_base);
315 if (result == reinterpret_cast<void*>(MAP_FAILED)) {
316 devmem_failure = true;
319 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
321 // Adjust the return memory so it is aligned
323 if ((ptr & (alignment - 1)) != 0) {
324 adjust = alignment - (ptr & (alignment - 1));
327 // Return the unused virtual memory to the system
329 munmap(reinterpret_cast<void*>(ptr), adjust);
331 if (adjust < extra) {
332 munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
336 physmem_base += adjust + size;
338 return reinterpret_cast<void*>(ptr);
342 void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, size_t alignment) {
343 // Discard requests that overflow
344 if (size + alignment < size) return NULL;
346 SpinLockHolder lock_holder(&spinlock);
348 // Enforce minimum alignment
349 if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner);
351 // Try twice, once avoiding allocators that failed before, and once
352 // more trying all allocators even if they failed before.
353 for (int i = 0; i < 2; i++) {
356 if (use_devmem && !devmem_failure) {
357 void* result = TryDevMem(size, actual_size, alignment);
358 if (result != NULL) return result;
363 if (use_sbrk && !sbrk_failure) {
364 void* result = TrySbrk(size, actual_size, alignment);
365 if (result != NULL) return result;
370 if (use_mmap && !mmap_failure) {
371 void* result = TryMmap(size, actual_size, alignment);
372 if (result != NULL) return result;
376 #if HAVE(VIRTUALALLOC)
377 if (use_VirtualAlloc && !VirtualAlloc_failure) {
378 void* result = TryVirtualAlloc(size, actual_size, alignment);
379 if (result != NULL) return result;
383 // nothing worked - reset failure flags and try again
384 devmem_failure = false;
385 sbrk_failure = false;
386 mmap_failure = false;
387 VirtualAlloc_failure = false;
392 void TCMalloc_SystemRelease(void* start, size_t length)
395 UNUSED_PARAM(length);
396 #if HAVE(MADV_DONTNEED)
397 if (FLAGS_malloc_devmem_start) {
398 // It's not safe to use MADV_DONTNEED if we've been mapping
399 // /dev/mem for heap memory
402 if (pagesize == 0) pagesize = getpagesize();
403 const size_t pagemask = pagesize - 1;
405 size_t new_start = reinterpret_cast<size_t>(start);
406 size_t end = new_start + length;
407 size_t new_end = end;
409 // Round up the starting address and round down the ending address
410 // to be page aligned:
411 new_start = (new_start + pagesize - 1) & ~pagemask;
412 new_end = new_end & ~pagemask;
414 ASSERT((new_start & pagemask) == 0);
415 ASSERT((new_end & pagemask) == 0);
416 ASSERT(new_start >= reinterpret_cast<size_t>(start));
417 ASSERT(new_end <= end);
419 if (new_end > new_start) {
420 // Note -- ignoring most return codes, because if this fails it
422 while (madvise(reinterpret_cast<char*>(new_start), new_end - new_start,
423 MADV_DONTNEED) == -1 &&
432 void *newAddress = mmap(start, length, cProtFlags, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
433 UNUSED_PARAM(newAddress);
434 // If the mmap failed then that's ok, we just won't return the memory to the system.
435 ASSERT(newAddress == start || newAddress == reinterpret_cast<void*>(MAP_FAILED));
440 #if HAVE(VIRTUALALLOC)
441 void TCMalloc_SystemCommit(void* start, size_t length)
444 UNUSED_PARAM(length);