1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/memory/build/replace_malloc.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,505 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#ifndef MOZ_MEMORY 1.9 +# error Should not compile this file when MOZ_MEMORY is not set 1.10 +#endif 1.11 + 1.12 +#ifndef MOZ_REPLACE_MALLOC 1.13 +# error Should not compile this file when replace-malloc is disabled 1.14 +#endif 1.15 + 1.16 +#ifdef MOZ_NATIVE_JEMALLOC 1.17 +# error Should not compile this file when we want to use native jemalloc 1.18 +#endif 1.19 + 1.20 +#include "mozmemory_wrap.h" 1.21 + 1.22 +/* Declare all je_* functions */ 1.23 +#define MALLOC_DECL(name, return_type, ...) \ 1.24 + return_type je_ ## name(__VA_ARGS__); 1.25 +#include "malloc_decls.h" 1.26 + 1.27 +#include "mozilla/Likely.h" 1.28 +/* 1.29 + * Windows doesn't come with weak imports as they are possible with 1.30 + * LD_PRELOAD or DYLD_INSERT_LIBRARIES on Linux/OSX. On this platform, 1.31 + * the replacement functions are defined as variable pointers to the 1.32 + * function resolved with GetProcAddress() instead of weak definitions 1.33 + * of functions. On Android, the same needs to happen as well, because 1.34 + * the Android linker doesn't handle weak linking with non LD_PRELOADed 1.35 + * libraries, but LD_PRELOADing is not very convenient on Android, with 1.36 + * the zygote. 1.37 + */ 1.38 +#ifdef XP_DARWIN 1.39 +# define MOZ_REPLACE_WEAK __attribute__((weak_import)) 1.40 +#elif defined(XP_WIN) || defined(MOZ_WIDGET_ANDROID) 1.41 +# define MOZ_NO_REPLACE_FUNC_DECL 1.42 +#elif defined(__GNUC__) 1.43 +# define MOZ_REPLACE_WEAK __attribute__((weak)) 1.44 +#endif 1.45 + 1.46 +#include "replace_malloc.h" 1.47 + 1.48 +#define MALLOC_DECL(name, return_type, ...) \ 1.49 + je_ ## name, 1.50 + 1.51 +static const malloc_table_t malloc_table = { 1.52 +#include "malloc_decls.h" 1.53 +}; 1.54 + 1.55 +#ifdef MOZ_NO_REPLACE_FUNC_DECL 1.56 +# define MALLOC_DECL(name, return_type, ...) \ 1.57 + typedef return_type (replace_ ## name ## _impl_t)(__VA_ARGS__); \ 1.58 + replace_ ## name ## _impl_t *replace_ ## name = NULL; 1.59 +# define MALLOC_FUNCS MALLOC_FUNCS_ALL 1.60 +# include "malloc_decls.h" 1.61 + 1.62 +# ifdef XP_WIN 1.63 +# include <windows.h> 1.64 +static void 1.65 +replace_malloc_init_funcs() 1.66 +{ 1.67 + char replace_malloc_lib[1024]; 1.68 + if (GetEnvironmentVariableA("MOZ_REPLACE_MALLOC_LIB", (LPSTR)&replace_malloc_lib, 1.69 + sizeof(replace_malloc_lib)) > 0) { 1.70 + HMODULE handle = LoadLibraryA(replace_malloc_lib); 1.71 + if (handle) { 1.72 +#define MALLOC_DECL(name, ...) \ 1.73 + replace_ ## name = (replace_ ## name ## _impl_t *) GetProcAddress(handle, "replace_" # name); 1.74 + 1.75 +# define MALLOC_FUNCS MALLOC_FUNCS_ALL 1.76 +#include "malloc_decls.h" 1.77 + } 1.78 + } 1.79 +} 1.80 +# elif defined(MOZ_WIDGET_ANDROID) 1.81 +# include <dlfcn.h> 1.82 +static void 1.83 +replace_malloc_init_funcs() 1.84 +{ 1.85 + char *replace_malloc_lib = getenv("MOZ_REPLACE_MALLOC_LIB"); 1.86 + if (replace_malloc_lib && *replace_malloc_lib) { 1.87 + void *handle = dlopen(replace_malloc_lib, RTLD_LAZY); 1.88 + if (handle) { 1.89 +#define MALLOC_DECL(name, ...) \ 1.90 + replace_ ## name = (replace_ ## name ## _impl_t *) dlsym(handle, "replace_" # name); 1.91 + 1.92 +# define MALLOC_FUNCS MALLOC_FUNCS_ALL 1.93 +#include "malloc_decls.h" 1.94 + } 1.95 + } 1.96 +} 1.97 +# else 1.98 +# error No implementation for replace_malloc_init_funcs() 1.99 +# endif 1.100 + 1.101 +#endif /* MOZ_NO_REPLACE_FUNC_DECL */ 1.102 + 1.103 +/* 1.104 + * Below is the malloc implementation overriding jemalloc and calling the 1.105 + * replacement functions if they exist. 1.106 + */ 1.107 + 1.108 +/* 1.109 + * On OSX, MOZ_MEMORY_API is defined to nothing, because malloc functions 1.110 + * are meant to have hidden visibility. But since the functions are only 1.111 + * used locally in the zone allocator further below, we can allow the 1.112 + * compiler to optimize more by switching to static. 1.113 + */ 1.114 +#ifdef XP_DARWIN 1.115 +#undef MOZ_MEMORY_API 1.116 +#define MOZ_MEMORY_API static 1.117 +#endif 1.118 + 1.119 +/* 1.120 + * Malloc implementation functions are MOZ_MEMORY_API, and jemalloc 1.121 + * specific functions MOZ_JEMALLOC_API; see mozmemory_wrap.h 1.122 + */ 1.123 +#define MALLOC_DECL(name, return_type, ...) \ 1.124 + MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__); 1.125 +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC 1.126 +#include "malloc_decls.h" 1.127 + 1.128 +#define MALLOC_DECL(name, return_type, ...) \ 1.129 + MOZ_JEMALLOC_API return_type name ## _impl(__VA_ARGS__); 1.130 +#define MALLOC_FUNCS MALLOC_FUNCS_JEMALLOC 1.131 +#include "malloc_decls.h" 1.132 + 1.133 +static int replace_malloc_initialized = 0; 1.134 +static void 1.135 +init() 1.136 +{ 1.137 +#ifdef MOZ_NO_REPLACE_FUNC_DECL 1.138 + replace_malloc_init_funcs(); 1.139 +#endif 1.140 + // Set this *before* calling replace_init, otherwise if replace_init calls 1.141 + // malloc() we'll get an infinite loop. 1.142 + replace_malloc_initialized = 1; 1.143 + if (replace_init) 1.144 + replace_init(&malloc_table); 1.145 +} 1.146 + 1.147 +void* 1.148 +malloc_impl(size_t size) 1.149 +{ 1.150 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.151 + init(); 1.152 + if (MOZ_LIKELY(!replace_malloc)) 1.153 + return je_malloc(size); 1.154 + return replace_malloc(size); 1.155 +} 1.156 + 1.157 +int 1.158 +posix_memalign_impl(void **memptr, size_t alignment, size_t size) 1.159 +{ 1.160 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.161 + init(); 1.162 + if (MOZ_LIKELY(!replace_posix_memalign)) 1.163 + return je_posix_memalign(memptr, alignment, size); 1.164 + return replace_posix_memalign(memptr, alignment, size); 1.165 +} 1.166 + 1.167 +void* 1.168 +aligned_alloc_impl(size_t alignment, size_t size) 1.169 +{ 1.170 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.171 + init(); 1.172 + if (MOZ_LIKELY(!replace_aligned_alloc)) 1.173 + return je_aligned_alloc(alignment, size); 1.174 + return replace_aligned_alloc(alignment, size); 1.175 +} 1.176 + 1.177 +void* 1.178 +calloc_impl(size_t num, size_t size) 1.179 +{ 1.180 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.181 + init(); 1.182 + if (MOZ_LIKELY(!replace_calloc)) 1.183 + return je_calloc(num, size); 1.184 + return replace_calloc(num, size); 1.185 +} 1.186 + 1.187 +void* 1.188 +realloc_impl(void *ptr, size_t size) 1.189 +{ 1.190 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.191 + init(); 1.192 + if (MOZ_LIKELY(!replace_realloc)) 1.193 + return je_realloc(ptr, size); 1.194 + return replace_realloc(ptr, size); 1.195 +} 1.196 + 1.197 +void 1.198 +free_impl(void *ptr) 1.199 +{ 1.200 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.201 + init(); 1.202 + if (MOZ_LIKELY(!replace_free)) 1.203 + je_free(ptr); 1.204 + else 1.205 + replace_free(ptr); 1.206 +} 1.207 + 1.208 +void* 1.209 +memalign_impl(size_t alignment, size_t size) 1.210 +{ 1.211 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.212 + init(); 1.213 + if (MOZ_LIKELY(!replace_memalign)) 1.214 + return je_memalign(alignment, size); 1.215 + return replace_memalign(alignment, size); 1.216 +} 1.217 + 1.218 +void* 1.219 +valloc_impl(size_t size) 1.220 +{ 1.221 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.222 + init(); 1.223 + if (MOZ_LIKELY(!replace_valloc)) 1.224 + return je_valloc(size); 1.225 + return replace_valloc(size); 1.226 +} 1.227 + 1.228 +size_t 1.229 +malloc_usable_size_impl(usable_ptr_t ptr) 1.230 +{ 1.231 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.232 + init(); 1.233 + if (MOZ_LIKELY(!replace_malloc_usable_size)) 1.234 + return je_malloc_usable_size(ptr); 1.235 + return replace_malloc_usable_size(ptr); 1.236 +} 1.237 + 1.238 +size_t 1.239 +malloc_good_size_impl(size_t size) 1.240 +{ 1.241 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.242 + init(); 1.243 + if (MOZ_LIKELY(!replace_malloc_good_size)) 1.244 + return je_malloc_good_size(size); 1.245 + return replace_malloc_good_size(size); 1.246 +} 1.247 + 1.248 +void 1.249 +jemalloc_stats_impl(jemalloc_stats_t *stats) 1.250 +{ 1.251 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.252 + init(); 1.253 + if (MOZ_LIKELY(!replace_jemalloc_stats)) 1.254 + je_jemalloc_stats(stats); 1.255 + else 1.256 + replace_jemalloc_stats(stats); 1.257 +} 1.258 + 1.259 +void 1.260 +jemalloc_purge_freed_pages_impl() 1.261 +{ 1.262 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.263 + init(); 1.264 + if (MOZ_LIKELY(!replace_jemalloc_purge_freed_pages)) 1.265 + je_jemalloc_purge_freed_pages(); 1.266 + else 1.267 + replace_jemalloc_purge_freed_pages(); 1.268 +} 1.269 + 1.270 +void 1.271 +jemalloc_free_dirty_pages_impl() 1.272 +{ 1.273 + if (MOZ_UNLIKELY(!replace_malloc_initialized)) 1.274 + init(); 1.275 + if (MOZ_LIKELY(!replace_jemalloc_free_dirty_pages)) 1.276 + je_jemalloc_free_dirty_pages(); 1.277 + else 1.278 + replace_jemalloc_free_dirty_pages(); 1.279 +} 1.280 + 1.281 +/* The following comment and definitions are from jemalloc.c: */ 1.282 +#if defined(__GLIBC__) && !defined(__UCLIBC__) 1.283 + 1.284 +/* 1.285 + * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible 1.286 + * to inconsistently reference libc's malloc(3)-compatible functions 1.287 + * (https://bugzilla.mozilla.org/show_bug.cgi?id=493541). 1.288 + * 1.289 + * These definitions interpose hooks in glibc. The functions are actually 1.290 + * passed an extra argument for the caller return address, which will be 1.291 + * ignored. 1.292 + */ 1.293 + 1.294 +typedef void (* __free_hook_type)(void *ptr); 1.295 +typedef void *(* __malloc_hook_type)(size_t size); 1.296 +typedef void *(* __realloc_hook_type)(void *ptr, size_t size); 1.297 +typedef void *(* __memalign_hook_type)(size_t alignment, size_t size); 1.298 + 1.299 +MOZ_MEMORY_API __free_hook_type __free_hook = free_impl; 1.300 +MOZ_MEMORY_API __malloc_hook_type __malloc_hook = malloc_impl; 1.301 +MOZ_MEMORY_API __realloc_hook_type __realloc_hook = realloc_impl; 1.302 +MOZ_MEMORY_API __memalign_hook_type __memalign_hook = memalign_impl; 1.303 + 1.304 +#endif 1.305 + 1.306 +/* 1.307 + * The following is a OSX zone allocator implementation. 1.308 + * /!\ WARNING. It assumes the underlying malloc implementation's 1.309 + * malloc_usable_size returns 0 when the given pointer is not owned by 1.310 + * the allocator. Sadly, OSX does call zone_size with pointers not 1.311 + * owned by the allocator. 1.312 + */ 1.313 + 1.314 +#ifdef XP_DARWIN 1.315 +#include <stdlib.h> 1.316 +#include <malloc/malloc.h> 1.317 +#include "mozilla/Assertions.h" 1.318 + 1.319 +static size_t 1.320 +zone_size(malloc_zone_t *zone, void *ptr) 1.321 +{ 1.322 + return malloc_usable_size_impl(ptr); 1.323 +} 1.324 + 1.325 +static void * 1.326 +zone_malloc(malloc_zone_t *zone, size_t size) 1.327 +{ 1.328 + return malloc_impl(size); 1.329 +} 1.330 + 1.331 +static void * 1.332 +zone_calloc(malloc_zone_t *zone, size_t num, size_t size) 1.333 +{ 1.334 + return calloc_impl(num, size); 1.335 +} 1.336 + 1.337 +static void * 1.338 +zone_realloc(malloc_zone_t *zone, void *ptr, size_t size) 1.339 +{ 1.340 + if (malloc_usable_size_impl(ptr)) 1.341 + return realloc_impl(ptr, size); 1.342 + return realloc(ptr, size); 1.343 +} 1.344 + 1.345 +static void 1.346 +zone_free(malloc_zone_t *zone, void *ptr) 1.347 +{ 1.348 + if (malloc_usable_size_impl(ptr)) { 1.349 + free_impl(ptr); 1.350 + return; 1.351 + } 1.352 + free(ptr); 1.353 +} 1.354 + 1.355 +static void 1.356 +zone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) 1.357 +{ 1.358 + size_t current_size = malloc_usable_size_impl(ptr); 1.359 + if (current_size) { 1.360 + MOZ_ASSERT(current_size == size); 1.361 + free_impl(ptr); 1.362 + return; 1.363 + } 1.364 + free(ptr); 1.365 +} 1.366 + 1.367 +static void * 1.368 +zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) 1.369 +{ 1.370 + void *ptr; 1.371 + if (posix_memalign_impl(&ptr, alignment, size) == 0) 1.372 + return ptr; 1.373 + return NULL; 1.374 +} 1.375 + 1.376 +static void * 1.377 +zone_valloc(malloc_zone_t *zone, size_t size) 1.378 +{ 1.379 + return valloc_impl(size); 1.380 +} 1.381 + 1.382 +static void * 1.383 +zone_destroy(malloc_zone_t *zone) 1.384 +{ 1.385 + /* This function should never be called. */ 1.386 + MOZ_CRASH(); 1.387 +} 1.388 + 1.389 +static size_t 1.390 +zone_good_size(malloc_zone_t *zone, size_t size) 1.391 +{ 1.392 + return malloc_good_size_impl(size); 1.393 +} 1.394 + 1.395 +#ifdef MOZ_JEMALLOC 1.396 + 1.397 +#include "jemalloc/internal/jemalloc_internal.h" 1.398 + 1.399 +static void 1.400 +zone_force_lock(malloc_zone_t *zone) 1.401 +{ 1.402 + /* /!\ This calls into jemalloc. It works because we're linked in the 1.403 + * same library. Stolen from jemalloc's zone.c. */ 1.404 + if (isthreaded) 1.405 + jemalloc_prefork(); 1.406 +} 1.407 + 1.408 +static void 1.409 +zone_force_unlock(malloc_zone_t *zone) 1.410 +{ 1.411 + /* /!\ This calls into jemalloc. It works because we're linked in the 1.412 + * same library. Stolen from jemalloc's zone.c. */ 1.413 + if (isthreaded) 1.414 + jemalloc_postfork_parent(); 1.415 +} 1.416 + 1.417 +#else 1.418 + 1.419 +#define JEMALLOC_ZONE_VERSION 6 1.420 + 1.421 +/* Empty implementations are needed, because fork() calls zone->force_(un)lock 1.422 + * unconditionally. */ 1.423 +static void 1.424 +zone_force_lock(malloc_zone_t *zone) 1.425 +{ 1.426 +} 1.427 + 1.428 +static void 1.429 +zone_force_unlock(malloc_zone_t *zone) 1.430 +{ 1.431 +} 1.432 + 1.433 +#endif 1.434 + 1.435 +static malloc_zone_t zone; 1.436 +static struct malloc_introspection_t zone_introspect; 1.437 + 1.438 +__attribute__((constructor)) void 1.439 +register_zone(void) 1.440 +{ 1.441 + zone.size = (void *)zone_size; 1.442 + zone.malloc = (void *)zone_malloc; 1.443 + zone.calloc = (void *)zone_calloc; 1.444 + zone.valloc = (void *)zone_valloc; 1.445 + zone.free = (void *)zone_free; 1.446 + zone.realloc = (void *)zone_realloc; 1.447 + zone.destroy = (void *)zone_destroy; 1.448 + zone.zone_name = "replace_malloc_zone"; 1.449 + zone.batch_malloc = NULL; 1.450 + zone.batch_free = NULL; 1.451 + zone.introspect = &zone_introspect; 1.452 + zone.version = JEMALLOC_ZONE_VERSION; 1.453 + zone.memalign = zone_memalign; 1.454 + zone.free_definite_size = zone_free_definite_size; 1.455 +#if (JEMALLOC_ZONE_VERSION >= 8) 1.456 + zone.pressure_relief = NULL; 1.457 +#endif 1.458 + zone_introspect.enumerator = NULL; 1.459 + zone_introspect.good_size = (void *)zone_good_size; 1.460 + zone_introspect.check = NULL; 1.461 + zone_introspect.print = NULL; 1.462 + zone_introspect.log = NULL; 1.463 + zone_introspect.force_lock = (void *)zone_force_lock; 1.464 + zone_introspect.force_unlock = (void *)zone_force_unlock; 1.465 + zone_introspect.statistics = NULL; 1.466 + zone_introspect.zone_locked = NULL; 1.467 +#if (JEMALLOC_ZONE_VERSION >= 7) 1.468 + zone_introspect.enable_discharge_checking = NULL; 1.469 + zone_introspect.disable_discharge_checking = NULL; 1.470 + zone_introspect.discharge = NULL; 1.471 +#ifdef __BLOCKS__ 1.472 + zone_introspect.enumerate_discharged_pointers = NULL; 1.473 +#else 1.474 + zone_introspect.enumerate_unavailable_without_blocks = NULL; 1.475 +#endif 1.476 +#endif 1.477 + 1.478 + /* 1.479 + * The default purgeable zone is created lazily by OSX's libc. It uses 1.480 + * the default zone when it is created for "small" allocations 1.481 + * (< 15 KiB), but assumes the default zone is a scalable_zone. This 1.482 + * obviously fails when the default zone is the jemalloc zone, so 1.483 + * malloc_default_purgeable_zone is called beforehand so that the 1.484 + * default purgeable zone is created when the default zone is still 1.485 + * a scalable_zone. As purgeable zones only exist on >= 10.6, we need 1.486 + * to check for the existence of malloc_default_purgeable_zone() at 1.487 + * run time. 1.488 + */ 1.489 + malloc_default_purgeable_zone(); 1.490 + 1.491 + /* Register the custom zone. At this point it won't be the default. */ 1.492 + malloc_zone_register(&zone); 1.493 + 1.494 + /* 1.495 + * Unregister and reregister the default zone. On OSX >= 10.6, 1.496 + * unregistering takes the last registered zone and places it at the 1.497 + * location of the specified zone. Unregistering the default zone thus 1.498 + * makes the last registered one the default. On OSX < 10.6, 1.499 + * unregistering shifts all registered zones. The first registered zone 1.500 + * then becomes the default. 1.501 + */ 1.502 + do { 1.503 + malloc_zone_t *default_zone = malloc_default_zone(); 1.504 + malloc_zone_unregister(default_zone); 1.505 + malloc_zone_register(default_zone); 1.506 + } while (malloc_default_zone() != &zone); 1.507 +} 1.508 +#endif