Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(241)

Unified Diff: third_party/tcmalloc/system-alloc.cc

Issue 165275: Major changes to the Chrome allocator.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/tcmalloc/system-alloc.h ('k') | third_party/tcmalloc/tcmalloc.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
+ }
+ }
+}
« no previous file with comments | « third_party/tcmalloc/system-alloc.h ('k') | third_party/tcmalloc/tcmalloc.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698