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