/* * 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; int je_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg) { size_t pagesize = getpagesize(); tsd_t* tsd = tsd_fetch_min(); rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd); // Make sure the pointer is aligned to at least 8 bytes. uintptr_t ptr = (base + 7) & ~7; uintptr_t end_ptr = ptr + size; while (ptr < end_ptr) { extent_t* extent = iealloc(tsd_tsdn(tsd), (void*)ptr); if (extent == NULL) { // Skip to the next page, guaranteed no other pointers on this page. ptr += pagesize; continue; } if (extent_szind_get_maybe_invalid(extent) >= NSIZES) { // Ignore this unused extent. ptr = (uintptr_t)extent_past_get(extent); continue; } szind_t szind; bool slab; rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx, ptr, true, &szind, &slab); if (slab) { // Small allocation. szind_t binind = extent_szind_get(extent); const bin_info_t* bin_info = &bin_infos[binind]; arena_slab_data_t* slab_data = extent_slab_data_get(extent); uintptr_t first_ptr = (uintptr_t)extent_addr_get(extent); size_t bin_size = bin_info->reg_size; // Align the pointer to the bin size. ptr = (ptr + bin_size - 1) & ~(bin_size - 1); for (size_t bit = (ptr - first_ptr) / bin_size; bit < bin_info->bitmap_info.nbits; bit++) { if (bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, bit)) { uintptr_t allocated_ptr = first_ptr + bin_size * bit; if (allocated_ptr >= end_ptr) { break; } callback(allocated_ptr, bin_size, arg); } } } else if (extent_state_get(extent) == extent_state_active) { // Large allocation. uintptr_t base_ptr = (uintptr_t)extent_addr_get(extent); if (ptr <= base_ptr) { // This extent is actually allocated and within the range to check. callback(base_ptr, extent_usize_get(extent), arg); } } ptr = (uintptr_t)extent_past_get(extent); } return 0; } 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); // Disable the tcache (if not already disabled) so that we don't // have to search the tcache for pointers. 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) { // Re-enable the tcache if it was enabled before the disabled call. je_mallctl("thread.tcache.enabled", NULL, NULL, &malloc_disabled_tcache, sizeof(malloc_disabled_tcache)); } pthread_mutex_unlock(&malloc_disabled_lock); }