Index: third_party/tcmalloc/system-alloc.cc |
=================================================================== |
--- third_party/tcmalloc/system-alloc.cc (revision 0) |
+++ third_party/tcmalloc/system-alloc.cc (revision 0) |
@@ -0,0 +1,505 @@ |
+// Copyright (c) 2005, Google Inc. |
+// All rights reserved. |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+// --- |
+// Author: Sanjay Ghemawat |
+ |
+#include <config.h> |
+#if defined HAVE_STDINT_H |
+#include <stdint.h> |
+#elif defined HAVE_INTTYPES_H |
+#include <inttypes.h> |
+#else |
+#include <sys/types.h> |
+#endif |
+#ifdef HAVE_UNISTD_H |
+#include <unistd.h> |
+#endif |
+#include <fcntl.h> // for open() |
+#ifdef HAVE_MMAP |
+#include <sys/mman.h> |
+#endif |
+#include <errno.h> |
+#include "system-alloc.h" |
+#include "internal_logging.h" |
+#include "base/logging.h" |
+#include "base/commandlineflags.h" |
+#include "base/spinlock.h" |
+ |
+// On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old |
+// form of the name instead. |
+#ifndef MAP_ANONYMOUS |
+# define MAP_ANONYMOUS MAP_ANON |
+#endif |
+ |
+// Solaris has a bug where it doesn't declare madvise() for C++. |
+// http://www.opensolaris.org/jive/thread.jspa?threadID=21035&tstart=0 |
+#if defined(__sun) && defined(__SVR4) |
+# include <sys/types.h> // for caddr_t |
+ extern "C" { extern int madvise(caddr_t, size_t, int); } |
+#endif |
+ |
+// Set kDebugMode mode so that we can have use C++ conditionals |
+// instead of preprocessor conditionals. |
+#ifdef NDEBUG |
+static const bool kDebugMode = false; |
+#else |
+static const bool kDebugMode = true; |
+#endif |
+ |
+// Structure for discovering alignment |
+union MemoryAligner { |
+ void* p; |
+ double d; |
+ size_t s; |
+}; |
+ |
+static SpinLock spinlock(SpinLock::LINKER_INITIALIZED); |
+ |
+#if defined(HAVE_MMAP) || defined(MADV_DONTNEED) |
+// Page size is initialized on demand (only needed for mmap-based allocators) |
+static size_t pagesize = 0; |
+#endif |
+ |
+// Configuration parameters. |
+ |
+DEFINE_int32(malloc_devmem_start, |
+ EnvToInt("TCMALLOC_DEVMEM_START", 0), |
+ "Physical memory starting location in MB for /dev/mem allocation." |
+ " Setting this to 0 disables /dev/mem allocation"); |
+DEFINE_int32(malloc_devmem_limit, |
+ EnvToInt("TCMALLOC_DEVMEM_LIMIT", 0), |
+ "Physical memory limit location in MB for /dev/mem allocation." |
+ " Setting this to 0 means no limit."); |
+DEFINE_bool(malloc_skip_sbrk, |
+ EnvToBool("TCMALLOC_SKIP_SBRK", false), |
+ "Whether sbrk can be used to obtain memory."); |
+DEFINE_bool(malloc_skip_mmap, |
+ EnvToBool("TCMALLOC_SKIP_MMAP", false), |
+ "Whether mmap can be used to obtain memory."); |
+ |
+// static allocators |
+class SbrkSysAllocator : public SysAllocator { |
+public: |
+ SbrkSysAllocator() : SysAllocator() { |
+ } |
+ void* Alloc(size_t size, size_t *actual_size, size_t alignment); |
+ void DumpStats(TCMalloc_Printer* printer); |
+}; |
+static char sbrk_space[sizeof(SbrkSysAllocator)]; |
+ |
+class MmapSysAllocator : public SysAllocator { |
+public: |
+ MmapSysAllocator() : SysAllocator() { |
+ } |
+ void* Alloc(size_t size, size_t *actual_size, size_t alignment); |
+ void DumpStats(TCMalloc_Printer* printer); |
+}; |
+static char mmap_space[sizeof(MmapSysAllocator)]; |
+ |
+class DevMemSysAllocator : public SysAllocator { |
+public: |
+ DevMemSysAllocator() : SysAllocator() { |
+ } |
+ void* Alloc(size_t size, size_t *actual_size, size_t alignment); |
+ void DumpStats(TCMalloc_Printer* printer); |
+}; |
+static char devmem_space[sizeof(DevMemSysAllocator)]; |
+ |
+static const int kStaticAllocators = 3; |
+// kMaxDynamicAllocators + kStaticAllocators; |
+static const int kMaxAllocators = 5; |
+static SysAllocator *allocators[kMaxAllocators]; |
+ |
+bool RegisterSystemAllocator(SysAllocator *a, int priority) { |
+ SpinLockHolder lock_holder(&spinlock); |
+ |
+ // No two allocators should have a priority conflict, since the order |
+ // is determined at compile time. |
+ CHECK_CONDITION(allocators[priority] == NULL); |
+ allocators[priority] = a; |
+ return true; |
+} |
+ |
+ |
+void* SbrkSysAllocator::Alloc(size_t size, size_t *actual_size, |
+ size_t alignment) { |
+ // Check if we should use sbrk allocation. |
+ // FLAGS_malloc_skip_sbrk starts out as false (its uninitialized |
+ // state) and eventually gets initialized to the specified value. Note |
+ // that this code runs for a while before the flags are initialized. |
+ // That means that even if this flag is set to true, some (initial) |
+ // memory will be allocated with sbrk before the flag takes effect. |
+ if (FLAGS_malloc_skip_sbrk) { |
+ return NULL; |
+ } |
+ |
+ // sbrk will release memory if passed a negative number, so we do |
+ // a strict check here |
+ if (static_cast<ptrdiff_t>(size + alignment) < 0) return NULL; |
+ |
+ // could theoretically return the "extra" bytes here, but this |
+ // is simple and correct. |
+ if (actual_size) { |
+ *actual_size = size; |
+ } |
+ |
+ // This doesn't overflow because TCMalloc_SystemAlloc has already |
+ // tested for overflow at the alignment boundary. |
+ size = ((size + alignment - 1) / alignment) * alignment; |
+ |
+ // Check that we we're not asking for so much more memory that we'd |
+ // wrap around the end of the virtual address space. (This seems |
+ // like something sbrk() should check for us, and indeed opensolaris |
+ // does, but glibc does not: |
+ // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/sys/sbrk.c?a=true |
+ // http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/libc/misc/sbrk.c?rev=1.1.2.1&content-type=text/plain&cvsroot=glibc |
+ // Without this check, sbrk may succeed when it ought to fail.) |
+ if (reinterpret_cast<intptr_t>(sbrk(0)) + size < size) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ |
+ void* result = sbrk(size); |
+ if (result == reinterpret_cast<void*>(-1)) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ |
+ // Is it aligned? |
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(result); |
+ if ((ptr & (alignment-1)) == 0) return result; |
+ |
+ // Try to get more memory for alignment |
+ size_t extra = alignment - (ptr & (alignment-1)); |
+ void* r2 = sbrk(extra); |
+ if (reinterpret_cast<uintptr_t>(r2) == (ptr + size)) { |
+ // Contiguous with previous result |
+ return reinterpret_cast<void*>(ptr + extra); |
+ } |
+ |
+ // Give up and ask for "size + alignment - 1" bytes so |
+ // that we can find an aligned region within it. |
+ result = sbrk(size + alignment - 1); |
+ if (result == reinterpret_cast<void*>(-1)) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ ptr = reinterpret_cast<uintptr_t>(result); |
+ if ((ptr & (alignment-1)) != 0) { |
+ ptr += alignment - (ptr & (alignment-1)); |
+ } |
+ return reinterpret_cast<void*>(ptr); |
+} |
+ |
+void SbrkSysAllocator::DumpStats(TCMalloc_Printer* printer) { |
+ printer->printf("SbrkSysAllocator: failed_=%d\n", failed_); |
+} |
+ |
+void* MmapSysAllocator::Alloc(size_t size, size_t *actual_size, |
+ size_t alignment) { |
+#ifndef HAVE_MMAP |
+ failed_ = true; |
+ return NULL; |
+#else |
+ // Check if we should use mmap allocation. |
+ // FLAGS_malloc_skip_mmap starts out as false (its uninitialized |
+ // state) and eventually gets initialized to the specified value. Note |
+ // that this code runs for a while before the flags are initialized. |
+ // Chances are we never get here before the flags are initialized since |
+ // sbrk is used until the heap is exhausted (before mmap is used). |
+ if (FLAGS_malloc_skip_mmap) { |
+ return NULL; |
+ } |
+ |
+ // could theoretically return the "extra" bytes here, but this |
+ // is simple and correct. |
+ if (actual_size) { |
+ *actual_size = size; |
+ } |
+ |
+ // Enforce page alignment |
+ if (pagesize == 0) pagesize = getpagesize(); |
+ if (alignment < pagesize) alignment = pagesize; |
+ size_t aligned_size = ((size + alignment - 1) / alignment) * alignment; |
+ if (aligned_size < size) { |
+ return NULL; |
+ } |
+ size = aligned_size; |
+ |
+ // Ask for extra memory if alignment > pagesize |
+ size_t extra = 0; |
+ if (alignment > pagesize) { |
+ extra = alignment - pagesize; |
+ } |
+ |
+ // Note: size + extra does not overflow since: |
+ // size + alignment < (1<<NBITS). |
+ // and extra <= alignment |
+ // therefore size + extra < (1<<NBITS) |
+ void* result = mmap(NULL, size + extra, |
+ PROT_READ|PROT_WRITE, |
+ MAP_PRIVATE|MAP_ANONYMOUS, |
+ -1, 0); |
+ if (result == reinterpret_cast<void*>(MAP_FAILED)) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ |
+ // Adjust the return memory so it is aligned |
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(result); |
+ size_t adjust = 0; |
+ if ((ptr & (alignment - 1)) != 0) { |
+ adjust = alignment - (ptr & (alignment - 1)); |
+ } |
+ |
+ // Return the unused memory to the system |
+ if (adjust > 0) { |
+ munmap(reinterpret_cast<void*>(ptr), adjust); |
+ } |
+ if (adjust < extra) { |
+ munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust); |
+ } |
+ |
+ ptr += adjust; |
+ return reinterpret_cast<void*>(ptr); |
+#endif // HAVE_MMAP |
+} |
+ |
+void MmapSysAllocator::DumpStats(TCMalloc_Printer* printer) { |
+ printer->printf("MmapSysAllocator: failed_=%d\n", failed_); |
+} |
+ |
+void* DevMemSysAllocator::Alloc(size_t size, size_t *actual_size, |
+ size_t alignment) { |
+#ifndef HAVE_MMAP |
+ failed_ = true; |
+ return NULL; |
+#else |
+ static bool initialized = false; |
+ static off_t physmem_base; // next physical memory address to allocate |
+ static off_t physmem_limit; // maximum physical address allowed |
+ static int physmem_fd; // file descriptor for /dev/mem |
+ |
+ // Check if we should use /dev/mem allocation. Note that it may take |
+ // a while to get this flag initialized, so meanwhile we fall back to |
+ // the next allocator. (It looks like 7MB gets allocated before |
+ // this flag gets initialized -khr.) |
+ if (FLAGS_malloc_devmem_start == 0) { |
+ // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to |
+ // try us again next time. |
+ return NULL; |
+ } |
+ |
+ if (!initialized) { |
+ physmem_fd = open("/dev/mem", O_RDWR); |
+ if (physmem_fd < 0) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ physmem_base = FLAGS_malloc_devmem_start*1024LL*1024LL; |
+ physmem_limit = FLAGS_malloc_devmem_limit*1024LL*1024LL; |
+ initialized = true; |
+ } |
+ |
+ // could theoretically return the "extra" bytes here, but this |
+ // is simple and correct. |
+ if (actual_size) { |
+ *actual_size = size; |
+ } |
+ |
+ // Enforce page alignment |
+ if (pagesize == 0) pagesize = getpagesize(); |
+ if (alignment < pagesize) alignment = pagesize; |
+ size_t aligned_size = ((size + alignment - 1) / alignment) * alignment; |
+ if (aligned_size < size) { |
+ return NULL; |
+ } |
+ size = aligned_size; |
+ |
+ // Ask for extra memory if alignment > pagesize |
+ size_t extra = 0; |
+ if (alignment > pagesize) { |
+ extra = alignment - pagesize; |
+ } |
+ |
+ // check to see if we have any memory left |
+ if (physmem_limit != 0 && |
+ ((size + extra) > (physmem_limit - physmem_base))) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ |
+ // Note: size + extra does not overflow since: |
+ // size + alignment < (1<<NBITS). |
+ // and extra <= alignment |
+ // therefore size + extra < (1<<NBITS) |
+ void *result = mmap(0, size + extra, PROT_WRITE|PROT_READ, |
+ MAP_SHARED, physmem_fd, physmem_base); |
+ if (result == reinterpret_cast<void*>(MAP_FAILED)) { |
+ failed_ = true; |
+ return NULL; |
+ } |
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(result); |
+ |
+ // Adjust the return memory so it is aligned |
+ size_t adjust = 0; |
+ if ((ptr & (alignment - 1)) != 0) { |
+ adjust = alignment - (ptr & (alignment - 1)); |
+ } |
+ |
+ // Return the unused virtual memory to the system |
+ if (adjust > 0) { |
+ munmap(reinterpret_cast<void*>(ptr), adjust); |
+ } |
+ if (adjust < extra) { |
+ munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust); |
+ } |
+ |
+ ptr += adjust; |
+ physmem_base += adjust + size; |
+ |
+ return reinterpret_cast<void*>(ptr); |
+#endif // HAVE_MMAP |
+} |
+ |
+void DevMemSysAllocator::DumpStats(TCMalloc_Printer* printer) { |
+ printer->printf("DevMemSysAllocator: failed_=%d\n", failed_); |
+} |
+ |
+static bool system_alloc_inited = false; |
+void InitSystemAllocators(void) { |
+ // This determines the order in which system allocators are called |
+ int i = kMaxDynamicAllocators; |
+ allocators[i++] = new (devmem_space) DevMemSysAllocator(); |
+ |
+ // In 64-bit debug mode, place the mmap allocator first since it |
+ // allocates pointers that do not fit in 32 bits and therefore gives |
+ // us better testing of code's 64-bit correctness. It also leads to |
+ // less false negatives in heap-checking code. (Numbers are less |
+ // likely to look like pointers and therefore the conservative gc in |
+ // the heap-checker is less likely to misinterpret a number as a |
+ // pointer). |
+ if (kDebugMode && sizeof(void*) > 4) { |
+ allocators[i++] = new (mmap_space) MmapSysAllocator(); |
+ allocators[i++] = new (sbrk_space) SbrkSysAllocator(); |
+ } else { |
+ allocators[i++] = new (sbrk_space) SbrkSysAllocator(); |
+ allocators[i++] = new (mmap_space) MmapSysAllocator(); |
+ } |
+} |
+ |
+void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, |
+ size_t alignment) { |
+ // Discard requests that overflow |
+ if (size + alignment < size) return NULL; |
+ |
+ SpinLockHolder lock_holder(&spinlock); |
+ |
+ if (!system_alloc_inited) { |
+ InitSystemAllocators(); |
+ system_alloc_inited = true; |
+ } |
+ |
+ // Enforce minimum alignment |
+ if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner); |
+ |
+ // Try twice, once avoiding allocators that failed before, and once |
+ // more trying all allocators even if they failed before. |
+ for (int i = 0; i < 2; i++) { |
+ for (int j = 0; j < kMaxAllocators; j++) { |
+ SysAllocator *a = allocators[j]; |
+ if (a == NULL) continue; |
+ if (a->usable_ && !a->failed_) { |
+ void* result = a->Alloc(size, actual_size, alignment); |
+ if (result != NULL) return result; |
+ } |
+ } |
+ |
+ // nothing worked - reset failed_ flags and try again |
+ for (int j = 0; j < kMaxAllocators; j++) { |
+ SysAllocator *a = allocators[j]; |
+ if (a == NULL) continue; |
+ a->failed_ = false; |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+void TCMalloc_SystemRelease(void* start, size_t length) { |
+#ifdef MADV_DONTNEED |
+ if (FLAGS_malloc_devmem_start) { |
+ // It's not safe to use MADV_DONTNEED if we've been mapping |
+ // /dev/mem for heap memory |
+ return; |
+ } |
+ if (pagesize == 0) pagesize = getpagesize(); |
+ const size_t pagemask = pagesize - 1; |
+ |
+ size_t new_start = reinterpret_cast<size_t>(start); |
+ size_t end = new_start + length; |
+ size_t new_end = end; |
+ |
+ // Round up the starting address and round down the ending address |
+ // to be page aligned: |
+ new_start = (new_start + pagesize - 1) & ~pagemask; |
+ new_end = new_end & ~pagemask; |
+ |
+ ASSERT((new_start & pagemask) == 0); |
+ ASSERT((new_end & pagemask) == 0); |
+ ASSERT(new_start >= reinterpret_cast<size_t>(start)); |
+ ASSERT(new_end <= end); |
+ |
+ if (new_end > new_start) { |
+ // Note -- ignoring most return codes, because if this fails it |
+ // doesn't matter... |
+ while (madvise(reinterpret_cast<char*>(new_start), new_end - new_start, |
+ MADV_DONTNEED) == -1 && |
+ errno == EAGAIN) { |
+ // NOP |
+ } |
+ } |
+#endif |
+} |
+ |
+void TCMalloc_SystemCommit(void* start, size_t length) { |
+ // Nothing to do here. TCMalloc_SystemRelease does not alter pages |
+ // such that they need to be re-committed before they can be used by the |
+ // application. |
+} |
+ |
+void DumpSystemAllocatorStats(TCMalloc_Printer* printer) { |
+ for (int j = 0; j < kMaxAllocators; j++) { |
+ SysAllocator *a = allocators[j]; |
+ if (a == NULL) continue; |
+ if (a->usable_) { |
+ a->DumpStats(printer); |
+ } |
+ } |
+} |