| Index: third_party/tcmalloc/allocator_shim.cc
|
| ===================================================================
|
| --- third_party/tcmalloc/allocator_shim.cc (revision 0)
|
| +++ third_party/tcmalloc/allocator_shim.cc (revision 0)
|
| @@ -0,0 +1,256 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <config.h>
|
| +
|
| +// When defined, different heap allocators can be used via an environment
|
| +// variable set before running the program. This may reduce the amount
|
| +// of inlining that we get with malloc/free/etc. Disabling makes it
|
| +// so that only tcmalloc can be used.
|
| +#define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| +
|
| +// TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
|
| +// from the "user code" so that debugging tools (HeapChecker) can work.
|
| +
|
| +// __THROW is defined in glibc systems. It means, counter-intuitively,
|
| +// "This function will never throw an exception." It's an optional
|
| +// optimization tool, but we may need to use it to match glibc prototypes.
|
| +#ifndef __THROW // I guess we're not on a glibc system
|
| +# define __THROW // __THROW is just an optimization, so ok to make it ""
|
| +#endif
|
| +
|
| +// new_mode behaves similarly to MSVC's _set_new_mode.
|
| +// If flag is 0 (default), calls to malloc will behave normally.
|
| +// If flag is 1, calls to malloc will behave like calls to new,
|
| +// and the std_new_handler will be invoked on failure.
|
| +// Can be set by calling _set_new_mode().
|
| +static int new_mode = 0;
|
| +
|
| +typedef enum {
|
| + TCMALLOC, // TCMalloc is the default allocator.
|
| + JEMALLOC, // JEMalloc
|
| + WINDEFAULT, // Windows Heap
|
| + WINLFH, // Windows LFH Heap
|
| +} Allocator;
|
| +
|
| +// This is the default allocator.
|
| +static Allocator allocator = TCMALLOC;
|
| +
|
| +// We include tcmalloc and the win_allocator to get as much inlining as
|
| +// possible.
|
| +#include "tcmalloc.cc"
|
| +#include "win_allocator.cc"
|
| +
|
| +// Forward declarations from jemalloc.
|
| +extern "C" {
|
| +void* je_malloc(size_t s);
|
| +void* je_realloc(void* p, size_t s);
|
| +void je_free(void* s);
|
| +size_t je_msize(void* p);
|
| +bool je_malloc_init_hard();
|
| +}
|
| +
|
| +extern "C" {
|
| +
|
| +// Call the new handler, if one has been set.
|
| +// Returns true on successfully calling the handler, false otherwise.
|
| +inline bool call_new_handler(bool nothrow) {
|
| + // Get the current new handler. NB: this function is not
|
| + // thread-safe. We make a feeble stab at making it so here, but
|
| + // this lock only protects against tcmalloc interfering with
|
| + // itself, not with other libraries calling set_new_handler.
|
| + std::new_handler nh;
|
| + {
|
| + SpinLockHolder h(&set_new_handler_lock);
|
| + nh = std::set_new_handler(0);
|
| + (void) std::set_new_handler(nh);
|
| + }
|
| +#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
|
| + if (!nh)
|
| + return false;
|
| + // Since exceptions are disabled, we don't really know if new_handler
|
| + // failed. Assume it will abort if it fails.
|
| + (*nh)();
|
| + return true;
|
| +#else
|
| + // If no new_handler is established, the allocation failed.
|
| + if (!nh) {
|
| + if (nothrow)
|
| + return 0;
|
| + throw std::bad_alloc();
|
| + }
|
| + // Otherwise, try the new_handler. If it returns, retry the
|
| + // allocation. If it throws std::bad_alloc, fail the allocation.
|
| + // if it throws something else, don't interfere.
|
| + try {
|
| + (*nh)();
|
| + } catch (const std::bad_alloc&) {
|
| + if (!nothrow)
|
| + throw;
|
| + return p;
|
| + }
|
| +#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
|
| +}
|
| +
|
| +void* malloc(size_t size) __THROW {
|
| + void* ptr;
|
| + for (;;) {
|
| +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| + switch (allocator) {
|
| + case JEMALLOC:
|
| + ptr = je_malloc(size);
|
| + break;
|
| + case WINDEFAULT:
|
| + case WINLFH:
|
| + ptr = win_heap_malloc(size);
|
| + break;
|
| + case TCMALLOC:
|
| + default:
|
| + ptr = do_malloc(size);
|
| + break;
|
| + }
|
| +#else
|
| + // TCMalloc case.
|
| + ptr = do_malloc(size);
|
| +#endif
|
| + if (ptr)
|
| + return ptr;
|
| +
|
| + if (!new_mode || !call_new_handler(true))
|
| + break;
|
| + }
|
| + return ptr;
|
| +}
|
| +
|
| +void free(void* p) __THROW {
|
| +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| + switch (allocator) {
|
| + case JEMALLOC:
|
| + je_free(p);
|
| + return;
|
| + case WINDEFAULT:
|
| + case WINLFH:
|
| + win_heap_free(p);
|
| + return;
|
| + }
|
| +#endif
|
| + // TCMalloc case.
|
| + do_free(p);
|
| +}
|
| +
|
| +void* realloc(void* ptr, size_t size) __THROW {
|
| + void* new_ptr;
|
| + for (;;) {
|
| +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| + switch (allocator) {
|
| + case JEMALLOC:
|
| + new_ptr = je_realloc(ptr, size);
|
| + break;
|
| + case WINDEFAULT:
|
| + case WINLFH:
|
| + new_ptr = win_heap_realloc(ptr, size);
|
| + break;
|
| + case TCMALLOC:
|
| + default:
|
| + new_ptr = do_realloc(ptr, size);
|
| + break;
|
| + }
|
| +#else
|
| + // TCMalloc case.
|
| + new_ptr = do_realloc(ptr, size);
|
| +#endif
|
| + if (new_ptr)
|
| + return new_ptr;
|
| + if (!new_mode || !call_new_handler(true))
|
| + break;
|
| + }
|
| + return new_ptr;
|
| +}
|
| +
|
| +// TODO(mbelshe): Implement this for other allocators.
|
| +void malloc_stats(void) __THROW {
|
| +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| + switch (allocator) {
|
| + case JEMALLOC:
|
| + // No stats.
|
| + return;
|
| + case WINDEFAULT:
|
| + case WINLFH:
|
| + // No stats.
|
| + return;
|
| + }
|
| +#endif
|
| + tc_malloc_stats();
|
| +}
|
| +
|
| +#ifdef WIN32
|
| +
|
| +extern "C" size_t _msize(void* p) {
|
| +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| + switch (allocator) {
|
| + case JEMALLOC:
|
| + return je_msize(p);
|
| + case WINDEFAULT:
|
| + case WINLFH:
|
| + return win_heap_msize(p);
|
| + }
|
| +#endif
|
| + return MallocExtension::instance()->GetAllocatedSize(p);
|
| +}
|
| +
|
| +// This is included to resolve references from libcmt.
|
| +extern "C" intptr_t _get_heap_handle() {
|
| + return 0;
|
| +}
|
| +
|
| +// The CRT heap initialization stub.
|
| +extern "C" int _heap_init() {
|
| +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
|
| + const char* override = GetenvBeforeMain("CHROME_ALLOCATOR");
|
| + if (override) {
|
| + if (!stricmp(override, "jemalloc"))
|
| + allocator = JEMALLOC;
|
| + else if (!stricmp(override, "winheap"))
|
| + allocator = WINDEFAULT;
|
| + else if (!stricmp(override, "winlfh"))
|
| + allocator = WINLFH;
|
| + else if (!stricmp(override, "tcmalloc"))
|
| + allocator = TCMALLOC;
|
| + }
|
| +
|
| + switch (allocator) {
|
| + case JEMALLOC:
|
| + return je_malloc_init_hard() ? 0 : 1;
|
| + case WINDEFAULT:
|
| + return win_heap_init(false) ? 1 : 0;
|
| + case WINLFH:
|
| + return win_heap_init(true) ? 1 : 0;
|
| + case TCMALLOC:
|
| + default:
|
| + // fall through
|
| + break;
|
| + }
|
| +#endif
|
| + // Initializing tcmalloc.
|
| + // We intentionally leak this object. It lasts for the process
|
| + // lifetime. Trying to teardown at _heap_term() is so late that
|
| + // you can't do anything useful anyway.
|
| + new TCMallocGuard();
|
| + return 1;
|
| +}
|
| +
|
| +// The CRT heap cleanup stub.
|
| +extern "C" void _heap_term() {}
|
| +
|
| +// We set this to 1 because part of the CRT uses a check of _crtheap != 0
|
| +// to test whether the CRT has been initialized. Once we've ripped out
|
| +// the allocators from libcmt, we need to provide this definition so that
|
| +// the rest of the CRT is still usable.
|
| +extern "C" void* _crtheap = reinterpret_cast<void*>(1);
|
| +
|
| +#endif // WIN32
|
| +
|
| +#include "generic_allocators.cc"
|
| +
|
| +} // extern C
|
|
|