190 lines
5.9 KiB
C
190 lines
5.9 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
static pthread_mutex_t malloc_disabled_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
static bool malloc_disabled_tcache;
|
|
|
|
static void je_iterate_chunk(arena_chunk_t *chunk,
|
|
void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg);
|
|
static void je_iterate_small(arena_run_t *run,
|
|
void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg);
|
|
|
|
/* je_iterate calls callback for each allocation found in the memory region
|
|
* between [base, base+size). base will be rounded down to by the jemalloc
|
|
* chunk size, and base+size will be rounded up to the chunk size. If no memory
|
|
* managed by jemalloc is found in the requested region, je_iterate returns -1
|
|
* and sets errno to EINVAL.
|
|
*
|
|
* je_iterate must be called when no allocations are in progress, either
|
|
* when single-threaded (for example just after a fork), or between
|
|
* jemalloc_prefork() and jemalloc_postfork_parent(). The callback must
|
|
* not attempt to allocate with jemalloc.
|
|
*/
|
|
int je_iterate(uintptr_t base, size_t size,
|
|
void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg) {
|
|
|
|
int error = EINVAL;
|
|
uintptr_t ptr = (uintptr_t)CHUNK_ADDR2BASE(base);
|
|
uintptr_t end = CHUNK_CEILING(base + size);
|
|
|
|
while (ptr < end) {
|
|
assert(ptr == (uintptr_t)CHUNK_ADDR2BASE(ptr));
|
|
extent_node_t *node;
|
|
|
|
node = chunk_lookup((void *)ptr, false);
|
|
if (node == NULL) {
|
|
ptr += chunksize;
|
|
continue;
|
|
}
|
|
|
|
assert(extent_node_achunk_get(node) ||
|
|
(uintptr_t)extent_node_addr_get(node) == ptr);
|
|
|
|
error = 0;
|
|
if (extent_node_achunk_get(node)) {
|
|
/* Chunk */
|
|
arena_chunk_t *chunk = (arena_chunk_t *)ptr;
|
|
ptr += chunksize;
|
|
|
|
if (&chunk->node != node) {
|
|
/* Empty retained chunk */
|
|
continue;
|
|
}
|
|
|
|
je_iterate_chunk(chunk, callback, arg);
|
|
} else if ((uintptr_t)extent_node_addr_get(node) == ptr) {
|
|
/* Huge allocation */
|
|
callback(ptr, extent_node_size_get(node), arg);
|
|
ptr = CHUNK_CEILING(ptr + extent_node_size_get(node));
|
|
}
|
|
}
|
|
|
|
if (error) {
|
|
set_errno(error);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over a valid jemalloc chunk, calling callback for each large
|
|
* allocation run, and calling je_iterate_small for each small allocation run */
|
|
static void je_iterate_chunk(arena_chunk_t *chunk,
|
|
void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg) {
|
|
size_t pageind;
|
|
|
|
pageind = map_bias;
|
|
|
|
while (pageind < chunk_npages) {
|
|
size_t mapbits;
|
|
size_t size;
|
|
|
|
mapbits = arena_mapbits_get(chunk, pageind);
|
|
if (!arena_mapbits_allocated_get(chunk, pageind)) {
|
|
/* Unallocated run */
|
|
size = arena_mapbits_unallocated_size_get(chunk, pageind);
|
|
} else if (arena_mapbits_large_get(chunk, pageind)) {
|
|
/* Large allocation run */
|
|
void *rpages;
|
|
|
|
size = arena_mapbits_large_size_get(chunk, pageind);
|
|
rpages = arena_miscelm_to_rpages(arena_miscelm_get_mutable(chunk, pageind));
|
|
callback((uintptr_t)rpages, size, arg);
|
|
} else {
|
|
/* Run of small allocations */
|
|
szind_t binind;
|
|
arena_run_t *run;
|
|
|
|
assert(arena_mapbits_small_runind_get(chunk, pageind) == pageind);
|
|
binind = arena_mapbits_binind_get(chunk, pageind);
|
|
run = &arena_miscelm_get_mutable(chunk, pageind)->run;
|
|
assert(run->binind == binind);
|
|
size = arena_bin_info[binind].run_size;
|
|
|
|
je_iterate_small(run, callback, arg);
|
|
}
|
|
assert(size == PAGE_CEILING(size));
|
|
assert(size > 0);
|
|
pageind += size >> LG_PAGE;
|
|
}
|
|
|
|
}
|
|
|
|
/* Iterate over a valid jemalloc small allocation run, calling callback for each
|
|
* active allocation. */
|
|
static void je_iterate_small(arena_run_t *run,
|
|
void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg) {
|
|
szind_t binind;
|
|
const arena_bin_info_t *bin_info;
|
|
uint32_t regind;
|
|
uintptr_t ptr;
|
|
void *rpages;
|
|
|
|
binind = run->binind;
|
|
bin_info = &arena_bin_info[binind];
|
|
rpages = arena_miscelm_to_rpages(arena_run_to_miscelm(run));
|
|
ptr = (uintptr_t)rpages + bin_info->reg0_offset;
|
|
|
|
for (regind = 0; regind < bin_info->nregs; regind++) {
|
|
if (bitmap_get(run->bitmap, &bin_info->bitmap_info, regind)) {
|
|
callback(ptr, bin_info->reg_size, arg);
|
|
}
|
|
ptr += bin_info->reg_interval;
|
|
}
|
|
}
|
|
|
|
static void je_malloc_disable_prefork() {
|
|
pthread_mutex_lock(&malloc_disabled_lock);
|
|
}
|
|
|
|
static void je_malloc_disable_postfork_parent() {
|
|
pthread_mutex_unlock(&malloc_disabled_lock);
|
|
}
|
|
|
|
static void je_malloc_disable_postfork_child() {
|
|
pthread_mutex_init(&malloc_disabled_lock, NULL);
|
|
}
|
|
|
|
void je_malloc_disable_init() {
|
|
if (pthread_atfork(je_malloc_disable_prefork,
|
|
je_malloc_disable_postfork_parent, je_malloc_disable_postfork_child) != 0) {
|
|
malloc_write("<jemalloc>: Error in pthread_atfork()\n");
|
|
if (opt_abort)
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void je_malloc_disable() {
|
|
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
|
|
pthread_once(&once_control, je_malloc_disable_init);
|
|
|
|
pthread_mutex_lock(&malloc_disabled_lock);
|
|
bool new_tcache = false;
|
|
size_t old_len = sizeof(malloc_disabled_tcache);
|
|
je_mallctl("thread.tcache.enabled",
|
|
&malloc_disabled_tcache, &old_len,
|
|
&new_tcache, sizeof(new_tcache));
|
|
jemalloc_prefork();
|
|
}
|
|
|
|
void je_malloc_enable() {
|
|
jemalloc_postfork_parent();
|
|
if (malloc_disabled_tcache) {
|
|
je_mallctl("thread.tcache.enabled", NULL, NULL,
|
|
&malloc_disabled_tcache, sizeof(malloc_disabled_tcache));
|
|
}
|
|
pthread_mutex_unlock(&malloc_disabled_lock);
|
|
}
|