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 // new_mode behaves similarly to MSVC's _set_new_mode.
20 // If flag is 0 (default), calls to malloc will behave normally.
21 // If flag is 1, calls to malloc will behave like calls to new,
22 // and the std_new_handler will be invoked on failure.
23 // Can be set by calling _set_new_mode().
24 static int new_mode
= 0;
27 TCMALLOC
, // TCMalloc is the default allocator.
28 WINHEAP
, // Windows Heap (standard Windows allocator).
29 WINLFH
, // Windows LFH Heap.
32 // This is the default allocator. This value can be changed at startup by
33 // specifying environment variables shown below it.
34 // See SetupSubprocessAllocator() to specify a default secondary (subprocess)
36 // TODO(jar): Switch to using TCMALLOC for the renderer as well.
38 // SyzyASan requires the use of "WINHEAP".
39 static Allocator allocator
= WINHEAP
;
41 static Allocator allocator
= TCMALLOC
;
43 // The names of the environment variables that can optionally control the
44 // selection of the allocator. The primary may be used to control overall
45 // allocator selection, and the secondary can be used to specify an allocator
46 // to use in sub-processes.
47 static const char primary_name
[] = "CHROME_ALLOCATOR";
48 static const char secondary_name
[] = "CHROME_ALLOCATOR_2";
50 // We include tcmalloc and the win_allocator to get as much inlining as
52 #include "debugallocation_shim.cc"
53 #include "win_allocator.cc"
55 // Call the new handler, if one has been set.
56 // Returns true on successfully calling the handler, false otherwise.
57 inline bool call_new_handler(bool nothrow
) {
58 // Get the current new handler. NB: this function is not
59 // thread-safe. We make a feeble stab at making it so here, but
60 // this lock only protects against tcmalloc interfering with
61 // itself, not with other libraries calling set_new_handler.
64 SpinLockHolder
h(&set_new_handler_lock
);
65 nh
= std::set_new_handler(0);
66 (void) std::set_new_handler(nh
);
68 #if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
69 (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
72 // Since exceptions are disabled, we don't really know if new_handler
73 // failed. Assume it will abort if it fails.
75 return false; // break out of the retry loop.
77 // If no new_handler is established, the allocation failed.
81 throw std::bad_alloc();
83 // Otherwise, try the new_handler. If it returns, retry the
84 // allocation. If it throws std::bad_alloc, fail the allocation.
85 // if it throws something else, don't interfere.
88 } catch (const std::bad_alloc
&) {
93 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
98 void* malloc(size_t size
) {
104 ptr
= win_heap_malloc(size
);
108 ptr
= do_malloc(size
);
114 if (!new_mode
|| !call_new_handler(true))
132 void* realloc(void* ptr
, size_t size
) {
133 // Webkit is brittle for allocators that return NULL for malloc(0). The
134 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
135 // to call malloc for this case.
144 new_ptr
= win_heap_realloc(ptr
, size
);
148 new_ptr
= do_realloc(ptr
, size
);
152 // Subtle warning: NULL return does not alwas indicate out-of-memory. If
153 // the requested new size is zero, realloc should free the ptr and return
155 if (new_ptr
|| !size
)
157 if (!new_mode
|| !call_new_handler(true))
163 // TODO(mbelshe): Implement this for other allocators.
164 void malloc_stats(void) {
178 extern "C" size_t _msize(void* p
) {
182 return win_heap_msize(p
);
186 return MallocExtension::instance()->GetAllocatedSize(p
);
189 // This is included to resolve references from libcmt.
190 extern "C" intptr_t _get_heap_handle() {
194 static bool get_allocator_waste_size_thunk(size_t* size
) {
198 // TODO(alexeif): Implement for allocators other than tcmalloc.
201 size_t heap_size
, allocated_bytes
, unmapped_bytes
;
202 MallocExtension
* ext
= MallocExtension::instance();
203 if (ext
->GetNumericProperty("generic.heap_size", &heap_size
) &&
204 ext
->GetNumericProperty("generic.current_allocated_bytes",
206 ext
->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes",
208 *size
= heap_size
- allocated_bytes
- unmapped_bytes
;
214 static void get_stats_thunk(char* buffer
, int buffer_length
) {
215 MallocExtension::instance()->GetStats(buffer
, buffer_length
);
218 static void release_free_memory_thunk() {
219 MallocExtension::instance()->ReleaseFreeMemory();
222 // The CRT heap initialization stub.
223 extern "C" int _heap_init() {
224 // Don't use the environment variable if SYZYASAN is defined, as the
225 // implementation requires Winheap to be the allocator.
226 #if !defined(SYZYASAN)
227 const char* environment_value
= GetenvBeforeMain(primary_name
);
228 if (environment_value
) {
229 if (!stricmp(environment_value
, "winheap"))
231 else if (!stricmp(environment_value
, "winlfh"))
233 else if (!stricmp(environment_value
, "tcmalloc"))
234 allocator
= TCMALLOC
;
240 return win_heap_init(false) ? 1 : 0;
242 return win_heap_init(true) ? 1 : 0;
249 // Initializing tcmalloc.
250 // We intentionally leak this object. It lasts for the process
251 // lifetime. Trying to teardown at _heap_term() is so late that
252 // you can't do anything useful anyway.
255 // Provide optional hook for monitoring allocation quantities on a per-thread
256 // basis. Only set the hook if the environment indicates this needs to be
258 const char* profiling
=
259 GetenvBeforeMain(tracked_objects::kAlternateProfilerTime
);
260 if (profiling
&& *profiling
== '1') {
261 tracked_objects::SetAlternateTimeSource(
262 tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread
,
263 tracked_objects::TIME_SOURCE_TYPE_TCMALLOC
);
266 base::allocator::thunks::SetGetAllocatorWasteSizeFunction(
267 get_allocator_waste_size_thunk
);
268 base::allocator::thunks::SetGetStatsFunction(get_stats_thunk
);
269 base::allocator::thunks::SetReleaseFreeMemoryFunction(
270 release_free_memory_thunk
);
275 // The CRT heap cleanup stub.
276 extern "C" void _heap_term() {}
278 // We set this to 1 because part of the CRT uses a check of _crtheap != 0
279 // to test whether the CRT has been initialized. Once we've ripped out
280 // the allocators from libcmt, we need to provide this definition so that
281 // the rest of the CRT is still usable.
282 extern "C" void* _crtheap
= reinterpret_cast<void*>(1);
284 // Provide support for aligned memory through Windows only _aligned_malloc().
285 void* _aligned_malloc(size_t size
, size_t alignment
) {
286 // _aligned_malloc guarantees parameter validation, so do so here. These
287 // checks are somewhat stricter than _aligned_malloc() since we're effectively
288 // using memalign() under the hood.
290 DCHECK_EQ(alignment
& (alignment
- 1), 0U);
291 DCHECK_EQ(alignment
% sizeof(void*), 0U);
298 ptr
= win_heap_memalign(alignment
, size
);
302 ptr
= tc_memalign(alignment
, size
);
307 // Sanity check alignment.
308 DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr
) & (alignment
- 1), 0U);
312 if (!new_mode
|| !call_new_handler(true))
318 void _aligned_free(void* p
) {
319 // TCMalloc returns pointers from memalign() that are safe to use with free().
320 // Pointers allocated with win_heap_memalign() MUST be freed via
321 // win_heap_memalign_free() since the aligned pointer is not the real one.
325 win_heap_memalign_free(p
);
334 #include "generic_allocators.cc"
339 namespace allocator
{
341 void SetupSubprocessAllocator() {
342 size_t primary_length
= 0;
343 getenv_s(&primary_length
, NULL
, 0, primary_name
);
345 size_t secondary_length
= 0;
347 getenv_s(&secondary_length
, buffer
, sizeof(buffer
), secondary_name
);
348 DCHECK_GT(sizeof(buffer
), secondary_length
);
349 buffer
[sizeof(buffer
) - 1] = '\0';
351 if (secondary_length
|| !primary_length
) {
352 // Don't use the environment variable if SYZYASAN is defined, as the
353 // implementation require Winheap to be the allocator.
354 #if !defined(SYZYASAN)
355 const char* secondary_value
= secondary_length
? buffer
: "TCMALLOC";
356 // Force renderer (or other subprocesses) to use secondary_value.
358 const char* secondary_value
= "WINHEAP";
360 int ret_val
= _putenv_s(primary_name
, secondary_value
);
361 DCHECK_EQ(0, ret_val
);
365 void* TCMallocDoMallocForTest(size_t size
) {
366 return do_malloc(size
);
369 void TCMallocDoFreeForTest(void* ptr
) {
373 size_t ExcludeSpaceForMarkForTest(size_t size
) {
374 return ExcludeSpaceForMark(size
);
377 } // namespace allocator.