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"
12 // This shim make it possible to use different allocators via an environment
13 // variable set before running the program. This may reduce the
14 // amount of inlining that we get with malloc/free/etc.
16 // TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
17 // from the "user code" so that debugging tools (HeapChecker) can work.
19 // __THROW is defined in glibc systems. It means, counter-intuitively,
20 // "This function will never throw an exception." It's an optional
21 // optimization tool, but we may need to use it to match glibc prototypes.
22 #ifndef __THROW // I guess we're not on a glibc system
23 # define __THROW // __THROW is just an optimization, so ok to make it ""
26 // new_mode behaves similarly to MSVC's _set_new_mode.
27 // If flag is 0 (default), calls to malloc will behave normally.
28 // If flag is 1, calls to malloc will behave like calls to new,
29 // and the std_new_handler will be invoked on failure.
30 // Can be set by calling _set_new_mode().
31 static int new_mode
= 0;
34 TCMALLOC
, // TCMalloc is the default allocator.
35 WINHEAP
, // Windows Heap (standard Windows allocator).
36 WINLFH
, // Windows LFH Heap.
39 // This is the default allocator. This value can be changed at startup by
40 // specifying environment variables shown below it.
41 // See SetupSubprocessAllocator() to specify a default secondary (subprocess)
43 // TODO(jar): Switch to using TCMALLOC for the renderer as well.
45 // SyzyASan requires the use of "WINHEAP".
46 static Allocator allocator
= WINHEAP
;
48 static Allocator allocator
= TCMALLOC
;
50 // The names of the environment variables that can optionally control the
51 // selection of the allocator. The primary may be used to control overall
52 // allocator selection, and the secondary can be used to specify an allocator
53 // to use in sub-processes.
54 static const char primary_name
[] = "CHROME_ALLOCATOR";
55 static const char secondary_name
[] = "CHROME_ALLOCATOR_2";
57 // We include tcmalloc and the win_allocator to get as much inlining as
59 #include "debugallocation_shim.cc"
60 #include "win_allocator.cc"
62 // Call the new handler, if one has been set.
63 // Returns true on successfully calling the handler, false otherwise.
64 inline bool call_new_handler(bool nothrow
) {
65 // Get the current new handler. NB: this function is not
66 // thread-safe. We make a feeble stab at making it so here, but
67 // this lock only protects against tcmalloc interfering with
68 // itself, not with other libraries calling set_new_handler.
71 SpinLockHolder
h(&set_new_handler_lock
);
72 nh
= std::set_new_handler(0);
73 (void) std::set_new_handler(nh
);
75 #if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
76 (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
79 // Since exceptions are disabled, we don't really know if new_handler
80 // failed. Assume it will abort if it fails.
82 return false; // break out of the retry loop.
84 // If no new_handler is established, the allocation failed.
88 throw std::bad_alloc();
90 // Otherwise, try the new_handler. If it returns, retry the
91 // allocation. If it throws std::bad_alloc, fail the allocation.
92 // if it throws something else, don't interfere.
95 } catch (const std::bad_alloc
&) {
100 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
105 void* malloc(size_t size
) __THROW
{
111 ptr
= win_heap_malloc(size
);
115 ptr
= do_malloc(size
);
121 if (!new_mode
|| !call_new_handler(true))
127 void free(void* p
) __THROW
{
139 void* realloc(void* ptr
, size_t size
) __THROW
{
140 // Webkit is brittle for allocators that return NULL for malloc(0). The
141 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
142 // to call malloc for this case.
151 new_ptr
= win_heap_realloc(ptr
, size
);
155 new_ptr
= do_realloc(ptr
, size
);
159 // Subtle warning: NULL return does not alwas indicate out-of-memory. If
160 // the requested new size is zero, realloc should free the ptr and return
162 if (new_ptr
|| !size
)
164 if (!new_mode
|| !call_new_handler(true))
170 // TODO(mbelshe): Implement this for other allocators.
171 void malloc_stats(void) __THROW
{
185 extern "C" size_t _msize(void* p
) {
189 return win_heap_msize(p
);
193 return MallocExtension::instance()->GetAllocatedSize(p
);
196 // This is included to resolve references from libcmt.
197 extern "C" intptr_t _get_heap_handle() {
201 static bool get_allocator_waste_size_thunk(size_t* size
) {
205 // TODO(alexeif): Implement for allocators other than tcmalloc.
208 size_t heap_size
, allocated_bytes
, unmapped_bytes
;
209 MallocExtension
* ext
= MallocExtension::instance();
210 if (ext
->GetNumericProperty("generic.heap_size", &heap_size
) &&
211 ext
->GetNumericProperty("generic.current_allocated_bytes",
213 ext
->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes",
215 *size
= heap_size
- allocated_bytes
- unmapped_bytes
;
221 static void get_stats_thunk(char* buffer
, int buffer_length
) {
222 MallocExtension::instance()->GetStats(buffer
, buffer_length
);
225 static void release_free_memory_thunk() {
226 MallocExtension::instance()->ReleaseFreeMemory();
229 // The CRT heap initialization stub.
230 extern "C" int _heap_init() {
231 // Don't use the environment variable if SYZYASAN is defined, as the
232 // implementation requires Winheap to be the allocator.
233 #if !defined(SYZYASAN)
234 const char* environment_value
= GetenvBeforeMain(primary_name
);
235 if (environment_value
) {
236 if (!stricmp(environment_value
, "winheap"))
238 else if (!stricmp(environment_value
, "winlfh"))
240 else if (!stricmp(environment_value
, "tcmalloc"))
241 allocator
= TCMALLOC
;
247 return win_heap_init(false) ? 1 : 0;
249 return win_heap_init(true) ? 1 : 0;
256 // Initializing tcmalloc.
257 // We intentionally leak this object. It lasts for the process
258 // lifetime. Trying to teardown at _heap_term() is so late that
259 // you can't do anything useful anyway.
262 // Provide optional hook for monitoring allocation quantities on a per-thread
263 // basis. Only set the hook if the environment indicates this needs to be
265 const char* profiling
=
266 GetenvBeforeMain(tracked_objects::kAlternateProfilerTime
);
267 if (profiling
&& *profiling
== '1') {
268 tracked_objects::SetAlternateTimeSource(
269 tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread
,
270 tracked_objects::TIME_SOURCE_TYPE_TCMALLOC
);
273 base::allocator::thunks::SetGetAllocatorWasteSizeFunction(
274 get_allocator_waste_size_thunk
);
275 base::allocator::thunks::SetGetStatsFunction(get_stats_thunk
);
276 base::allocator::thunks::SetReleaseFreeMemoryFunction(
277 release_free_memory_thunk
);
282 // The CRT heap cleanup stub.
283 extern "C" void _heap_term() {}
285 // We set this to 1 because part of the CRT uses a check of _crtheap != 0
286 // to test whether the CRT has been initialized. Once we've ripped out
287 // the allocators from libcmt, we need to provide this definition so that
288 // the rest of the CRT is still usable.
289 extern "C" void* _crtheap
= reinterpret_cast<void*>(1);
291 // Provide support for aligned memory through Windows only _aligned_malloc().
292 void* _aligned_malloc(size_t size
, size_t alignment
) {
293 // _aligned_malloc guarantees parameter validation, so do so here. These
294 // checks are somewhat stricter than _aligned_malloc() since we're effectively
295 // using memalign() under the hood.
297 DCHECK_EQ(alignment
& (alignment
- 1), 0U);
298 DCHECK_EQ(alignment
% sizeof(void*), 0U);
305 ptr
= win_heap_memalign(alignment
, size
);
309 ptr
= tc_memalign(alignment
, size
);
314 // Sanity check alignment.
315 DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr
) & (alignment
- 1), 0U);
319 if (!new_mode
|| !call_new_handler(true))
325 void _aligned_free(void* p
) {
326 // TCMalloc returns pointers from memalign() that are safe to use with free().
327 // Pointers allocated with win_heap_memalign() MUST be freed via
328 // win_heap_memalign_free() since the aligned pointer is not the real one.
332 win_heap_memalign_free(p
);
341 #include "generic_allocators.cc"
346 namespace allocator
{
348 void SetupSubprocessAllocator() {
349 size_t primary_length
= 0;
350 getenv_s(&primary_length
, NULL
, 0, primary_name
);
352 size_t secondary_length
= 0;
354 getenv_s(&secondary_length
, buffer
, sizeof(buffer
), secondary_name
);
355 DCHECK_GT(sizeof(buffer
), secondary_length
);
356 buffer
[sizeof(buffer
) - 1] = '\0';
358 if (secondary_length
|| !primary_length
) {
359 // Don't use the environment variable if SYZYASAN is defined, as the
360 // implementation require Winheap to be the allocator.
361 #if !defined(SYZYASAN)
362 const char* secondary_value
= secondary_length
? buffer
: "TCMALLOC";
363 // Force renderer (or other subprocesses) to use secondary_value.
365 const char* secondary_value
= "WINHEAP";
367 int ret_val
= _putenv_s(primary_name
, secondary_value
);
368 DCHECK_EQ(0, ret_val
);
372 void* TCMallocDoMallocForTest(size_t size
) {
373 return do_malloc(size
);
376 void TCMallocDoFreeForTest(void* ptr
) {
380 size_t ExcludeSpaceForMarkForTest(size_t size
) {
381 return ExcludeSpaceForMark(size
);
384 } // namespace allocator.