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. 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 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 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 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. 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 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. 213 extern "C" intptr_t _get_heap_handle() { 214 return 0; 215 } 216 217 // The CRT heap initialization stub. 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. 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