• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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