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. 4 5 #include "base/allocator/allocator_shim.h" 6 7 #include <config.h> 8 #include "base/allocator/allocator_extension_thunks.h" 9 #include "base/profiler/alternate_timer.h" 10 #include "base/sysinfo.h" 11 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. 15 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. 18 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; 25 26 typedef enum { 27 TCMALLOC, // TCMalloc is the default allocator. 28 WINHEAP, // Windows Heap (standard Windows allocator). 29 WINLFH, // Windows LFH Heap. 30 } Allocator; 31 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) 35 // allocator. 36 // TODO(jar): Switch to using TCMALLOC for the renderer as well. 37 #if defined(SYZYASAN) 38 // SyzyASan requires the use of "WINHEAP". 39 static Allocator allocator = WINHEAP; 40 #else 41 static Allocator allocator = TCMALLOC; 42 #endif 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"; 49 50 // We include tcmalloc and the win_allocator to get as much inlining as 51 // possible. 52 #include "debugallocation_shim.cc" 53 #include "win_allocator.cc" 54 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. 62 std::new_handler nh; 63 { 64 SpinLockHolder h(&set_new_handler_lock); 65 nh = std::set_new_handler(0); 66 (void) std::set_new_handler(nh); 67 } 68 #if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ 69 (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 false; 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 true; 92 } 93 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) 94 return false; 95 } 96 97 extern "C" { 98 void* malloc(size_t size) { 99 void* ptr; 100 for (;;) { 101 switch (allocator) { 102 case WINHEAP: 103 case WINLFH: 104 ptr = win_heap_malloc(size); 105 break; 106 case TCMALLOC: 107 default: 108 ptr = do_malloc(size); 109 break; 110 } 111 if (ptr) 112 return ptr; 113 114 if (!new_mode || !call_new_handler(true)) 115 break; 116 } 117 return ptr; 118 } 119 120 void free(void* p) { 121 switch (allocator) { 122 case WINHEAP: 123 case WINLFH: 124 win_heap_free(p); 125 return; 126 case TCMALLOC: 127 do_free(p); 128 return; 129 } 130 } 131 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. 136 if (!ptr) 137 return malloc(size); 138 139 void* new_ptr; 140 for (;;) { 141 switch (allocator) { 142 case WINHEAP: 143 case WINLFH: 144 new_ptr = win_heap_realloc(ptr, size); 145 break; 146 case TCMALLOC: 147 default: 148 new_ptr = do_realloc(ptr, size); 149 break; 150 } 151 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 154 // NULL. 155 if (new_ptr || !size) 156 return new_ptr; 157 if (!new_mode || !call_new_handler(true)) 158 break; 159 } 160 return new_ptr; 161 } 162 163 // TODO(mbelshe): Implement this for other allocators. 164 void malloc_stats(void) { 165 switch (allocator) { 166 case WINHEAP: 167 case WINLFH: 168 // No stats. 169 return; 170 case TCMALLOC: 171 tc_malloc_stats(); 172 return; 173 } 174 } 175 176 #ifdef WIN32 177 178 extern "C" size_t _msize(void* p) { 179 switch (allocator) { 180 case WINHEAP: 181 case WINLFH: 182 return win_heap_msize(p); 183 } 184 185 // TCMALLOC 186 return MallocExtension::instance()->GetAllocatedSize(p); 187 } 188 189 // This is included to resolve references from libcmt. 190 extern "C" intptr_t _get_heap_handle() { 191 return 0; 192 } 193 194 static bool get_allocator_waste_size_thunk(size_t* size) { 195 switch (allocator) { 196 case WINHEAP: 197 case WINLFH: 198 // TODO(alexeif): Implement for allocators other than tcmalloc. 199 return false; 200 } 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", 205 &allocated_bytes) && 206 ext->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", 207 &unmapped_bytes)) { 208 *size = heap_size - allocated_bytes - unmapped_bytes; 209 return true; 210 } 211 return false; 212 } 213 214 static void get_stats_thunk(char* buffer, int buffer_length) { 215 MallocExtension::instance()->GetStats(buffer, buffer_length); 216 } 217 218 static void release_free_memory_thunk() { 219 MallocExtension::instance()->ReleaseFreeMemory(); 220 } 221 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")) 230 allocator = WINHEAP; 231 else if (!stricmp(environment_value, "winlfh")) 232 allocator = WINLFH; 233 else if (!stricmp(environment_value, "tcmalloc")) 234 allocator = TCMALLOC; 235 } 236 #endif 237 238 switch (allocator) { 239 case WINHEAP: 240 return win_heap_init(false) ? 1 : 0; 241 case WINLFH: 242 return win_heap_init(true) ? 1 : 0; 243 case TCMALLOC: 244 default: 245 // fall through 246 break; 247 } 248 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. 253 new TCMallocGuard(); 254 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 257 // enabled. 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); 264 } 265 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); 271 272 return 1; 273 } 274 275 // The CRT heap cleanup stub. 276 extern "C" void _heap_term() {} 277 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); 283 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. 289 DCHECK_GT(size, 0U); 290 DCHECK_EQ(alignment & (alignment - 1), 0U); 291 DCHECK_EQ(alignment % sizeof(void*), 0U); 292 293 void* ptr; 294 for (;;) { 295 switch (allocator) { 296 case WINHEAP: 297 case WINLFH: 298 ptr = win_heap_memalign(alignment, size); 299 break; 300 case TCMALLOC: 301 default: 302 ptr = tc_memalign(alignment, size); 303 break; 304 } 305 306 if (ptr) { 307 // Sanity check alignment. 308 DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U); 309 return ptr; 310 } 311 312 if (!new_mode || !call_new_handler(true)) 313 break; 314 } 315 return ptr; 316 } 317 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. 322 switch (allocator) { 323 case WINHEAP: 324 case WINLFH: 325 win_heap_memalign_free(p); 326 return; 327 case TCMALLOC: 328 do_free(p); 329 } 330 } 331 332 #endif // WIN32 333 334 #include "generic_allocators.cc" 335 336 } // extern C 337 338 namespace base { 339 namespace allocator { 340 341 void SetupSubprocessAllocator() { 342 size_t primary_length = 0; 343 getenv_s(&primary_length, NULL, 0, primary_name); 344 345 size_t secondary_length = 0; 346 char buffer[20]; 347 getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name); 348 DCHECK_GT(sizeof(buffer), secondary_length); 349 buffer[sizeof(buffer) - 1] = '\0'; 350 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. 357 #else 358 const char* secondary_value = "WINHEAP"; 359 #endif 360 int ret_val = _putenv_s(primary_name, secondary_value); 361 DCHECK_EQ(0, ret_val); 362 } 363 } 364 365 void* TCMallocDoMallocForTest(size_t size) { 366 return do_malloc(size); 367 } 368 369 void TCMallocDoFreeForTest(void* ptr) { 370 do_free(ptr); 371 } 372 373 size_t ExcludeSpaceForMarkForTest(size_t size) { 374 return ExcludeSpaceForMark(size); 375 } 376 377 } // namespace allocator. 378 } // namespace base. 379