OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/allocator/allocator_shim.h" | 5 #include "base/allocator/allocator_shim.h" |
6 | 6 |
7 #include <config.h> | 7 #include <config.h> |
8 #include "base/allocator/allocator_extension_thunks.h" | 8 #include "base/allocator/allocator_extension_thunks.h" |
9 #include "base/logging.h" | |
9 #include "base/profiler/alternate_timer.h" | 10 #include "base/profiler/alternate_timer.h" |
10 #include "base/sysinfo.h" | 11 #include "base/sysinfo.h" |
11 #include "jemalloc.h" | 12 #include "jemalloc.h" |
12 | 13 |
13 // When defined, different heap allocators can be used via an environment | 14 // This shim make it possible to use different allocators via an environment |
14 // variable set before running the program. This may reduce the amount | 15 // variable set before running the program. This may reduce the |
15 // of inlining that we get with malloc/free/etc. Disabling makes it | 16 // amount of inlining that we get with malloc/free/etc. |
16 // so that only tcmalloc can be used. | |
17 #define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
18 | 17 |
19 // TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth | 18 // TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth |
20 // from the "user code" so that debugging tools (HeapChecker) can work. | 19 // from the "user code" so that debugging tools (HeapChecker) can work. |
21 | 20 |
22 // __THROW is defined in glibc systems. It means, counter-intuitively, | 21 // __THROW is defined in glibc systems. It means, counter-intuitively, |
23 // "This function will never throw an exception." It's an optional | 22 // "This function will never throw an exception." It's an optional |
24 // optimization tool, but we may need to use it to match glibc prototypes. | 23 // optimization tool, but we may need to use it to match glibc prototypes. |
25 #ifndef __THROW // I guess we're not on a glibc system | 24 #ifndef __THROW // I guess we're not on a glibc system |
26 # define __THROW // __THROW is just an optimization, so ok to make it "" | 25 # define __THROW // __THROW is just an optimization, so ok to make it "" |
27 #endif | 26 #endif |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
112 return true; | 111 return true; |
113 } | 112 } |
114 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPT IONS) && !_HAS_EXCEPTIONS) | 113 #endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPT IONS) && !_HAS_EXCEPTIONS) |
115 return false; | 114 return false; |
116 } | 115 } |
117 | 116 |
118 extern "C" { | 117 extern "C" { |
119 void* malloc(size_t size) __THROW { | 118 void* malloc(size_t size) __THROW { |
120 void* ptr; | 119 void* ptr; |
121 for (;;) { | 120 for (;;) { |
122 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
123 switch (allocator) { | 121 switch (allocator) { |
124 case JEMALLOC: | 122 case JEMALLOC: |
125 ptr = je_malloc(size); | 123 ptr = je_malloc(size); |
126 break; | 124 break; |
127 case WINHEAP: | 125 case WINHEAP: |
128 case WINLFH: | 126 case WINLFH: |
129 ptr = win_heap_malloc(size); | 127 ptr = win_heap_malloc(size); |
130 break; | 128 break; |
131 case TCMALLOC: | 129 case TCMALLOC: |
132 default: | 130 default: |
133 ptr = do_malloc(size); | 131 ptr = do_malloc(size); |
134 break; | 132 break; |
135 } | 133 } |
136 #else | |
137 // TCMalloc case. | |
138 ptr = do_malloc(size); | |
139 #endif | |
140 if (ptr) | 134 if (ptr) |
141 return ptr; | 135 return ptr; |
142 | 136 |
143 if (!new_mode || !call_new_handler(true)) | 137 if (!new_mode || !call_new_handler(true)) |
144 break; | 138 break; |
145 } | 139 } |
146 return ptr; | 140 return ptr; |
147 } | 141 } |
148 | 142 |
149 void free(void* p) __THROW { | 143 void free(void* p) __THROW { |
150 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
151 switch (allocator) { | 144 switch (allocator) { |
152 case JEMALLOC: | 145 case JEMALLOC: |
153 je_free(p); | 146 je_free(p); |
154 return; | 147 return; |
155 case WINHEAP: | 148 case WINHEAP: |
156 case WINLFH: | 149 case WINLFH: |
157 win_heap_free(p); | 150 win_heap_free(p); |
158 return; | 151 return; |
152 case TCMALLOC: | |
153 do_free(p); | |
154 return; | |
159 } | 155 } |
160 #endif | |
161 // TCMalloc case. | |
162 do_free(p); | |
163 } | 156 } |
164 | 157 |
165 void* realloc(void* ptr, size_t size) __THROW { | 158 void* realloc(void* ptr, size_t size) __THROW { |
166 // Webkit is brittle for allocators that return NULL for malloc(0). The | 159 // Webkit is brittle for allocators that return NULL for malloc(0). The |
167 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure | 160 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure |
168 // to call malloc for this case. | 161 // to call malloc for this case. |
169 if (!ptr) | 162 if (!ptr) |
170 return malloc(size); | 163 return malloc(size); |
171 | 164 |
172 void* new_ptr; | 165 void* new_ptr; |
173 for (;;) { | 166 for (;;) { |
174 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
175 switch (allocator) { | 167 switch (allocator) { |
176 case JEMALLOC: | 168 case JEMALLOC: |
177 new_ptr = je_realloc(ptr, size); | 169 new_ptr = je_realloc(ptr, size); |
178 break; | 170 break; |
179 case WINHEAP: | 171 case WINHEAP: |
180 case WINLFH: | 172 case WINLFH: |
181 new_ptr = win_heap_realloc(ptr, size); | 173 new_ptr = win_heap_realloc(ptr, size); |
182 break; | 174 break; |
183 case TCMALLOC: | 175 case TCMALLOC: |
184 default: | 176 default: |
185 new_ptr = do_realloc(ptr, size); | 177 new_ptr = do_realloc(ptr, size); |
186 break; | 178 break; |
187 } | 179 } |
188 #else | |
189 // TCMalloc case. | |
190 new_ptr = do_realloc(ptr, size); | |
191 #endif | |
192 | 180 |
193 // Subtle warning: NULL return does not alwas indicate out-of-memory. If | 181 // Subtle warning: NULL return does not alwas indicate out-of-memory. If |
194 // the requested new size is zero, realloc should free the ptr and return | 182 // the requested new size is zero, realloc should free the ptr and return |
195 // NULL. | 183 // NULL. |
196 if (new_ptr || !size) | 184 if (new_ptr || !size) |
197 return new_ptr; | 185 return new_ptr; |
198 if (!new_mode || !call_new_handler(true)) | 186 if (!new_mode || !call_new_handler(true)) |
199 break; | 187 break; |
200 } | 188 } |
201 return new_ptr; | 189 return new_ptr; |
202 } | 190 } |
203 | 191 |
204 // TODO(mbelshe): Implement this for other allocators. | 192 // TODO(mbelshe): Implement this for other allocators. |
205 void malloc_stats(void) __THROW { | 193 void malloc_stats(void) __THROW { |
206 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
207 switch (allocator) { | 194 switch (allocator) { |
208 case JEMALLOC: | 195 case JEMALLOC: |
209 // No stats. | 196 // No stats. |
210 return; | 197 return; |
211 case WINHEAP: | 198 case WINHEAP: |
212 case WINLFH: | 199 case WINLFH: |
213 // No stats. | 200 // No stats. |
214 return; | 201 return; |
202 case TCMALLOC: | |
203 tc_malloc_stats(); | |
204 return; | |
215 } | 205 } |
216 #endif | |
217 tc_malloc_stats(); | |
218 } | 206 } |
219 | 207 |
220 #ifdef WIN32 | 208 #ifdef WIN32 |
221 | 209 |
222 extern "C" size_t _msize(void* p) { | 210 extern "C" size_t _msize(void* p) { |
223 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
224 switch (allocator) { | 211 switch (allocator) { |
225 case JEMALLOC: | 212 case JEMALLOC: |
226 return je_msize(p); | 213 return je_msize(p); |
227 case WINHEAP: | 214 case WINHEAP: |
228 case WINLFH: | 215 case WINLFH: |
229 return win_heap_msize(p); | 216 return win_heap_msize(p); |
217 case TCMALLOC: | |
218 return MallocExtension::instance()->GetAllocatedSize(p); | |
230 } | 219 } |
231 #endif | 220 |
232 return MallocExtension::instance()->GetAllocatedSize(p); | 221 NOTREACHED(); |
jar (doing other things)
2013/11/07 19:22:37
Although this is a reasonable debug check.... it i
| |
222 return 0; | |
233 } | 223 } |
234 | 224 |
235 // This is included to resolve references from libcmt. | 225 // This is included to resolve references from libcmt. |
236 extern "C" intptr_t _get_heap_handle() { | 226 extern "C" intptr_t _get_heap_handle() { |
237 return 0; | 227 return 0; |
238 } | 228 } |
239 | 229 |
240 static bool get_allocator_waste_size_thunk(size_t* size) { | 230 static bool get_allocator_waste_size_thunk(size_t* size) { |
241 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
242 switch (allocator) { | 231 switch (allocator) { |
243 case JEMALLOC: | 232 case JEMALLOC: |
244 case WINHEAP: | 233 case WINHEAP: |
245 case WINLFH: | 234 case WINLFH: |
246 // TODO(alexeif): Implement for allocators other than tcmalloc. | 235 // TODO(alexeif): Implement for allocators other than tcmalloc. |
247 return false; | 236 return false; |
248 } | 237 } |
249 #endif | |
250 size_t heap_size, allocated_bytes, unmapped_bytes; | 238 size_t heap_size, allocated_bytes, unmapped_bytes; |
251 MallocExtension* ext = MallocExtension::instance(); | 239 MallocExtension* ext = MallocExtension::instance(); |
252 if (ext->GetNumericProperty("generic.heap_size", &heap_size) && | 240 if (ext->GetNumericProperty("generic.heap_size", &heap_size) && |
253 ext->GetNumericProperty("generic.current_allocated_bytes", | 241 ext->GetNumericProperty("generic.current_allocated_bytes", |
254 &allocated_bytes) && | 242 &allocated_bytes) && |
255 ext->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", | 243 ext->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", |
256 &unmapped_bytes)) { | 244 &unmapped_bytes)) { |
257 *size = heap_size - allocated_bytes - unmapped_bytes; | 245 *size = heap_size - allocated_bytes - unmapped_bytes; |
258 return true; | 246 return true; |
259 } | 247 } |
260 return false; | 248 return false; |
261 } | 249 } |
262 | 250 |
263 static void get_stats_thunk(char* buffer, int buffer_length) { | 251 static void get_stats_thunk(char* buffer, int buffer_length) { |
264 MallocExtension::instance()->GetStats(buffer, buffer_length); | 252 MallocExtension::instance()->GetStats(buffer, buffer_length); |
265 } | 253 } |
266 | 254 |
267 static void release_free_memory_thunk() { | 255 static void release_free_memory_thunk() { |
268 MallocExtension::instance()->ReleaseFreeMemory(); | 256 MallocExtension::instance()->ReleaseFreeMemory(); |
269 } | 257 } |
270 | 258 |
271 // The CRT heap initialization stub. | 259 // The CRT heap initialization stub. |
272 extern "C" int _heap_init() { | 260 extern "C" int _heap_init() { |
273 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
274 // Don't use the environment variable if ADDRESS_SANITIZER is defined on | 261 // Don't use the environment variable if ADDRESS_SANITIZER is defined on |
275 // Windows, as the implementation requires Winheap to be the allocator. | 262 // Windows, as the implementation requires Winheap to be the allocator. |
276 #if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN)) | 263 #if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN)) |
277 const char* environment_value = GetenvBeforeMain(primary_name); | 264 const char* environment_value = GetenvBeforeMain(primary_name); |
278 if (environment_value) { | 265 if (environment_value) { |
279 if (!stricmp(environment_value, "jemalloc")) | 266 if (!stricmp(environment_value, "jemalloc")) |
280 allocator = JEMALLOC; | 267 allocator = JEMALLOC; |
281 else if (!stricmp(environment_value, "winheap")) | 268 else if (!stricmp(environment_value, "winheap")) |
282 allocator = WINHEAP; | 269 allocator = WINHEAP; |
283 else if (!stricmp(environment_value, "winlfh")) | 270 else if (!stricmp(environment_value, "winlfh")) |
284 allocator = WINLFH; | 271 allocator = WINLFH; |
285 else if (!stricmp(environment_value, "tcmalloc")) | 272 else if (!stricmp(environment_value, "tcmalloc")) |
286 allocator = TCMALLOC; | 273 allocator = TCMALLOC; |
287 } | 274 } |
288 #endif | 275 #endif |
289 | 276 |
290 switch (allocator) { | 277 switch (allocator) { |
291 case JEMALLOC: | 278 case JEMALLOC: |
292 return je_malloc_init_hard() ? 0 : 1; | 279 return je_malloc_init_hard() ? 0 : 1; |
293 case WINHEAP: | 280 case WINHEAP: |
294 return win_heap_init(false) ? 1 : 0; | 281 return win_heap_init(false) ? 1 : 0; |
295 case WINLFH: | 282 case WINLFH: |
296 return win_heap_init(true) ? 1 : 0; | 283 return win_heap_init(true) ? 1 : 0; |
297 case TCMALLOC: | 284 case TCMALLOC: |
298 default: | 285 default: |
299 // fall through | 286 // fall through |
300 break; | 287 break; |
301 } | 288 } |
302 #endif | 289 |
303 // Initializing tcmalloc. | 290 // Initializing tcmalloc. |
304 // We intentionally leak this object. It lasts for the process | 291 // We intentionally leak this object. It lasts for the process |
305 // lifetime. Trying to teardown at _heap_term() is so late that | 292 // lifetime. Trying to teardown at _heap_term() is so late that |
306 // you can't do anything useful anyway. | 293 // you can't do anything useful anyway. |
307 new TCMallocGuard(); | 294 new TCMallocGuard(); |
308 | 295 |
309 // Provide optional hook for monitoring allocation quantities on a per-thread | 296 // Provide optional hook for monitoring allocation quantities on a per-thread |
310 // basis. Only set the hook if the environment indicates this needs to be | 297 // basis. Only set the hook if the environment indicates this needs to be |
311 // enabled. | 298 // enabled. |
312 const char* profiling = | 299 const char* profiling = |
(...skipping 26 matching lines...) Expand all Loading... | |
339 void* _aligned_malloc(size_t size, size_t alignment) { | 326 void* _aligned_malloc(size_t size, size_t alignment) { |
340 // _aligned_malloc guarantees parameter validation, so do so here. These | 327 // _aligned_malloc guarantees parameter validation, so do so here. These |
341 // checks are somewhat stricter than _aligned_malloc() since we're effectively | 328 // checks are somewhat stricter than _aligned_malloc() since we're effectively |
342 // using memalign() under the hood. | 329 // using memalign() under the hood. |
343 DCHECK_GT(size, 0U); | 330 DCHECK_GT(size, 0U); |
344 DCHECK_EQ(alignment & (alignment - 1), 0U); | 331 DCHECK_EQ(alignment & (alignment - 1), 0U); |
345 DCHECK_EQ(alignment % sizeof(void*), 0U); | 332 DCHECK_EQ(alignment % sizeof(void*), 0U); |
346 | 333 |
347 void* ptr; | 334 void* ptr; |
348 for (;;) { | 335 for (;;) { |
349 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
350 switch (allocator) { | 336 switch (allocator) { |
351 case JEMALLOC: | 337 case JEMALLOC: |
352 ptr = je_memalign(alignment, size); | 338 ptr = je_memalign(alignment, size); |
353 break; | 339 break; |
354 case WINHEAP: | 340 case WINHEAP: |
355 case WINLFH: | 341 case WINLFH: |
356 ptr = win_heap_memalign(alignment, size); | 342 ptr = win_heap_memalign(alignment, size); |
357 break; | 343 break; |
358 case TCMALLOC: | 344 case TCMALLOC: |
359 default: | 345 default: |
360 ptr = tc_memalign(alignment, size); | 346 ptr = tc_memalign(alignment, size); |
361 break; | 347 break; |
362 } | 348 } |
363 #else | 349 |
364 // TCMalloc case. | |
365 ptr = tc_memalign(alignment, size); | |
366 #endif | |
367 if (ptr) { | 350 if (ptr) { |
368 // Sanity check alignment. | 351 // Sanity check alignment. |
369 DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U); | 352 DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U); |
370 return ptr; | 353 return ptr; |
371 } | 354 } |
372 | 355 |
373 if (!new_mode || !call_new_handler(true)) | 356 if (!new_mode || !call_new_handler(true)) |
374 break; | 357 break; |
375 } | 358 } |
376 return ptr; | 359 return ptr; |
377 } | 360 } |
378 | 361 |
379 void _aligned_free(void* p) { | 362 void _aligned_free(void* p) { |
380 // Both JEMalloc and TCMalloc return pointers from memalign() that are safe to | 363 // Both JEMalloc and TCMalloc return pointers from memalign() that are safe to |
381 // use with free(). Pointers allocated with win_heap_memalign() MUST be freed | 364 // use with free(). Pointers allocated with win_heap_memalign() MUST be freed |
382 // via win_heap_memalign_free() since the aligned pointer is not the real one. | 365 // via win_heap_memalign_free() since the aligned pointer is not the real one. |
383 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
384 switch (allocator) { | 366 switch (allocator) { |
385 case JEMALLOC: | 367 case JEMALLOC: |
386 je_free(p); | 368 je_free(p); |
387 return; | 369 return; |
388 case WINHEAP: | 370 case WINHEAP: |
389 case WINLFH: | 371 case WINLFH: |
390 win_heap_memalign_free(p); | 372 win_heap_memalign_free(p); |
391 return; | 373 return; |
374 case TCMALLOC: | |
375 do_free(p); | |
392 } | 376 } |
393 #endif | |
394 // TCMalloc case. | |
395 do_free(p); | |
396 } | 377 } |
397 | 378 |
398 #endif // WIN32 | 379 #endif // WIN32 |
399 | 380 |
400 #include "generic_allocators.cc" | 381 #include "generic_allocators.cc" |
401 | 382 |
402 } // extern C | 383 } // extern C |
403 | 384 |
404 namespace base { | 385 namespace base { |
405 namespace allocator { | 386 namespace allocator { |
406 | 387 |
407 void SetupSubprocessAllocator() { | 388 void SetupSubprocessAllocator() { |
408 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
409 size_t primary_length = 0; | 389 size_t primary_length = 0; |
410 getenv_s(&primary_length, NULL, 0, primary_name); | 390 getenv_s(&primary_length, NULL, 0, primary_name); |
411 | 391 |
412 size_t secondary_length = 0; | 392 size_t secondary_length = 0; |
413 char buffer[20]; | 393 char buffer[20]; |
414 getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name); | 394 getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name); |
415 DCHECK_GT(sizeof(buffer), secondary_length); | 395 DCHECK_GT(sizeof(buffer), secondary_length); |
416 buffer[sizeof(buffer) - 1] = '\0'; | 396 buffer[sizeof(buffer) - 1] = '\0'; |
417 | 397 |
418 if (secondary_length || !primary_length) { | 398 if (secondary_length || !primary_length) { |
419 // Don't use the environment variable if ADDRESS_SANITIZER is defined on | 399 // Don't use the environment variable if ADDRESS_SANITIZER is defined on |
420 // Windows, as the implementation require Winheap to be the allocator. | 400 // Windows, as the implementation require Winheap to be the allocator. |
421 #if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN)) | 401 #if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN)) |
422 const char* secondary_value = secondary_length ? buffer : "TCMALLOC"; | 402 const char* secondary_value = secondary_length ? buffer : "TCMALLOC"; |
423 // Force renderer (or other subprocesses) to use secondary_value. | 403 // Force renderer (or other subprocesses) to use secondary_value. |
424 #else | 404 #else |
425 const char* secondary_value = "WINHEAP"; | 405 const char* secondary_value = "WINHEAP"; |
426 #endif | 406 #endif |
427 int ret_val = _putenv_s(primary_name, secondary_value); | 407 int ret_val = _putenv_s(primary_name, secondary_value); |
428 DCHECK_EQ(0, ret_val); | 408 DCHECK_EQ(0, ret_val); |
429 } | 409 } |
430 #endif // ENABLE_DYNAMIC_ALLOCATOR_SWITCHING | |
431 } | 410 } |
432 | 411 |
433 void* TCMallocDoMallocForTest(size_t size) { | 412 void* TCMallocDoMallocForTest(size_t size) { |
434 return do_malloc(size); | 413 return do_malloc(size); |
435 } | 414 } |
436 | 415 |
437 void TCMallocDoFreeForTest(void* ptr) { | 416 void TCMallocDoFreeForTest(void* ptr) { |
438 do_free(ptr); | 417 do_free(ptr); |
439 } | 418 } |
440 | 419 |
441 size_t ExcludeSpaceForMarkForTest(size_t size) { | 420 size_t ExcludeSpaceForMarkForTest(size_t size) { |
442 return ExcludeSpaceForMark(size); | 421 return ExcludeSpaceForMark(size); |
443 } | 422 } |
444 | 423 |
445 } // namespace allocator. | 424 } // namespace allocator. |
446 } // namespace base. | 425 } // namespace base. |
OLD | NEW |