1 // Copyright (c) 2012 The Chromium 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.
5 #include "base/allocator/allocator_shim.h"
8 #include "base/allocator/allocator_extension_thunks.h"
9 #include "base/profiler/alternate_timer.h"
10 #include "base/sysinfo.h"
13 // When defined, different heap allocators can be used via an environment
14 // variable set before running the program. This may reduce the amount
15 // of inlining that we get with malloc/free/etc. Disabling makes it
16 // so that only tcmalloc can be used.
17 #define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
19 // TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
20 // from the "user code" so that debugging tools (HeapChecker) can work.
22 // __THROW is defined in glibc systems. It means, counter-intuitively,
23 // "This function will never throw an exception." It's an optional
24 // optimization tool, but we may need to use it to match glibc prototypes.
25 #ifndef __THROW // I guess we're not on a glibc system
26 # define __THROW // __THROW is just an optimization, so ok to make it ""
29 // new_mode behaves similarly to MSVC's _set_new_mode.
30 // If flag is 0 (default), calls to malloc will behave normally.
31 // If flag is 1, calls to malloc will behave like calls to new,
32 // and the std_new_handler will be invoked on failure.
33 // Can be set by calling _set_new_mode().
34 static int new_mode
= 0;
37 TCMALLOC
, // TCMalloc is the default allocator.
38 JEMALLOC
, // JEMalloc.
39 WINHEAP
, // Windows Heap (standard Windows allocator).
40 WINLFH
, // Windows LFH Heap.
43 // This is the default allocator. This value can be changed at startup by
44 // specifying environment variables shown below it.
45 // See SetupSubprocessAllocator() to specify a default secondary (subprocess)
47 // TODO(jar): Switch to using TCMALLOC for the renderer as well.
48 #if (defined(ADDRESS_SANITIZER) && defined(OS_WIN))
49 // The Windows implementation of Asan requires the use of "WINHEAP".
50 static Allocator allocator
= WINHEAP
;
52 static Allocator allocator
= TCMALLOC
;
54 // The names of the environment variables that can optionally control the
55 // selection of the allocator. The primary may be used to control overall
56 // allocator selection, and the secondary can be used to specify an allocator
57 // to use in sub-processes.
58 static const char primary_name
[] = "CHROME_ALLOCATOR";
59 static const char secondary_name
[] = "CHROME_ALLOCATOR_2";
61 // We include tcmalloc and the win_allocator to get as much inlining as
63 #include "debugallocation_shim.cc"
64 #include "win_allocator.cc"
66 // Forward declarations from jemalloc.
68 void* je_malloc(size_t s
);
69 void* je_realloc(void* p
, size_t s
);
70 void je_free(void* s
);
71 size_t je_msize(void* p
);
72 bool je_malloc_init_hard();
73 void* je_memalign(size_t a
, size_t s
);
76 // Call the new handler, if one has been set.
77 // Returns true on successfully calling the handler, false otherwise.
78 inline bool call_new_handler(bool nothrow
) {
79 // Get the current new handler. NB: this function is not
80 // thread-safe. We make a feeble stab at making it so here, but
81 // this lock only protects against tcmalloc interfering with
82 // itself, not with other libraries calling set_new_handler.
85 SpinLockHolder
h(&set_new_handler_lock
);
86 nh
= std::set_new_handler(0);
87 (void) std::set_new_handler(nh
);
89 #if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
90 (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
93 // Since exceptions are disabled, we don't really know if new_handler
94 // failed. Assume it will abort if it fails.
96 return false; // break out of the retry loop.
98 // If no new_handler is established, the allocation failed.
102 throw std::bad_alloc();
104 // Otherwise, try the new_handler. If it returns, retry the
105 // allocation. If it throws std::bad_alloc, fail the allocation.
106 // if it throws something else, don't interfere.
109 } catch (const std::bad_alloc
&) {
114 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
119 void* malloc(size_t size
) __THROW
{
122 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
125 ptr
= je_malloc(size
);
129 ptr
= win_heap_malloc(size
);
133 ptr
= do_malloc(size
);
138 ptr
= do_malloc(size
);
143 if (!new_mode
|| !call_new_handler(true))
149 void free(void* p
) __THROW
{
150 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
165 void* realloc(void* ptr
, size_t size
) __THROW
{
166 // Webkit is brittle for allocators that return NULL for malloc(0). The
167 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
168 // to call malloc for this case.
174 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
177 new_ptr
= je_realloc(ptr
, size
);
181 new_ptr
= win_heap_realloc(ptr
, size
);
185 new_ptr
= do_realloc(ptr
, size
);
190 new_ptr
= do_realloc(ptr
, size
);
193 // Subtle warning: NULL return does not alwas indicate out-of-memory. If
194 // the requested new size is zero, realloc should free the ptr and return
196 if (new_ptr
|| !size
)
198 if (!new_mode
|| !call_new_handler(true))
204 // TODO(mbelshe): Implement this for other allocators.
205 void malloc_stats(void) __THROW
{
206 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
222 extern "C" size_t _msize(void* p
) {
223 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
229 return win_heap_msize(p
);
232 return MallocExtension::instance()->GetAllocatedSize(p
);
235 // This is included to resolve references from libcmt.
236 extern "C" intptr_t _get_heap_handle() {
240 static bool get_allocator_waste_size_thunk(size_t* size
) {
241 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
246 // TODO(alexeif): Implement for allocators other than tcmalloc.
250 size_t heap_size
, allocated_bytes
, unmapped_bytes
;
251 MallocExtension
* ext
= MallocExtension::instance();
252 if (ext
->GetNumericProperty("generic.heap_size", &heap_size
) &&
253 ext
->GetNumericProperty("generic.current_allocated_bytes",
255 ext
->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes",
257 *size
= heap_size
- allocated_bytes
- unmapped_bytes
;
263 static void get_stats_thunk(char* buffer
, int buffer_length
) {
264 MallocExtension::instance()->GetStats(buffer
, buffer_length
);
267 static void release_free_memory_thunk() {
268 MallocExtension::instance()->ReleaseFreeMemory();
271 // The CRT heap initialization stub.
272 extern "C" int _heap_init() {
273 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
274 // Don't use the environment variable if ADDRESS_SANITIZER is defined on
275 // Windows, as the implementation requires Winheap to be the allocator.
276 #if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN))
277 const char* environment_value
= GetenvBeforeMain(primary_name
);
278 if (environment_value
) {
279 if (!stricmp(environment_value
, "jemalloc"))
280 allocator
= JEMALLOC
;
281 else if (!stricmp(environment_value
, "winheap"))
283 else if (!stricmp(environment_value
, "winlfh"))
285 else if (!stricmp(environment_value
, "tcmalloc"))
286 allocator
= TCMALLOC
;
292 return je_malloc_init_hard() ? 0 : 1;
294 return win_heap_init(false) ? 1 : 0;
296 return win_heap_init(true) ? 1 : 0;
303 // Initializing tcmalloc.
304 // We intentionally leak this object. It lasts for the process
305 // lifetime. Trying to teardown at _heap_term() is so late that
306 // you can't do anything useful anyway.
309 // Provide optional hook for monitoring allocation quantities on a per-thread
310 // basis. Only set the hook if the environment indicates this needs to be
312 const char* profiling
=
313 GetenvBeforeMain(tracked_objects::kAlternateProfilerTime
);
314 if (profiling
&& *profiling
== '1') {
315 tracked_objects::SetAlternateTimeSource(
316 tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread
,
317 tracked_objects::TIME_SOURCE_TYPE_TCMALLOC
);
320 base::allocator::thunks::SetGetAllocatorWasteSizeFunction(
321 get_allocator_waste_size_thunk
);
322 base::allocator::thunks::SetGetStatsFunction(get_stats_thunk
);
323 base::allocator::thunks::SetReleaseFreeMemoryFunction(
324 release_free_memory_thunk
);
329 // The CRT heap cleanup stub.
330 extern "C" void _heap_term() {}
332 // We set this to 1 because part of the CRT uses a check of _crtheap != 0
333 // to test whether the CRT has been initialized. Once we've ripped out
334 // the allocators from libcmt, we need to provide this definition so that
335 // the rest of the CRT is still usable.
336 extern "C" void* _crtheap
= reinterpret_cast<void*>(1);
338 // Provide support for aligned memory through Windows only _aligned_malloc().
339 void* _aligned_malloc(size_t size
, size_t alignment
) {
340 // _aligned_malloc guarantees parameter validation, so do so here. These
341 // checks are somewhat stricter than _aligned_malloc() since we're effectively
342 // using memalign() under the hood.
344 DCHECK_EQ(alignment
& (alignment
- 1), 0U);
345 DCHECK_EQ(alignment
% sizeof(void*), 0U);
349 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
352 ptr
= je_memalign(alignment
, size
);
356 ptr
= win_heap_memalign(alignment
, size
);
360 ptr
= tc_memalign(alignment
, size
);
365 ptr
= tc_memalign(alignment
, size
);
368 // Sanity check alignment.
369 DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr
) & (alignment
- 1), 0U);
373 if (!new_mode
|| !call_new_handler(true))
379 void _aligned_free(void* p
) {
380 // Both JEMalloc and TCMalloc return pointers from memalign() that are safe to
381 // use with free(). Pointers allocated with win_heap_memalign() MUST be freed
382 // via win_heap_memalign_free() since the aligned pointer is not the real one.
383 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
390 win_heap_memalign_free(p
);
400 #include "generic_allocators.cc"
405 namespace allocator
{
407 void SetupSubprocessAllocator() {
408 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
409 size_t primary_length
= 0;
410 getenv_s(&primary_length
, NULL
, 0, primary_name
);
412 size_t secondary_length
= 0;
414 getenv_s(&secondary_length
, buffer
, sizeof(buffer
), secondary_name
);
415 DCHECK_GT(sizeof(buffer
), secondary_length
);
416 buffer
[sizeof(buffer
) - 1] = '\0';
418 if (secondary_length
|| !primary_length
) {
419 // Don't use the environment variable if ADDRESS_SANITIZER is defined on
420 // Windows, as the implementation require Winheap to be the allocator.
421 #if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN))
422 const char* secondary_value
= secondary_length
? buffer
: "TCMALLOC";
423 // Force renderer (or other subprocesses) to use secondary_value.
425 const char* secondary_value
= "WINHEAP";
427 int ret_val
= _putenv_s(primary_name
, secondary_value
);
428 DCHECK_EQ(0, ret_val
);
430 #endif // ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
433 void* TCMallocDoMallocForTest(size_t size
) {
434 return do_malloc(size
);
437 void TCMallocDoFreeForTest(void* ptr
) {
441 size_t ExcludeSpaceForMarkForTest(size_t size
) {
442 return ExcludeSpaceForMark(size
);
445 } // namespace allocator.