1 // Copyright (c) 2009 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.
4
5 #include <config.h>
6
7 // When defined, different heap allocators can be used via an environment
8 // variable set before running the program. This may reduce the amount
9 // of inlining that we get with malloc/free/etc. Disabling makes it
10 // so that only tcmalloc can be used.
11 #define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
12
13 // TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
14 // from the "user code" so that debugging tools (HeapChecker) can work.
15
16 // __THROW is defined in glibc systems. It means, counter-intuitively,
17 // "This function will never throw an exception." It's an optional
18 // optimization tool, but we may need to use it to match glibc prototypes.
19 #ifndef __THROW // I guess we're not on a glibc system
20 # define __THROW // __THROW is just an optimization, so ok to make it ""
21 #endif
22
23 // new_mode behaves similarly to MSVC's _set_new_mode.
24 // If flag is 0 (default), calls to malloc will behave normally.
25 // If flag is 1, calls to malloc will behave like calls to new,
26 // and the std_new_handler will be invoked on failure.
27 // Can be set by calling _set_new_mode().
28 static int new_mode = 0;
29
30 typedef enum {
31 TCMALLOC, // TCMalloc is the default allocator.
32 JEMALLOC, // JEMalloc
33 WINDEFAULT, // Windows Heap
34 WINLFH, // Windows LFH Heap
35 } Allocator;
36
37 // This is the default allocator.
38 static Allocator allocator = TCMALLOC;
39
40 // We include tcmalloc and the win_allocator to get as much inlining as
41 // possible.
42 #include "tcmalloc.cc"
43 #include "win_allocator.cc"
44
45 // Forward declarations from jemalloc.
46 extern "C" {
47 void* je_malloc(size_t s);
48 void* je_realloc(void* p, size_t s);
49 void je_free(void* s);
50 size_t je_msize(void* p);
51 bool je_malloc_init_hard();
52 }
53
54 extern "C" {
55
56 // Call the new handler, if one has been set.
57 // Returns true on successfully calling the handler, false otherwise.
call_new_handler(bool nothrow)58 inline bool call_new_handler(bool nothrow) {
59 // Get the current new handler. NB: this function is not
60 // thread-safe. We make a feeble stab at making it so here, but
61 // this lock only protects against tcmalloc interfering with
62 // itself, not with other libraries calling set_new_handler.
63 std::new_handler nh;
64 {
65 SpinLockHolder h(&set_new_handler_lock);
66 nh = std::set_new_handler(0);
67 (void) std::set_new_handler(nh);
68 }
69 #if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
70 if (!nh)
71 return false;
72 // Since exceptions are disabled, we don't really know if new_handler
73 // failed. Assume it will abort if it fails.
74 (*nh)();
75 return false; // break out of the retry loop.
76 #else
77 // If no new_handler is established, the allocation failed.
78 if (!nh) {
79 if (nothrow)
80 return 0;
81 throw std::bad_alloc();
82 }
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.
86 try {
87 (*nh)();
88 } catch (const std::bad_alloc&) {
89 if (!nothrow)
90 throw;
91 return p;
92 }
93 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
94 }
95
malloc(size_t size)96 void* malloc(size_t size) __THROW {
97 void* ptr;
98 for (;;) {
99 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
100 switch (allocator) {
101 case JEMALLOC:
102 ptr = je_malloc(size);
103 break;
104 case WINDEFAULT:
105 case WINLFH:
106 ptr = win_heap_malloc(size);
107 break;
108 case TCMALLOC:
109 default:
110 ptr = do_malloc(size);
111 break;
112 }
113 #else
114 // TCMalloc case.
115 ptr = do_malloc(size);
116 #endif
117 if (ptr)
118 return ptr;
119
120 if (!new_mode || !call_new_handler(true))
121 break;
122 }
123 return ptr;
124 }
125
free(void * p)126 void free(void* p) __THROW {
127 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
128 switch (allocator) {
129 case JEMALLOC:
130 je_free(p);
131 return;
132 case WINDEFAULT:
133 case WINLFH:
134 win_heap_free(p);
135 return;
136 }
137 #endif
138 // TCMalloc case.
139 do_free(p);
140 }
141
realloc(void * ptr,size_t size)142 void* realloc(void* ptr, size_t size) __THROW {
143 // Webkit is brittle for allocators that return NULL for malloc(0). The
144 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
145 // to call malloc for this case.
146 if (!ptr)
147 return malloc(size);
148
149 void* new_ptr;
150 for (;;) {
151 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
152 switch (allocator) {
153 case JEMALLOC:
154 new_ptr = je_realloc(ptr, size);
155 break;
156 case WINDEFAULT:
157 case WINLFH:
158 new_ptr = win_heap_realloc(ptr, size);
159 break;
160 case TCMALLOC:
161 default:
162 new_ptr = do_realloc(ptr, size);
163 break;
164 }
165 #else
166 // TCMalloc case.
167 new_ptr = do_realloc(ptr, size);
168 #endif
169
170 // Subtle warning: NULL return does not alwas indicate out-of-memory. If
171 // the requested new size is zero, realloc should free the ptr and return
172 // NULL.
173 if (new_ptr || !size)
174 return new_ptr;
175 if (!new_mode || !call_new_handler(true))
176 break;
177 }
178 return new_ptr;
179 }
180
181 // TODO(mbelshe): Implement this for other allocators.
malloc_stats(void)182 void malloc_stats(void) __THROW {
183 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
184 switch (allocator) {
185 case JEMALLOC:
186 // No stats.
187 return;
188 case WINDEFAULT:
189 case WINLFH:
190 // No stats.
191 return;
192 }
193 #endif
194 tc_malloc_stats();
195 }
196
197 #ifdef WIN32
198
_msize(void * p)199 extern "C" size_t _msize(void* p) {
200 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
201 switch (allocator) {
202 case JEMALLOC:
203 return je_msize(p);
204 case WINDEFAULT:
205 case WINLFH:
206 return win_heap_msize(p);
207 }
208 #endif
209 return MallocExtension::instance()->GetAllocatedSize(p);
210 }
211
212 // This is included to resolve references from libcmt.
_get_heap_handle()213 extern "C" intptr_t _get_heap_handle() {
214 return 0;
215 }
216
217 // The CRT heap initialization stub.
_heap_init()218 extern "C" int _heap_init() {
219 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
220 const char* override = GetenvBeforeMain("CHROME_ALLOCATOR");
221 if (override) {
222 if (!stricmp(override, "jemalloc"))
223 allocator = JEMALLOC;
224 else if (!stricmp(override, "winheap"))
225 allocator = WINDEFAULT;
226 else if (!stricmp(override, "winlfh"))
227 allocator = WINLFH;
228 else if (!stricmp(override, "tcmalloc"))
229 allocator = TCMALLOC;
230 }
231
232 switch (allocator) {
233 case JEMALLOC:
234 return je_malloc_init_hard() ? 0 : 1;
235 case WINDEFAULT:
236 return win_heap_init(false) ? 1 : 0;
237 case WINLFH:
238 return win_heap_init(true) ? 1 : 0;
239 case TCMALLOC:
240 default:
241 // fall through
242 break;
243 }
244 #endif
245 // Initializing tcmalloc.
246 // We intentionally leak this object. It lasts for the process
247 // lifetime. Trying to teardown at _heap_term() is so late that
248 // you can't do anything useful anyway.
249 new TCMallocGuard();
250 return 1;
251 }
252
253 // The CRT heap cleanup stub.
_heap_term()254 extern "C" void _heap_term() {}
255
256 // We set this to 1 because part of the CRT uses a check of _crtheap != 0
257 // to test whether the CRT has been initialized. Once we've ripped out
258 // the allocators from libcmt, we need to provide this definition so that
259 // the rest of the CRT is still usable.
260 extern "C" void* _crtheap = reinterpret_cast<void*>(1);
261
262 #endif // WIN32
263
264 #include "generic_allocators.cc"
265
266 } // extern C
267