diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-24 09:54:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-24 09:54:44 +0000 |
commit | 836b47cb7e99a977c5a23b059ca1d0b5065d310e (patch) | |
tree | 1604da8f482d02effa033c94a84be42bc0c848c3 /fluent-bit/lib/jemalloc-5.3.0/src | |
parent | Releasing debian version 1.44.3-2. (diff) | |
download | netdata-836b47cb7e99a977c5a23b059ca1d0b5065d310e.tar.xz netdata-836b47cb7e99a977c5a23b059ca1d0b5065d310e.zip |
Merging upstream version 1.46.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fluent-bit/lib/jemalloc-5.3.0/src')
65 files changed, 0 insertions, 32750 deletions
diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/arena.c b/fluent-bit/lib/jemalloc-5.3.0/src/arena.c deleted file mode 100644 index 857b27c5..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/arena.c +++ /dev/null @@ -1,1891 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/decay.h" -#include "jemalloc/internal/ehooks.h" -#include "jemalloc/internal/extent_dss.h" -#include "jemalloc/internal/extent_mmap.h" -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/rtree.h" -#include "jemalloc/internal/safety_check.h" -#include "jemalloc/internal/util.h" - -JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS - -/******************************************************************************/ -/* Data. */ - -/* - * Define names for both unininitialized and initialized phases, so that - * options and mallctl processing are straightforward. - */ -const char *percpu_arena_mode_names[] = { - "percpu", - "phycpu", - "disabled", - "percpu", - "phycpu" -}; -percpu_arena_mode_t opt_percpu_arena = PERCPU_ARENA_DEFAULT; - -ssize_t opt_dirty_decay_ms = DIRTY_DECAY_MS_DEFAULT; -ssize_t opt_muzzy_decay_ms = MUZZY_DECAY_MS_DEFAULT; - -static atomic_zd_t dirty_decay_ms_default; -static atomic_zd_t muzzy_decay_ms_default; - -emap_t arena_emap_global; -pa_central_t arena_pa_central_global; - -div_info_t arena_binind_div_info[SC_NBINS]; - -size_t opt_oversize_threshold = OVERSIZE_THRESHOLD_DEFAULT; -size_t oversize_threshold = OVERSIZE_THRESHOLD_DEFAULT; - -uint32_t arena_bin_offsets[SC_NBINS]; -static unsigned nbins_total; - -static unsigned huge_arena_ind; - -const arena_config_t arena_config_default = { - /* .extent_hooks = */ (extent_hooks_t *)&ehooks_default_extent_hooks, - /* .metadata_use_hooks = */ true, -}; - -/******************************************************************************/ -/* - * Function prototypes for static functions that are referenced prior to - * definition. - */ - -static bool arena_decay_dirty(tsdn_t *tsdn, arena_t *arena, - bool is_background_thread, bool all); -static void arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, edata_t *slab, - bin_t *bin); -static void -arena_maybe_do_deferred_work(tsdn_t *tsdn, arena_t *arena, decay_t *decay, - size_t npages_new); - -/******************************************************************************/ - -void -arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads, - const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms, - size_t *nactive, size_t *ndirty, size_t *nmuzzy) { - *nthreads += arena_nthreads_get(arena, false); - *dss = dss_prec_names[arena_dss_prec_get(arena)]; - *dirty_decay_ms = arena_decay_ms_get(arena, extent_state_dirty); - *muzzy_decay_ms = arena_decay_ms_get(arena, extent_state_muzzy); - pa_shard_basic_stats_merge(&arena->pa_shard, nactive, ndirty, nmuzzy); -} - -void -arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads, - const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms, - size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats, - bin_stats_data_t *bstats, arena_stats_large_t *lstats, - pac_estats_t *estats, hpa_shard_stats_t *hpastats, sec_stats_t *secstats) { - cassert(config_stats); - - arena_basic_stats_merge(tsdn, arena, nthreads, dss, dirty_decay_ms, - muzzy_decay_ms, nactive, ndirty, nmuzzy); - - size_t base_allocated, base_resident, base_mapped, metadata_thp; - base_stats_get(tsdn, arena->base, &base_allocated, &base_resident, - &base_mapped, &metadata_thp); - size_t pac_mapped_sz = pac_mapped(&arena->pa_shard.pac); - astats->mapped += base_mapped + pac_mapped_sz; - astats->resident += base_resident; - - LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx); - - astats->base += base_allocated; - atomic_load_add_store_zu(&astats->internal, arena_internal_get(arena)); - astats->metadata_thp += metadata_thp; - - for (szind_t i = 0; i < SC_NSIZES - SC_NBINS; i++) { - uint64_t nmalloc = locked_read_u64(tsdn, - LOCKEDINT_MTX(arena->stats.mtx), - &arena->stats.lstats[i].nmalloc); - locked_inc_u64_unsynchronized(&lstats[i].nmalloc, nmalloc); - astats->nmalloc_large += nmalloc; - - uint64_t ndalloc = locked_read_u64(tsdn, - LOCKEDINT_MTX(arena->stats.mtx), - &arena->stats.lstats[i].ndalloc); - locked_inc_u64_unsynchronized(&lstats[i].ndalloc, ndalloc); - astats->ndalloc_large += ndalloc; - - uint64_t nrequests = locked_read_u64(tsdn, - LOCKEDINT_MTX(arena->stats.mtx), - &arena->stats.lstats[i].nrequests); - locked_inc_u64_unsynchronized(&lstats[i].nrequests, - nmalloc + nrequests); - astats->nrequests_large += nmalloc + nrequests; - - /* nfill == nmalloc for large currently. */ - locked_inc_u64_unsynchronized(&lstats[i].nfills, nmalloc); - astats->nfills_large += nmalloc; - - uint64_t nflush = locked_read_u64(tsdn, - LOCKEDINT_MTX(arena->stats.mtx), - &arena->stats.lstats[i].nflushes); - locked_inc_u64_unsynchronized(&lstats[i].nflushes, nflush); - astats->nflushes_large += nflush; - - assert(nmalloc >= ndalloc); - assert(nmalloc - ndalloc <= SIZE_T_MAX); - size_t curlextents = (size_t)(nmalloc - ndalloc); - lstats[i].curlextents += curlextents; - astats->allocated_large += - curlextents * sz_index2size(SC_NBINS + i); - } - - pa_shard_stats_merge(tsdn, &arena->pa_shard, &astats->pa_shard_stats, - estats, hpastats, secstats, &astats->resident); - - LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx); - - /* Currently cached bytes and sanitizer-stashed bytes in tcache. */ - astats->tcache_bytes = 0; - astats->tcache_stashed_bytes = 0; - malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); - cache_bin_array_descriptor_t *descriptor; - ql_foreach(descriptor, &arena->cache_bin_array_descriptor_ql, link) { - for (szind_t i = 0; i < nhbins; i++) { - cache_bin_t *cache_bin = &descriptor->bins[i]; - cache_bin_sz_t ncached, nstashed; - cache_bin_nitems_get_remote(cache_bin, - &tcache_bin_info[i], &ncached, &nstashed); - - astats->tcache_bytes += ncached * sz_index2size(i); - astats->tcache_stashed_bytes += nstashed * - sz_index2size(i); - } - } - malloc_mutex_prof_read(tsdn, - &astats->mutex_prof_data[arena_prof_mutex_tcache_list], - &arena->tcache_ql_mtx); - malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); - -#define READ_ARENA_MUTEX_PROF_DATA(mtx, ind) \ - malloc_mutex_lock(tsdn, &arena->mtx); \ - malloc_mutex_prof_read(tsdn, &astats->mutex_prof_data[ind], \ - &arena->mtx); \ - malloc_mutex_unlock(tsdn, &arena->mtx); - - /* Gather per arena mutex profiling data. */ - READ_ARENA_MUTEX_PROF_DATA(large_mtx, arena_prof_mutex_large); - READ_ARENA_MUTEX_PROF_DATA(base->mtx, - arena_prof_mutex_base); -#undef READ_ARENA_MUTEX_PROF_DATA - pa_shard_mtx_stats_read(tsdn, &arena->pa_shard, - astats->mutex_prof_data); - - nstime_copy(&astats->uptime, &arena->create_time); - nstime_update(&astats->uptime); - nstime_subtract(&astats->uptime, &arena->create_time); - - for (szind_t i = 0; i < SC_NBINS; i++) { - for (unsigned j = 0; j < bin_infos[i].n_shards; j++) { - bin_stats_merge(tsdn, &bstats[i], - arena_get_bin(arena, i, j)); - } - } -} - -static void -arena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena, - bool is_background_thread) { - if (!background_thread_enabled() || is_background_thread) { - return; - } - background_thread_info_t *info = - arena_background_thread_info_get(arena); - if (background_thread_indefinite_sleep(info)) { - arena_maybe_do_deferred_work(tsdn, arena, - &arena->pa_shard.pac.decay_dirty, 0); - } -} - -/* - * React to deferred work generated by a PAI function. - */ -void arena_handle_deferred_work(tsdn_t *tsdn, arena_t *arena) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - if (decay_immediately(&arena->pa_shard.pac.decay_dirty)) { - arena_decay_dirty(tsdn, arena, false, true); - } - arena_background_thread_inactivity_check(tsdn, arena, false); -} - -static void * -arena_slab_reg_alloc(edata_t *slab, const bin_info_t *bin_info) { - void *ret; - slab_data_t *slab_data = edata_slab_data_get(slab); - size_t regind; - - assert(edata_nfree_get(slab) > 0); - assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info)); - - regind = bitmap_sfu(slab_data->bitmap, &bin_info->bitmap_info); - ret = (void *)((uintptr_t)edata_addr_get(slab) + - (uintptr_t)(bin_info->reg_size * regind)); - edata_nfree_dec(slab); - return ret; -} - -static void -arena_slab_reg_alloc_batch(edata_t *slab, const bin_info_t *bin_info, - unsigned cnt, void** ptrs) { - slab_data_t *slab_data = edata_slab_data_get(slab); - - assert(edata_nfree_get(slab) >= cnt); - assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info)); - -#if (! defined JEMALLOC_INTERNAL_POPCOUNTL) || (defined BITMAP_USE_TREE) - for (unsigned i = 0; i < cnt; i++) { - size_t regind = bitmap_sfu(slab_data->bitmap, - &bin_info->bitmap_info); - *(ptrs + i) = (void *)((uintptr_t)edata_addr_get(slab) + - (uintptr_t)(bin_info->reg_size * regind)); - } -#else - unsigned group = 0; - bitmap_t g = slab_data->bitmap[group]; - unsigned i = 0; - while (i < cnt) { - while (g == 0) { - g = slab_data->bitmap[++group]; - } - size_t shift = group << LG_BITMAP_GROUP_NBITS; - size_t pop = popcount_lu(g); - if (pop > (cnt - i)) { - pop = cnt - i; - } - - /* - * Load from memory locations only once, outside the - * hot loop below. - */ - uintptr_t base = (uintptr_t)edata_addr_get(slab); - uintptr_t regsize = (uintptr_t)bin_info->reg_size; - while (pop--) { - size_t bit = cfs_lu(&g); - size_t regind = shift + bit; - *(ptrs + i) = (void *)(base + regsize * regind); - - i++; - } - slab_data->bitmap[group] = g; - } -#endif - edata_nfree_sub(slab, cnt); -} - -static void -arena_large_malloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) { - szind_t index, hindex; - - cassert(config_stats); - - if (usize < SC_LARGE_MINCLASS) { - usize = SC_LARGE_MINCLASS; - } - index = sz_size2index(usize); - hindex = (index >= SC_NBINS) ? index - SC_NBINS : 0; - - locked_inc_u64(tsdn, LOCKEDINT_MTX(arena->stats.mtx), - &arena->stats.lstats[hindex].nmalloc, 1); -} - -static void -arena_large_dalloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) { - szind_t index, hindex; - - cassert(config_stats); - - if (usize < SC_LARGE_MINCLASS) { - usize = SC_LARGE_MINCLASS; - } - index = sz_size2index(usize); - hindex = (index >= SC_NBINS) ? index - SC_NBINS : 0; - - locked_inc_u64(tsdn, LOCKEDINT_MTX(arena->stats.mtx), - &arena->stats.lstats[hindex].ndalloc, 1); -} - -static void -arena_large_ralloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t oldusize, - size_t usize) { - arena_large_malloc_stats_update(tsdn, arena, usize); - arena_large_dalloc_stats_update(tsdn, arena, oldusize); -} - -edata_t * -arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena, size_t usize, - size_t alignment, bool zero) { - bool deferred_work_generated = false; - szind_t szind = sz_size2index(usize); - size_t esize = usize + sz_large_pad; - - bool guarded = san_large_extent_decide_guard(tsdn, - arena_get_ehooks(arena), esize, alignment); - edata_t *edata = pa_alloc(tsdn, &arena->pa_shard, esize, alignment, - /* slab */ false, szind, zero, guarded, &deferred_work_generated); - assert(deferred_work_generated == false); - - if (edata != NULL) { - if (config_stats) { - LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx); - arena_large_malloc_stats_update(tsdn, arena, usize); - LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx); - } - } - - if (edata != NULL && sz_large_pad != 0) { - arena_cache_oblivious_randomize(tsdn, arena, edata, alignment); - } - - return edata; -} - -void -arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena, edata_t *edata) { - if (config_stats) { - LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx); - arena_large_dalloc_stats_update(tsdn, arena, - edata_usize_get(edata)); - LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx); - } -} - -void -arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena, edata_t *edata, - size_t oldusize) { - size_t usize = edata_usize_get(edata); - - if (config_stats) { - LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx); - arena_large_ralloc_stats_update(tsdn, arena, oldusize, usize); - LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx); - } -} - -void -arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena, edata_t *edata, - size_t oldusize) { - size_t usize = edata_usize_get(edata); - - if (config_stats) { - LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx); - arena_large_ralloc_stats_update(tsdn, arena, oldusize, usize); - LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx); - } -} - -/* - * In situations where we're not forcing a decay (i.e. because the user - * specifically requested it), should we purge ourselves, or wait for the - * background thread to get to it. - */ -static pac_purge_eagerness_t -arena_decide_unforced_purge_eagerness(bool is_background_thread) { - if (is_background_thread) { - return PAC_PURGE_ALWAYS; - } else if (!is_background_thread && background_thread_enabled()) { - return PAC_PURGE_NEVER; - } else { - return PAC_PURGE_ON_EPOCH_ADVANCE; - } -} - -bool -arena_decay_ms_set(tsdn_t *tsdn, arena_t *arena, extent_state_t state, - ssize_t decay_ms) { - pac_purge_eagerness_t eagerness = arena_decide_unforced_purge_eagerness( - /* is_background_thread */ false); - return pa_decay_ms_set(tsdn, &arena->pa_shard, state, decay_ms, - eagerness); -} - -ssize_t -arena_decay_ms_get(arena_t *arena, extent_state_t state) { - return pa_decay_ms_get(&arena->pa_shard, state); -} - -static bool -arena_decay_impl(tsdn_t *tsdn, arena_t *arena, decay_t *decay, - pac_decay_stats_t *decay_stats, ecache_t *ecache, - bool is_background_thread, bool all) { - if (all) { - malloc_mutex_lock(tsdn, &decay->mtx); - pac_decay_all(tsdn, &arena->pa_shard.pac, decay, decay_stats, - ecache, /* fully_decay */ all); - malloc_mutex_unlock(tsdn, &decay->mtx); - return false; - } - - if (malloc_mutex_trylock(tsdn, &decay->mtx)) { - /* No need to wait if another thread is in progress. */ - return true; - } - pac_purge_eagerness_t eagerness = - arena_decide_unforced_purge_eagerness(is_background_thread); - bool epoch_advanced = pac_maybe_decay_purge(tsdn, &arena->pa_shard.pac, - decay, decay_stats, ecache, eagerness); - size_t npages_new; - if (epoch_advanced) { - /* Backlog is updated on epoch advance. */ - npages_new = decay_epoch_npages_delta(decay); - } - malloc_mutex_unlock(tsdn, &decay->mtx); - - if (have_background_thread && background_thread_enabled() && - epoch_advanced && !is_background_thread) { - arena_maybe_do_deferred_work(tsdn, arena, decay, npages_new); - } - - return false; -} - -static bool -arena_decay_dirty(tsdn_t *tsdn, arena_t *arena, bool is_background_thread, - bool all) { - return arena_decay_impl(tsdn, arena, &arena->pa_shard.pac.decay_dirty, - &arena->pa_shard.pac.stats->decay_dirty, - &arena->pa_shard.pac.ecache_dirty, is_background_thread, all); -} - -static bool -arena_decay_muzzy(tsdn_t *tsdn, arena_t *arena, bool is_background_thread, - bool all) { - if (pa_shard_dont_decay_muzzy(&arena->pa_shard)) { - return false; - } - return arena_decay_impl(tsdn, arena, &arena->pa_shard.pac.decay_muzzy, - &arena->pa_shard.pac.stats->decay_muzzy, - &arena->pa_shard.pac.ecache_muzzy, is_background_thread, all); -} - -void -arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread, bool all) { - if (all) { - /* - * We should take a purge of "all" to mean "save as much memory - * as possible", including flushing any caches (for situations - * like thread death, or manual purge calls). - */ - sec_flush(tsdn, &arena->pa_shard.hpa_sec); - } - if (arena_decay_dirty(tsdn, arena, is_background_thread, all)) { - return; - } - arena_decay_muzzy(tsdn, arena, is_background_thread, all); -} - -static bool -arena_should_decay_early(tsdn_t *tsdn, arena_t *arena, decay_t *decay, - background_thread_info_t *info, nstime_t *remaining_sleep, - size_t npages_new) { - malloc_mutex_assert_owner(tsdn, &info->mtx); - - if (malloc_mutex_trylock(tsdn, &decay->mtx)) { - return false; - } - - if (!decay_gradually(decay)) { - malloc_mutex_unlock(tsdn, &decay->mtx); - return false; - } - - nstime_init(remaining_sleep, background_thread_wakeup_time_get(info)); - if (nstime_compare(remaining_sleep, &decay->epoch) <= 0) { - malloc_mutex_unlock(tsdn, &decay->mtx); - return false; - } - nstime_subtract(remaining_sleep, &decay->epoch); - if (npages_new > 0) { - uint64_t npurge_new = decay_npages_purge_in(decay, - remaining_sleep, npages_new); - info->npages_to_purge_new += npurge_new; - } - malloc_mutex_unlock(tsdn, &decay->mtx); - return info->npages_to_purge_new > - ARENA_DEFERRED_PURGE_NPAGES_THRESHOLD; -} - -/* - * Check if deferred work needs to be done sooner than planned. - * For decay we might want to wake up earlier because of an influx of dirty - * pages. Rather than waiting for previously estimated time, we proactively - * purge those pages. - * If background thread sleeps indefinitely, always wake up because some - * deferred work has been generated. - */ -static void -arena_maybe_do_deferred_work(tsdn_t *tsdn, arena_t *arena, decay_t *decay, - size_t npages_new) { - background_thread_info_t *info = arena_background_thread_info_get( - arena); - if (malloc_mutex_trylock(tsdn, &info->mtx)) { - /* - * Background thread may hold the mutex for a long period of - * time. We'd like to avoid the variance on application - * threads. So keep this non-blocking, and leave the work to a - * future epoch. - */ - return; - } - if (!background_thread_is_started(info)) { - goto label_done; - } - - nstime_t remaining_sleep; - if (background_thread_indefinite_sleep(info)) { - background_thread_wakeup_early(info, NULL); - } else if (arena_should_decay_early(tsdn, arena, decay, info, - &remaining_sleep, npages_new)) { - info->npages_to_purge_new = 0; - background_thread_wakeup_early(info, &remaining_sleep); - } -label_done: - malloc_mutex_unlock(tsdn, &info->mtx); -} - -/* Called from background threads. */ -void -arena_do_deferred_work(tsdn_t *tsdn, arena_t *arena) { - arena_decay(tsdn, arena, true, false); - pa_shard_do_deferred_work(tsdn, &arena->pa_shard); -} - -void -arena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, edata_t *slab) { - bool deferred_work_generated = false; - pa_dalloc(tsdn, &arena->pa_shard, slab, &deferred_work_generated); - if (deferred_work_generated) { - arena_handle_deferred_work(tsdn, arena); - } -} - -static void -arena_bin_slabs_nonfull_insert(bin_t *bin, edata_t *slab) { - assert(edata_nfree_get(slab) > 0); - edata_heap_insert(&bin->slabs_nonfull, slab); - if (config_stats) { - bin->stats.nonfull_slabs++; - } -} - -static void -arena_bin_slabs_nonfull_remove(bin_t *bin, edata_t *slab) { - edata_heap_remove(&bin->slabs_nonfull, slab); - if (config_stats) { - bin->stats.nonfull_slabs--; - } -} - -static edata_t * -arena_bin_slabs_nonfull_tryget(bin_t *bin) { - edata_t *slab = edata_heap_remove_first(&bin->slabs_nonfull); - if (slab == NULL) { - return NULL; - } - if (config_stats) { - bin->stats.reslabs++; - bin->stats.nonfull_slabs--; - } - return slab; -} - -static void -arena_bin_slabs_full_insert(arena_t *arena, bin_t *bin, edata_t *slab) { - assert(edata_nfree_get(slab) == 0); - /* - * Tracking extents is required by arena_reset, which is not allowed - * for auto arenas. Bypass this step to avoid touching the edata - * linkage (often results in cache misses) for auto arenas. - */ - if (arena_is_auto(arena)) { - return; - } - edata_list_active_append(&bin->slabs_full, slab); -} - -static void -arena_bin_slabs_full_remove(arena_t *arena, bin_t *bin, edata_t *slab) { - if (arena_is_auto(arena)) { - return; - } - edata_list_active_remove(&bin->slabs_full, slab); -} - -static void -arena_bin_reset(tsd_t *tsd, arena_t *arena, bin_t *bin) { - edata_t *slab; - - malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); - if (bin->slabcur != NULL) { - slab = bin->slabcur; - bin->slabcur = NULL; - malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); - arena_slab_dalloc(tsd_tsdn(tsd), arena, slab); - malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); - } - while ((slab = edata_heap_remove_first(&bin->slabs_nonfull)) != NULL) { - malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); - arena_slab_dalloc(tsd_tsdn(tsd), arena, slab); - malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); - } - for (slab = edata_list_active_first(&bin->slabs_full); slab != NULL; - slab = edata_list_active_first(&bin->slabs_full)) { - arena_bin_slabs_full_remove(arena, bin, slab); - malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); - arena_slab_dalloc(tsd_tsdn(tsd), arena, slab); - malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); - } - if (config_stats) { - bin->stats.curregs = 0; - bin->stats.curslabs = 0; - } - malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); -} - -void -arena_reset(tsd_t *tsd, arena_t *arena) { - /* - * Locking in this function is unintuitive. The caller guarantees that - * no concurrent operations are happening in this arena, but there are - * still reasons that some locking is necessary: - * - * - Some of the functions in the transitive closure of calls assume - * appropriate locks are held, and in some cases these locks are - * temporarily dropped to avoid lock order reversal or deadlock due to - * reentry. - * - mallctl("epoch", ...) may concurrently refresh stats. While - * strictly speaking this is a "concurrent operation", disallowing - * stats refreshes would impose an inconvenient burden. - */ - - /* Large allocations. */ - malloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx); - - for (edata_t *edata = edata_list_active_first(&arena->large); - edata != NULL; edata = edata_list_active_first(&arena->large)) { - void *ptr = edata_base_get(edata); - size_t usize; - - malloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx); - emap_alloc_ctx_t alloc_ctx; - emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr, - &alloc_ctx); - assert(alloc_ctx.szind != SC_NSIZES); - - if (config_stats || (config_prof && opt_prof)) { - usize = sz_index2size(alloc_ctx.szind); - assert(usize == isalloc(tsd_tsdn(tsd), ptr)); - } - /* Remove large allocation from prof sample set. */ - if (config_prof && opt_prof) { - prof_free(tsd, ptr, usize, &alloc_ctx); - } - large_dalloc(tsd_tsdn(tsd), edata); - malloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx); - } - malloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx); - - /* Bins. */ - for (unsigned i = 0; i < SC_NBINS; i++) { - for (unsigned j = 0; j < bin_infos[i].n_shards; j++) { - arena_bin_reset(tsd, arena, arena_get_bin(arena, i, j)); - } - } - pa_shard_reset(tsd_tsdn(tsd), &arena->pa_shard); -} - -static void -arena_prepare_base_deletion_sync_finish(tsd_t *tsd, malloc_mutex_t **mutexes, - unsigned n_mtx) { - for (unsigned i = 0; i < n_mtx; i++) { - malloc_mutex_lock(tsd_tsdn(tsd), mutexes[i]); - malloc_mutex_unlock(tsd_tsdn(tsd), mutexes[i]); - } -} - -#define ARENA_DESTROY_MAX_DELAYED_MTX 32 -static void -arena_prepare_base_deletion_sync(tsd_t *tsd, malloc_mutex_t *mtx, - malloc_mutex_t **delayed_mtx, unsigned *n_delayed) { - if (!malloc_mutex_trylock(tsd_tsdn(tsd), mtx)) { - /* No contention. */ - malloc_mutex_unlock(tsd_tsdn(tsd), mtx); - return; - } - unsigned n = *n_delayed; - assert(n < ARENA_DESTROY_MAX_DELAYED_MTX); - /* Add another to the batch. */ - delayed_mtx[n++] = mtx; - - if (n == ARENA_DESTROY_MAX_DELAYED_MTX) { - arena_prepare_base_deletion_sync_finish(tsd, delayed_mtx, n); - n = 0; - } - *n_delayed = n; -} - -static void -arena_prepare_base_deletion(tsd_t *tsd, base_t *base_to_destroy) { - /* - * In order to coalesce, emap_try_acquire_edata_neighbor will attempt to - * check neighbor edata's state to determine eligibility. This means - * under certain conditions, the metadata from an arena can be accessed - * w/o holding any locks from that arena. In order to guarantee safe - * memory access, the metadata and the underlying base allocator needs - * to be kept alive, until all pending accesses are done. - * - * 1) with opt_retain, the arena boundary implies the is_head state - * (tracked in the rtree leaf), and the coalesce flow will stop at the - * head state branch. Therefore no cross arena metadata access - * possible. - * - * 2) w/o opt_retain, the arena id needs to be read from the edata_t, - * meaning read only cross-arena metadata access is possible. The - * coalesce attempt will stop at the arena_id mismatch, and is always - * under one of the ecache locks. To allow safe passthrough of such - * metadata accesses, the loop below will iterate through all manual - * arenas' ecache locks. As all the metadata from this base allocator - * have been unlinked from the rtree, after going through all the - * relevant ecache locks, it's safe to say that a) pending accesses are - * all finished, and b) no new access will be generated. - */ - if (opt_retain) { - return; - } - unsigned destroy_ind = base_ind_get(base_to_destroy); - assert(destroy_ind >= manual_arena_base); - - tsdn_t *tsdn = tsd_tsdn(tsd); - malloc_mutex_t *delayed_mtx[ARENA_DESTROY_MAX_DELAYED_MTX]; - unsigned n_delayed = 0, total = narenas_total_get(); - for (unsigned i = 0; i < total; i++) { - if (i == destroy_ind) { - continue; - } - arena_t *arena = arena_get(tsdn, i, false); - if (arena == NULL) { - continue; - } - pac_t *pac = &arena->pa_shard.pac; - arena_prepare_base_deletion_sync(tsd, &pac->ecache_dirty.mtx, - delayed_mtx, &n_delayed); - arena_prepare_base_deletion_sync(tsd, &pac->ecache_muzzy.mtx, - delayed_mtx, &n_delayed); - arena_prepare_base_deletion_sync(tsd, &pac->ecache_retained.mtx, - delayed_mtx, &n_delayed); - } - arena_prepare_base_deletion_sync_finish(tsd, delayed_mtx, n_delayed); -} -#undef ARENA_DESTROY_MAX_DELAYED_MTX - -void -arena_destroy(tsd_t *tsd, arena_t *arena) { - assert(base_ind_get(arena->base) >= narenas_auto); - assert(arena_nthreads_get(arena, false) == 0); - assert(arena_nthreads_get(arena, true) == 0); - - /* - * No allocations have occurred since arena_reset() was called. - * Furthermore, the caller (arena_i_destroy_ctl()) purged all cached - * extents, so only retained extents may remain and it's safe to call - * pa_shard_destroy_retained. - */ - pa_shard_destroy(tsd_tsdn(tsd), &arena->pa_shard); - - /* - * Remove the arena pointer from the arenas array. We rely on the fact - * that there is no way for the application to get a dirty read from the - * arenas array unless there is an inherent race in the application - * involving access of an arena being concurrently destroyed. The - * application must synchronize knowledge of the arena's validity, so as - * long as we use an atomic write to update the arenas array, the - * application will get a clean read any time after it synchronizes - * knowledge that the arena is no longer valid. - */ - arena_set(base_ind_get(arena->base), NULL); - - /* - * Destroy the base allocator, which manages all metadata ever mapped by - * this arena. The prepare function will make sure no pending access to - * the metadata in this base anymore. - */ - arena_prepare_base_deletion(tsd, arena->base); - base_delete(tsd_tsdn(tsd), arena->base); -} - -static edata_t * -arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind, unsigned binshard, - const bin_info_t *bin_info) { - bool deferred_work_generated = false; - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - bool guarded = san_slab_extent_decide_guard(tsdn, - arena_get_ehooks(arena)); - edata_t *slab = pa_alloc(tsdn, &arena->pa_shard, bin_info->slab_size, - /* alignment */ PAGE, /* slab */ true, /* szind */ binind, - /* zero */ false, guarded, &deferred_work_generated); - - if (deferred_work_generated) { - arena_handle_deferred_work(tsdn, arena); - } - - if (slab == NULL) { - return NULL; - } - assert(edata_slab_get(slab)); - - /* Initialize slab internals. */ - slab_data_t *slab_data = edata_slab_data_get(slab); - edata_nfree_binshard_set(slab, bin_info->nregs, binshard); - bitmap_init(slab_data->bitmap, &bin_info->bitmap_info, false); - - return slab; -} - -/* - * Before attempting the _with_fresh_slab approaches below, the _no_fresh_slab - * variants (i.e. through slabcur and nonfull) must be tried first. - */ -static void -arena_bin_refill_slabcur_with_fresh_slab(tsdn_t *tsdn, arena_t *arena, - bin_t *bin, szind_t binind, edata_t *fresh_slab) { - malloc_mutex_assert_owner(tsdn, &bin->lock); - /* Only called after slabcur and nonfull both failed. */ - assert(bin->slabcur == NULL); - assert(edata_heap_first(&bin->slabs_nonfull) == NULL); - assert(fresh_slab != NULL); - - /* A new slab from arena_slab_alloc() */ - assert(edata_nfree_get(fresh_slab) == bin_infos[binind].nregs); - if (config_stats) { - bin->stats.nslabs++; - bin->stats.curslabs++; - } - bin->slabcur = fresh_slab; -} - -/* Refill slabcur and then alloc using the fresh slab */ -static void * -arena_bin_malloc_with_fresh_slab(tsdn_t *tsdn, arena_t *arena, bin_t *bin, - szind_t binind, edata_t *fresh_slab) { - malloc_mutex_assert_owner(tsdn, &bin->lock); - arena_bin_refill_slabcur_with_fresh_slab(tsdn, arena, bin, binind, - fresh_slab); - - return arena_slab_reg_alloc(bin->slabcur, &bin_infos[binind]); -} - -static bool -arena_bin_refill_slabcur_no_fresh_slab(tsdn_t *tsdn, arena_t *arena, - bin_t *bin) { - malloc_mutex_assert_owner(tsdn, &bin->lock); - /* Only called after arena_slab_reg_alloc[_batch] failed. */ - assert(bin->slabcur == NULL || edata_nfree_get(bin->slabcur) == 0); - - if (bin->slabcur != NULL) { - arena_bin_slabs_full_insert(arena, bin, bin->slabcur); - } - - /* Look for a usable slab. */ - bin->slabcur = arena_bin_slabs_nonfull_tryget(bin); - assert(bin->slabcur == NULL || edata_nfree_get(bin->slabcur) > 0); - - return (bin->slabcur == NULL); -} - -bin_t * -arena_bin_choose(tsdn_t *tsdn, arena_t *arena, szind_t binind, - unsigned *binshard_p) { - unsigned binshard; - if (tsdn_null(tsdn) || tsd_arena_get(tsdn_tsd(tsdn)) == NULL) { - binshard = 0; - } else { - binshard = tsd_binshardsp_get(tsdn_tsd(tsdn))->binshard[binind]; - } - assert(binshard < bin_infos[binind].n_shards); - if (binshard_p != NULL) { - *binshard_p = binshard; - } - return arena_get_bin(arena, binind, binshard); -} - -void -arena_cache_bin_fill_small(tsdn_t *tsdn, arena_t *arena, - cache_bin_t *cache_bin, cache_bin_info_t *cache_bin_info, szind_t binind, - const unsigned nfill) { - assert(cache_bin_ncached_get_local(cache_bin, cache_bin_info) == 0); - - const bin_info_t *bin_info = &bin_infos[binind]; - - CACHE_BIN_PTR_ARRAY_DECLARE(ptrs, nfill); - cache_bin_init_ptr_array_for_fill(cache_bin, cache_bin_info, &ptrs, - nfill); - /* - * Bin-local resources are used first: 1) bin->slabcur, and 2) nonfull - * slabs. After both are exhausted, new slabs will be allocated through - * arena_slab_alloc(). - * - * Bin lock is only taken / released right before / after the while(...) - * refill loop, with new slab allocation (which has its own locking) - * kept outside of the loop. This setup facilitates flat combining, at - * the cost of the nested loop (through goto label_refill). - * - * To optimize for cases with contention and limited resources - * (e.g. hugepage-backed or non-overcommit arenas), each fill-iteration - * gets one chance of slab_alloc, and a retry of bin local resources - * after the slab allocation (regardless if slab_alloc failed, because - * the bin lock is dropped during the slab allocation). - * - * In other words, new slab allocation is allowed, as long as there was - * progress since the previous slab_alloc. This is tracked with - * made_progress below, initialized to true to jump start the first - * iteration. - * - * In other words (again), the loop will only terminate early (i.e. stop - * with filled < nfill) after going through the three steps: a) bin - * local exhausted, b) unlock and slab_alloc returns null, c) re-lock - * and bin local fails again. - */ - bool made_progress = true; - edata_t *fresh_slab = NULL; - bool alloc_and_retry = false; - unsigned filled = 0; - unsigned binshard; - bin_t *bin = arena_bin_choose(tsdn, arena, binind, &binshard); - -label_refill: - malloc_mutex_lock(tsdn, &bin->lock); - - while (filled < nfill) { - /* Try batch-fill from slabcur first. */ - edata_t *slabcur = bin->slabcur; - if (slabcur != NULL && edata_nfree_get(slabcur) > 0) { - unsigned tofill = nfill - filled; - unsigned nfree = edata_nfree_get(slabcur); - unsigned cnt = tofill < nfree ? tofill : nfree; - - arena_slab_reg_alloc_batch(slabcur, bin_info, cnt, - &ptrs.ptr[filled]); - made_progress = true; - filled += cnt; - continue; - } - /* Next try refilling slabcur from nonfull slabs. */ - if (!arena_bin_refill_slabcur_no_fresh_slab(tsdn, arena, bin)) { - assert(bin->slabcur != NULL); - continue; - } - - /* Then see if a new slab was reserved already. */ - if (fresh_slab != NULL) { - arena_bin_refill_slabcur_with_fresh_slab(tsdn, arena, - bin, binind, fresh_slab); - assert(bin->slabcur != NULL); - fresh_slab = NULL; - continue; - } - - /* Try slab_alloc if made progress (or never did slab_alloc). */ - if (made_progress) { - assert(bin->slabcur == NULL); - assert(fresh_slab == NULL); - alloc_and_retry = true; - /* Alloc a new slab then come back. */ - break; - } - - /* OOM. */ - - assert(fresh_slab == NULL); - assert(!alloc_and_retry); - break; - } /* while (filled < nfill) loop. */ - - if (config_stats && !alloc_and_retry) { - bin->stats.nmalloc += filled; - bin->stats.nrequests += cache_bin->tstats.nrequests; - bin->stats.curregs += filled; - bin->stats.nfills++; - cache_bin->tstats.nrequests = 0; - } - - malloc_mutex_unlock(tsdn, &bin->lock); - - if (alloc_and_retry) { - assert(fresh_slab == NULL); - assert(filled < nfill); - assert(made_progress); - - fresh_slab = arena_slab_alloc(tsdn, arena, binind, binshard, - bin_info); - /* fresh_slab NULL case handled in the for loop. */ - - alloc_and_retry = false; - made_progress = false; - goto label_refill; - } - assert(filled == nfill || (fresh_slab == NULL && !made_progress)); - - /* Release if allocated but not used. */ - if (fresh_slab != NULL) { - assert(edata_nfree_get(fresh_slab) == bin_info->nregs); - arena_slab_dalloc(tsdn, arena, fresh_slab); - fresh_slab = NULL; - } - - cache_bin_finish_fill(cache_bin, cache_bin_info, &ptrs, filled); - arena_decay_tick(tsdn, arena); -} - -size_t -arena_fill_small_fresh(tsdn_t *tsdn, arena_t *arena, szind_t binind, - void **ptrs, size_t nfill, bool zero) { - assert(binind < SC_NBINS); - const bin_info_t *bin_info = &bin_infos[binind]; - const size_t nregs = bin_info->nregs; - assert(nregs > 0); - const size_t usize = bin_info->reg_size; - - const bool manual_arena = !arena_is_auto(arena); - unsigned binshard; - bin_t *bin = arena_bin_choose(tsdn, arena, binind, &binshard); - - size_t nslab = 0; - size_t filled = 0; - edata_t *slab = NULL; - edata_list_active_t fulls; - edata_list_active_init(&fulls); - - while (filled < nfill && (slab = arena_slab_alloc(tsdn, arena, binind, - binshard, bin_info)) != NULL) { - assert((size_t)edata_nfree_get(slab) == nregs); - ++nslab; - size_t batch = nfill - filled; - if (batch > nregs) { - batch = nregs; - } - assert(batch > 0); - arena_slab_reg_alloc_batch(slab, bin_info, (unsigned)batch, - &ptrs[filled]); - assert(edata_addr_get(slab) == ptrs[filled]); - if (zero) { - memset(ptrs[filled], 0, batch * usize); - } - filled += batch; - if (batch == nregs) { - if (manual_arena) { - edata_list_active_append(&fulls, slab); - } - slab = NULL; - } - } - - malloc_mutex_lock(tsdn, &bin->lock); - /* - * Only the last slab can be non-empty, and the last slab is non-empty - * iff slab != NULL. - */ - if (slab != NULL) { - arena_bin_lower_slab(tsdn, arena, slab, bin); - } - if (manual_arena) { - edata_list_active_concat(&bin->slabs_full, &fulls); - } - assert(edata_list_active_empty(&fulls)); - if (config_stats) { - bin->stats.nslabs += nslab; - bin->stats.curslabs += nslab; - bin->stats.nmalloc += filled; - bin->stats.nrequests += filled; - bin->stats.curregs += filled; - } - malloc_mutex_unlock(tsdn, &bin->lock); - - arena_decay_tick(tsdn, arena); - return filled; -} - -/* - * Without allocating a new slab, try arena_slab_reg_alloc() and re-fill - * bin->slabcur if necessary. - */ -static void * -arena_bin_malloc_no_fresh_slab(tsdn_t *tsdn, arena_t *arena, bin_t *bin, - szind_t binind) { - malloc_mutex_assert_owner(tsdn, &bin->lock); - if (bin->slabcur == NULL || edata_nfree_get(bin->slabcur) == 0) { - if (arena_bin_refill_slabcur_no_fresh_slab(tsdn, arena, bin)) { - return NULL; - } - } - - assert(bin->slabcur != NULL && edata_nfree_get(bin->slabcur) > 0); - return arena_slab_reg_alloc(bin->slabcur, &bin_infos[binind]); -} - -static void * -arena_malloc_small(tsdn_t *tsdn, arena_t *arena, szind_t binind, bool zero) { - assert(binind < SC_NBINS); - const bin_info_t *bin_info = &bin_infos[binind]; - size_t usize = sz_index2size(binind); - unsigned binshard; - bin_t *bin = arena_bin_choose(tsdn, arena, binind, &binshard); - - malloc_mutex_lock(tsdn, &bin->lock); - edata_t *fresh_slab = NULL; - void *ret = arena_bin_malloc_no_fresh_slab(tsdn, arena, bin, binind); - if (ret == NULL) { - malloc_mutex_unlock(tsdn, &bin->lock); - /******************************/ - fresh_slab = arena_slab_alloc(tsdn, arena, binind, binshard, - bin_info); - /********************************/ - malloc_mutex_lock(tsdn, &bin->lock); - /* Retry since the lock was dropped. */ - ret = arena_bin_malloc_no_fresh_slab(tsdn, arena, bin, binind); - if (ret == NULL) { - if (fresh_slab == NULL) { - /* OOM */ - malloc_mutex_unlock(tsdn, &bin->lock); - return NULL; - } - ret = arena_bin_malloc_with_fresh_slab(tsdn, arena, bin, - binind, fresh_slab); - fresh_slab = NULL; - } - } - if (config_stats) { - bin->stats.nmalloc++; - bin->stats.nrequests++; - bin->stats.curregs++; - } - malloc_mutex_unlock(tsdn, &bin->lock); - - if (fresh_slab != NULL) { - arena_slab_dalloc(tsdn, arena, fresh_slab); - } - if (zero) { - memset(ret, 0, usize); - } - arena_decay_tick(tsdn, arena); - - return ret; -} - -void * -arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, - bool zero) { - assert(!tsdn_null(tsdn) || arena != NULL); - - if (likely(!tsdn_null(tsdn))) { - arena = arena_choose_maybe_huge(tsdn_tsd(tsdn), arena, size); - } - if (unlikely(arena == NULL)) { - return NULL; - } - - if (likely(size <= SC_SMALL_MAXCLASS)) { - return arena_malloc_small(tsdn, arena, ind, zero); - } - return large_malloc(tsdn, arena, sz_index2size(ind), zero); -} - -void * -arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment, - bool zero, tcache_t *tcache) { - void *ret; - - if (usize <= SC_SMALL_MAXCLASS) { - /* Small; alignment doesn't require special slab placement. */ - - /* usize should be a result of sz_sa2u() */ - assert((usize & (alignment - 1)) == 0); - - /* - * Small usize can't come from an alignment larger than a page. - */ - assert(alignment <= PAGE); - - ret = arena_malloc(tsdn, arena, usize, sz_size2index(usize), - zero, tcache, true); - } else { - if (likely(alignment <= CACHELINE)) { - ret = large_malloc(tsdn, arena, usize, zero); - } else { - ret = large_palloc(tsdn, arena, usize, alignment, zero); - } - } - return ret; -} - -void -arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize) { - cassert(config_prof); - assert(ptr != NULL); - assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS); - assert(usize <= SC_SMALL_MAXCLASS); - - if (config_opt_safety_checks) { - safety_check_set_redzone(ptr, usize, SC_LARGE_MINCLASS); - } - - edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - - szind_t szind = sz_size2index(usize); - edata_szind_set(edata, szind); - emap_remap(tsdn, &arena_emap_global, edata, szind, /* slab */ false); - - assert(isalloc(tsdn, ptr) == usize); -} - -static size_t -arena_prof_demote(tsdn_t *tsdn, edata_t *edata, const void *ptr) { - cassert(config_prof); - assert(ptr != NULL); - - edata_szind_set(edata, SC_NBINS); - emap_remap(tsdn, &arena_emap_global, edata, SC_NBINS, /* slab */ false); - - assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS); - - return SC_LARGE_MINCLASS; -} - -void -arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache, - bool slow_path) { - cassert(config_prof); - assert(opt_prof); - - edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - size_t usize = edata_usize_get(edata); - size_t bumped_usize = arena_prof_demote(tsdn, edata, ptr); - if (config_opt_safety_checks && usize < SC_LARGE_MINCLASS) { - /* - * Currently, we only do redzoning for small sampled - * allocations. - */ - assert(bumped_usize == SC_LARGE_MINCLASS); - safety_check_verify_redzone(ptr, usize, bumped_usize); - } - if (bumped_usize <= tcache_maxclass && tcache != NULL) { - tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr, - sz_size2index(bumped_usize), slow_path); - } else { - large_dalloc(tsdn, edata); - } -} - -static void -arena_dissociate_bin_slab(arena_t *arena, edata_t *slab, bin_t *bin) { - /* Dissociate slab from bin. */ - if (slab == bin->slabcur) { - bin->slabcur = NULL; - } else { - szind_t binind = edata_szind_get(slab); - const bin_info_t *bin_info = &bin_infos[binind]; - - /* - * The following block's conditional is necessary because if the - * slab only contains one region, then it never gets inserted - * into the non-full slabs heap. - */ - if (bin_info->nregs == 1) { - arena_bin_slabs_full_remove(arena, bin, slab); - } else { - arena_bin_slabs_nonfull_remove(bin, slab); - } - } -} - -static void -arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, edata_t *slab, - bin_t *bin) { - assert(edata_nfree_get(slab) > 0); - - /* - * Make sure that if bin->slabcur is non-NULL, it refers to the - * oldest/lowest non-full slab. It is okay to NULL slabcur out rather - * than proactively keeping it pointing at the oldest/lowest non-full - * slab. - */ - if (bin->slabcur != NULL && edata_snad_comp(bin->slabcur, slab) > 0) { - /* Switch slabcur. */ - if (edata_nfree_get(bin->slabcur) > 0) { - arena_bin_slabs_nonfull_insert(bin, bin->slabcur); - } else { - arena_bin_slabs_full_insert(arena, bin, bin->slabcur); - } - bin->slabcur = slab; - if (config_stats) { - bin->stats.reslabs++; - } - } else { - arena_bin_slabs_nonfull_insert(bin, slab); - } -} - -static void -arena_dalloc_bin_slab_prepare(tsdn_t *tsdn, edata_t *slab, bin_t *bin) { - malloc_mutex_assert_owner(tsdn, &bin->lock); - - assert(slab != bin->slabcur); - if (config_stats) { - bin->stats.curslabs--; - } -} - -void -arena_dalloc_bin_locked_handle_newly_empty(tsdn_t *tsdn, arena_t *arena, - edata_t *slab, bin_t *bin) { - arena_dissociate_bin_slab(arena, slab, bin); - arena_dalloc_bin_slab_prepare(tsdn, slab, bin); -} - -void -arena_dalloc_bin_locked_handle_newly_nonempty(tsdn_t *tsdn, arena_t *arena, - edata_t *slab, bin_t *bin) { - arena_bin_slabs_full_remove(arena, bin, slab); - arena_bin_lower_slab(tsdn, arena, slab, bin); -} - -static void -arena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, edata_t *edata, void *ptr) { - szind_t binind = edata_szind_get(edata); - unsigned binshard = edata_binshard_get(edata); - bin_t *bin = arena_get_bin(arena, binind, binshard); - - malloc_mutex_lock(tsdn, &bin->lock); - arena_dalloc_bin_locked_info_t info; - arena_dalloc_bin_locked_begin(&info, binind); - bool ret = arena_dalloc_bin_locked_step(tsdn, arena, bin, - &info, binind, edata, ptr); - arena_dalloc_bin_locked_finish(tsdn, arena, bin, &info); - malloc_mutex_unlock(tsdn, &bin->lock); - - if (ret) { - arena_slab_dalloc(tsdn, arena, edata); - } -} - -void -arena_dalloc_small(tsdn_t *tsdn, void *ptr) { - edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - arena_t *arena = arena_get_from_edata(edata); - - arena_dalloc_bin(tsdn, arena, edata, ptr); - arena_decay_tick(tsdn, arena); -} - -bool -arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, - size_t extra, bool zero, size_t *newsize) { - bool ret; - /* Calls with non-zero extra had to clamp extra. */ - assert(extra == 0 || size + extra <= SC_LARGE_MAXCLASS); - - edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - if (unlikely(size > SC_LARGE_MAXCLASS)) { - ret = true; - goto done; - } - - size_t usize_min = sz_s2u(size); - size_t usize_max = sz_s2u(size + extra); - if (likely(oldsize <= SC_SMALL_MAXCLASS && usize_min - <= SC_SMALL_MAXCLASS)) { - /* - * Avoid moving the allocation if the size class can be left the - * same. - */ - assert(bin_infos[sz_size2index(oldsize)].reg_size == - oldsize); - if ((usize_max > SC_SMALL_MAXCLASS - || sz_size2index(usize_max) != sz_size2index(oldsize)) - && (size > oldsize || usize_max < oldsize)) { - ret = true; - goto done; - } - - arena_t *arena = arena_get_from_edata(edata); - arena_decay_tick(tsdn, arena); - ret = false; - } else if (oldsize >= SC_LARGE_MINCLASS - && usize_max >= SC_LARGE_MINCLASS) { - ret = large_ralloc_no_move(tsdn, edata, usize_min, usize_max, - zero); - } else { - ret = true; - } -done: - assert(edata == emap_edata_lookup(tsdn, &arena_emap_global, ptr)); - *newsize = edata_usize_get(edata); - - return ret; -} - -static void * -arena_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize, - size_t alignment, bool zero, tcache_t *tcache) { - if (alignment == 0) { - return arena_malloc(tsdn, arena, usize, sz_size2index(usize), - zero, tcache, true); - } - usize = sz_sa2u(usize, alignment); - if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) { - return NULL; - } - return ipalloct(tsdn, usize, alignment, zero, tcache, arena); -} - -void * -arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize, - size_t size, size_t alignment, bool zero, tcache_t *tcache, - hook_ralloc_args_t *hook_args) { - size_t usize = alignment == 0 ? sz_s2u(size) : sz_sa2u(size, alignment); - if (unlikely(usize == 0 || size > SC_LARGE_MAXCLASS)) { - return NULL; - } - - if (likely(usize <= SC_SMALL_MAXCLASS)) { - /* Try to avoid moving the allocation. */ - UNUSED size_t newsize; - if (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero, - &newsize)) { - hook_invoke_expand(hook_args->is_realloc - ? hook_expand_realloc : hook_expand_rallocx, - ptr, oldsize, usize, (uintptr_t)ptr, - hook_args->args); - return ptr; - } - } - - if (oldsize >= SC_LARGE_MINCLASS - && usize >= SC_LARGE_MINCLASS) { - return large_ralloc(tsdn, arena, ptr, usize, - alignment, zero, tcache, hook_args); - } - - /* - * size and oldsize are different enough that we need to move the - * object. In that case, fall back to allocating new space and copying. - */ - void *ret = arena_ralloc_move_helper(tsdn, arena, usize, alignment, - zero, tcache); - if (ret == NULL) { - return NULL; - } - - hook_invoke_alloc(hook_args->is_realloc - ? hook_alloc_realloc : hook_alloc_rallocx, ret, (uintptr_t)ret, - hook_args->args); - hook_invoke_dalloc(hook_args->is_realloc - ? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args); - - /* - * Junk/zero-filling were already done by - * ipalloc()/arena_malloc(). - */ - size_t copysize = (usize < oldsize) ? usize : oldsize; - memcpy(ret, ptr, copysize); - isdalloct(tsdn, ptr, oldsize, tcache, NULL, true); - return ret; -} - -ehooks_t * -arena_get_ehooks(arena_t *arena) { - return base_ehooks_get(arena->base); -} - -extent_hooks_t * -arena_set_extent_hooks(tsd_t *tsd, arena_t *arena, - extent_hooks_t *extent_hooks) { - background_thread_info_t *info; - if (have_background_thread) { - info = arena_background_thread_info_get(arena); - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - } - /* No using the HPA now that we have the custom hooks. */ - pa_shard_disable_hpa(tsd_tsdn(tsd), &arena->pa_shard); - extent_hooks_t *ret = base_extent_hooks_set(arena->base, extent_hooks); - if (have_background_thread) { - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - } - - return ret; -} - -dss_prec_t -arena_dss_prec_get(arena_t *arena) { - return (dss_prec_t)atomic_load_u(&arena->dss_prec, ATOMIC_ACQUIRE); -} - -bool -arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) { - if (!have_dss) { - return (dss_prec != dss_prec_disabled); - } - atomic_store_u(&arena->dss_prec, (unsigned)dss_prec, ATOMIC_RELEASE); - return false; -} - -ssize_t -arena_dirty_decay_ms_default_get(void) { - return atomic_load_zd(&dirty_decay_ms_default, ATOMIC_RELAXED); -} - -bool -arena_dirty_decay_ms_default_set(ssize_t decay_ms) { - if (!decay_ms_valid(decay_ms)) { - return true; - } - atomic_store_zd(&dirty_decay_ms_default, decay_ms, ATOMIC_RELAXED); - return false; -} - -ssize_t -arena_muzzy_decay_ms_default_get(void) { - return atomic_load_zd(&muzzy_decay_ms_default, ATOMIC_RELAXED); -} - -bool -arena_muzzy_decay_ms_default_set(ssize_t decay_ms) { - if (!decay_ms_valid(decay_ms)) { - return true; - } - atomic_store_zd(&muzzy_decay_ms_default, decay_ms, ATOMIC_RELAXED); - return false; -} - -bool -arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena, size_t *old_limit, - size_t *new_limit) { - assert(opt_retain); - return pac_retain_grow_limit_get_set(tsd_tsdn(tsd), - &arena->pa_shard.pac, old_limit, new_limit); -} - -unsigned -arena_nthreads_get(arena_t *arena, bool internal) { - return atomic_load_u(&arena->nthreads[internal], ATOMIC_RELAXED); -} - -void -arena_nthreads_inc(arena_t *arena, bool internal) { - atomic_fetch_add_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED); -} - -void -arena_nthreads_dec(arena_t *arena, bool internal) { - atomic_fetch_sub_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED); -} - -arena_t * -arena_new(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) { - arena_t *arena; - base_t *base; - unsigned i; - - if (ind == 0) { - base = b0get(); - } else { - base = base_new(tsdn, ind, config->extent_hooks, - config->metadata_use_hooks); - if (base == NULL) { - return NULL; - } - } - - size_t arena_size = sizeof(arena_t) + sizeof(bin_t) * nbins_total; - arena = (arena_t *)base_alloc(tsdn, base, arena_size, CACHELINE); - if (arena == NULL) { - goto label_error; - } - - atomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED); - atomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED); - arena->last_thd = NULL; - - if (config_stats) { - if (arena_stats_init(tsdn, &arena->stats)) { - goto label_error; - } - - ql_new(&arena->tcache_ql); - ql_new(&arena->cache_bin_array_descriptor_ql); - if (malloc_mutex_init(&arena->tcache_ql_mtx, "tcache_ql", - WITNESS_RANK_TCACHE_QL, malloc_mutex_rank_exclusive)) { - goto label_error; - } - } - - atomic_store_u(&arena->dss_prec, (unsigned)extent_dss_prec_get(), - ATOMIC_RELAXED); - - edata_list_active_init(&arena->large); - if (malloc_mutex_init(&arena->large_mtx, "arena_large", - WITNESS_RANK_ARENA_LARGE, malloc_mutex_rank_exclusive)) { - goto label_error; - } - - nstime_t cur_time; - nstime_init_update(&cur_time); - if (pa_shard_init(tsdn, &arena->pa_shard, &arena_pa_central_global, - &arena_emap_global, base, ind, &arena->stats.pa_shard_stats, - LOCKEDINT_MTX(arena->stats.mtx), &cur_time, oversize_threshold, - arena_dirty_decay_ms_default_get(), - arena_muzzy_decay_ms_default_get())) { - goto label_error; - } - - /* Initialize bins. */ - atomic_store_u(&arena->binshard_next, 0, ATOMIC_RELEASE); - for (i = 0; i < nbins_total; i++) { - bool err = bin_init(&arena->bins[i]); - if (err) { - goto label_error; - } - } - - arena->base = base; - /* Set arena before creating background threads. */ - arena_set(ind, arena); - arena->ind = ind; - - nstime_init_update(&arena->create_time); - - /* - * We turn on the HPA if set to. There are two exceptions: - * - Custom extent hooks (we should only return memory allocated from - * them in that case). - * - Arena 0 initialization. In this case, we're mid-bootstrapping, and - * so arena_hpa_global is not yet initialized. - */ - if (opt_hpa && ehooks_are_default(base_ehooks_get(base)) && ind != 0) { - hpa_shard_opts_t hpa_shard_opts = opt_hpa_opts; - hpa_shard_opts.deferral_allowed = background_thread_enabled(); - if (pa_shard_enable_hpa(tsdn, &arena->pa_shard, - &hpa_shard_opts, &opt_hpa_sec_opts)) { - goto label_error; - } - } - - /* We don't support reentrancy for arena 0 bootstrapping. */ - if (ind != 0) { - /* - * If we're here, then arena 0 already exists, so bootstrapping - * is done enough that we should have tsd. - */ - assert(!tsdn_null(tsdn)); - pre_reentrancy(tsdn_tsd(tsdn), arena); - if (test_hooks_arena_new_hook) { - test_hooks_arena_new_hook(); - } - post_reentrancy(tsdn_tsd(tsdn)); - } - - return arena; -label_error: - if (ind != 0) { - base_delete(tsdn, base); - } - return NULL; -} - -arena_t * -arena_choose_huge(tsd_t *tsd) { - /* huge_arena_ind can be 0 during init (will use a0). */ - if (huge_arena_ind == 0) { - assert(!malloc_initialized()); - } - - arena_t *huge_arena = arena_get(tsd_tsdn(tsd), huge_arena_ind, false); - if (huge_arena == NULL) { - /* Create the huge arena on demand. */ - assert(huge_arena_ind != 0); - huge_arena = arena_get(tsd_tsdn(tsd), huge_arena_ind, true); - if (huge_arena == NULL) { - return NULL; - } - /* - * Purge eagerly for huge allocations, because: 1) number of - * huge allocations is usually small, which means ticker based - * decay is not reliable; and 2) less immediate reuse is - * expected for huge allocations. - */ - if (arena_dirty_decay_ms_default_get() > 0) { - arena_decay_ms_set(tsd_tsdn(tsd), huge_arena, - extent_state_dirty, 0); - } - if (arena_muzzy_decay_ms_default_get() > 0) { - arena_decay_ms_set(tsd_tsdn(tsd), huge_arena, - extent_state_muzzy, 0); - } - } - - return huge_arena; -} - -bool -arena_init_huge(void) { - bool huge_enabled; - - /* The threshold should be large size class. */ - if (opt_oversize_threshold > SC_LARGE_MAXCLASS || - opt_oversize_threshold < SC_LARGE_MINCLASS) { - opt_oversize_threshold = 0; - oversize_threshold = SC_LARGE_MAXCLASS + PAGE; - huge_enabled = false; - } else { - /* Reserve the index for the huge arena. */ - huge_arena_ind = narenas_total_get(); - oversize_threshold = opt_oversize_threshold; - huge_enabled = true; - } - - return huge_enabled; -} - -bool -arena_is_huge(unsigned arena_ind) { - if (huge_arena_ind == 0) { - return false; - } - return (arena_ind == huge_arena_ind); -} - -bool -arena_boot(sc_data_t *sc_data, base_t *base, bool hpa) { - arena_dirty_decay_ms_default_set(opt_dirty_decay_ms); - arena_muzzy_decay_ms_default_set(opt_muzzy_decay_ms); - for (unsigned i = 0; i < SC_NBINS; i++) { - sc_t *sc = &sc_data->sc[i]; - div_init(&arena_binind_div_info[i], - (1U << sc->lg_base) + (sc->ndelta << sc->lg_delta)); - } - - uint32_t cur_offset = (uint32_t)offsetof(arena_t, bins); - for (szind_t i = 0; i < SC_NBINS; i++) { - arena_bin_offsets[i] = cur_offset; - nbins_total += bin_infos[i].n_shards; - cur_offset += (uint32_t)(bin_infos[i].n_shards * sizeof(bin_t)); - } - return pa_central_init(&arena_pa_central_global, base, hpa, - &hpa_hooks_default); -} - -void -arena_prefork0(tsdn_t *tsdn, arena_t *arena) { - pa_shard_prefork0(tsdn, &arena->pa_shard); -} - -void -arena_prefork1(tsdn_t *tsdn, arena_t *arena) { - if (config_stats) { - malloc_mutex_prefork(tsdn, &arena->tcache_ql_mtx); - } -} - -void -arena_prefork2(tsdn_t *tsdn, arena_t *arena) { - pa_shard_prefork2(tsdn, &arena->pa_shard); -} - -void -arena_prefork3(tsdn_t *tsdn, arena_t *arena) { - pa_shard_prefork3(tsdn, &arena->pa_shard); -} - -void -arena_prefork4(tsdn_t *tsdn, arena_t *arena) { - pa_shard_prefork4(tsdn, &arena->pa_shard); -} - -void -arena_prefork5(tsdn_t *tsdn, arena_t *arena) { - pa_shard_prefork5(tsdn, &arena->pa_shard); -} - -void -arena_prefork6(tsdn_t *tsdn, arena_t *arena) { - base_prefork(tsdn, arena->base); -} - -void -arena_prefork7(tsdn_t *tsdn, arena_t *arena) { - malloc_mutex_prefork(tsdn, &arena->large_mtx); -} - -void -arena_prefork8(tsdn_t *tsdn, arena_t *arena) { - for (unsigned i = 0; i < nbins_total; i++) { - bin_prefork(tsdn, &arena->bins[i]); - } -} - -void -arena_postfork_parent(tsdn_t *tsdn, arena_t *arena) { - for (unsigned i = 0; i < nbins_total; i++) { - bin_postfork_parent(tsdn, &arena->bins[i]); - } - - malloc_mutex_postfork_parent(tsdn, &arena->large_mtx); - base_postfork_parent(tsdn, arena->base); - pa_shard_postfork_parent(tsdn, &arena->pa_shard); - if (config_stats) { - malloc_mutex_postfork_parent(tsdn, &arena->tcache_ql_mtx); - } -} - -void -arena_postfork_child(tsdn_t *tsdn, arena_t *arena) { - atomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED); - atomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED); - if (tsd_arena_get(tsdn_tsd(tsdn)) == arena) { - arena_nthreads_inc(arena, false); - } - if (tsd_iarena_get(tsdn_tsd(tsdn)) == arena) { - arena_nthreads_inc(arena, true); - } - if (config_stats) { - ql_new(&arena->tcache_ql); - ql_new(&arena->cache_bin_array_descriptor_ql); - tcache_slow_t *tcache_slow = tcache_slow_get(tsdn_tsd(tsdn)); - if (tcache_slow != NULL && tcache_slow->arena == arena) { - tcache_t *tcache = tcache_slow->tcache; - ql_elm_new(tcache_slow, link); - ql_tail_insert(&arena->tcache_ql, tcache_slow, link); - cache_bin_array_descriptor_init( - &tcache_slow->cache_bin_array_descriptor, - tcache->bins); - ql_tail_insert(&arena->cache_bin_array_descriptor_ql, - &tcache_slow->cache_bin_array_descriptor, link); - } - } - - for (unsigned i = 0; i < nbins_total; i++) { - bin_postfork_child(tsdn, &arena->bins[i]); - } - - malloc_mutex_postfork_child(tsdn, &arena->large_mtx); - base_postfork_child(tsdn, arena->base); - pa_shard_postfork_child(tsdn, &arena->pa_shard); - if (config_stats) { - malloc_mutex_postfork_child(tsdn, &arena->tcache_ql_mtx); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/background_thread.c b/fluent-bit/lib/jemalloc-5.3.0/src/background_thread.c deleted file mode 100644 index 3bb8d26c..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/background_thread.c +++ /dev/null @@ -1,820 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" - -JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS - -/******************************************************************************/ -/* Data. */ - -/* This option should be opt-in only. */ -#define BACKGROUND_THREAD_DEFAULT false -/* Read-only after initialization. */ -bool opt_background_thread = BACKGROUND_THREAD_DEFAULT; -size_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT + 1; - -/* Used for thread creation, termination and stats. */ -malloc_mutex_t background_thread_lock; -/* Indicates global state. Atomic because decay reads this w/o locking. */ -atomic_b_t background_thread_enabled_state; -size_t n_background_threads; -size_t max_background_threads; -/* Thread info per-index. */ -background_thread_info_t *background_thread_info; - -/******************************************************************************/ - -#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER - -static int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *, - void *(*)(void *), void *__restrict); - -static void -pthread_create_wrapper_init(void) { -#ifdef JEMALLOC_LAZY_LOCK - if (!isthreaded) { - isthreaded = true; - } -#endif -} - -int -pthread_create_wrapper(pthread_t *__restrict thread, const pthread_attr_t *attr, - void *(*start_routine)(void *), void *__restrict arg) { - pthread_create_wrapper_init(); - - return pthread_create_fptr(thread, attr, start_routine, arg); -} -#endif /* JEMALLOC_PTHREAD_CREATE_WRAPPER */ - -#ifndef JEMALLOC_BACKGROUND_THREAD -#define NOT_REACHED { not_reached(); } -bool background_thread_create(tsd_t *tsd, unsigned arena_ind) NOT_REACHED -bool background_threads_enable(tsd_t *tsd) NOT_REACHED -bool background_threads_disable(tsd_t *tsd) NOT_REACHED -bool background_thread_is_started(background_thread_info_t *info) NOT_REACHED -void background_thread_wakeup_early(background_thread_info_t *info, - nstime_t *remaining_sleep) NOT_REACHED -void background_thread_prefork0(tsdn_t *tsdn) NOT_REACHED -void background_thread_prefork1(tsdn_t *tsdn) NOT_REACHED -void background_thread_postfork_parent(tsdn_t *tsdn) NOT_REACHED -void background_thread_postfork_child(tsdn_t *tsdn) NOT_REACHED -bool background_thread_stats_read(tsdn_t *tsdn, - background_thread_stats_t *stats) NOT_REACHED -void background_thread_ctl_init(tsdn_t *tsdn) NOT_REACHED -#undef NOT_REACHED -#else - -static bool background_thread_enabled_at_fork; - -static void -background_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) { - background_thread_wakeup_time_set(tsdn, info, 0); - info->npages_to_purge_new = 0; - if (config_stats) { - info->tot_n_runs = 0; - nstime_init_zero(&info->tot_sleep_time); - } -} - -static inline bool -set_current_thread_affinity(int cpu) { -#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY) - cpu_set_t cpuset; -#else -# ifndef __NetBSD__ - cpuset_t cpuset; -# else - cpuset_t *cpuset; -# endif -#endif - -#ifndef __NetBSD__ - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); -#else - cpuset = cpuset_create(); -#endif - -#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY) - return (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) != 0); -#else -# ifndef __NetBSD__ - int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpuset_t), - &cpuset); -# else - int ret = pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), - cpuset); - cpuset_destroy(cpuset); -# endif - return ret != 0; -#endif -} - -#define BILLION UINT64_C(1000000000) -/* Minimal sleep interval 100 ms. */ -#define BACKGROUND_THREAD_MIN_INTERVAL_NS (BILLION / 10) - -static void -background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info, - uint64_t interval) { - if (config_stats) { - info->tot_n_runs++; - } - info->npages_to_purge_new = 0; - - struct timeval tv; - /* Specific clock required by timedwait. */ - gettimeofday(&tv, NULL); - nstime_t before_sleep; - nstime_init2(&before_sleep, tv.tv_sec, tv.tv_usec * 1000); - - int ret; - if (interval == BACKGROUND_THREAD_INDEFINITE_SLEEP) { - background_thread_wakeup_time_set(tsdn, info, - BACKGROUND_THREAD_INDEFINITE_SLEEP); - ret = pthread_cond_wait(&info->cond, &info->mtx.lock); - assert(ret == 0); - } else { - assert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS && - interval <= BACKGROUND_THREAD_INDEFINITE_SLEEP); - /* We need malloc clock (can be different from tv). */ - nstime_t next_wakeup; - nstime_init_update(&next_wakeup); - nstime_iadd(&next_wakeup, interval); - assert(nstime_ns(&next_wakeup) < - BACKGROUND_THREAD_INDEFINITE_SLEEP); - background_thread_wakeup_time_set(tsdn, info, - nstime_ns(&next_wakeup)); - - nstime_t ts_wakeup; - nstime_copy(&ts_wakeup, &before_sleep); - nstime_iadd(&ts_wakeup, interval); - struct timespec ts; - ts.tv_sec = (size_t)nstime_sec(&ts_wakeup); - ts.tv_nsec = (size_t)nstime_nsec(&ts_wakeup); - - assert(!background_thread_indefinite_sleep(info)); - ret = pthread_cond_timedwait(&info->cond, &info->mtx.lock, &ts); - assert(ret == ETIMEDOUT || ret == 0); - } - if (config_stats) { - gettimeofday(&tv, NULL); - nstime_t after_sleep; - nstime_init2(&after_sleep, tv.tv_sec, tv.tv_usec * 1000); - if (nstime_compare(&after_sleep, &before_sleep) > 0) { - nstime_subtract(&after_sleep, &before_sleep); - nstime_add(&info->tot_sleep_time, &after_sleep); - } - } -} - -static bool -background_thread_pause_check(tsdn_t *tsdn, background_thread_info_t *info) { - if (unlikely(info->state == background_thread_paused)) { - malloc_mutex_unlock(tsdn, &info->mtx); - /* Wait on global lock to update status. */ - malloc_mutex_lock(tsdn, &background_thread_lock); - malloc_mutex_unlock(tsdn, &background_thread_lock); - malloc_mutex_lock(tsdn, &info->mtx); - return true; - } - - return false; -} - -static inline void -background_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info, - unsigned ind) { - uint64_t ns_until_deferred = BACKGROUND_THREAD_DEFERRED_MAX; - unsigned narenas = narenas_total_get(); - bool slept_indefinitely = background_thread_indefinite_sleep(info); - - for (unsigned i = ind; i < narenas; i += max_background_threads) { - arena_t *arena = arena_get(tsdn, i, false); - if (!arena) { - continue; - } - /* - * If thread was woken up from the indefinite sleep, don't - * do the work instantly, but rather check when the deferred - * work that caused this thread to wake up is scheduled for. - */ - if (!slept_indefinitely) { - arena_do_deferred_work(tsdn, arena); - } - if (ns_until_deferred <= BACKGROUND_THREAD_MIN_INTERVAL_NS) { - /* Min interval will be used. */ - continue; - } - uint64_t ns_arena_deferred = pa_shard_time_until_deferred_work( - tsdn, &arena->pa_shard); - if (ns_arena_deferred < ns_until_deferred) { - ns_until_deferred = ns_arena_deferred; - } - } - - uint64_t sleep_ns; - if (ns_until_deferred == BACKGROUND_THREAD_DEFERRED_MAX) { - sleep_ns = BACKGROUND_THREAD_INDEFINITE_SLEEP; - } else { - sleep_ns = - (ns_until_deferred < BACKGROUND_THREAD_MIN_INTERVAL_NS) - ? BACKGROUND_THREAD_MIN_INTERVAL_NS - : ns_until_deferred; - - } - - background_thread_sleep(tsdn, info, sleep_ns); -} - -static bool -background_threads_disable_single(tsd_t *tsd, background_thread_info_t *info) { - if (info == &background_thread_info[0]) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), - &background_thread_lock); - } else { - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), - &background_thread_lock); - } - - pre_reentrancy(tsd, NULL); - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - bool has_thread; - assert(info->state != background_thread_paused); - if (info->state == background_thread_started) { - has_thread = true; - info->state = background_thread_stopped; - pthread_cond_signal(&info->cond); - } else { - has_thread = false; - } - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - - if (!has_thread) { - post_reentrancy(tsd); - return false; - } - void *ret; - if (pthread_join(info->thread, &ret)) { - post_reentrancy(tsd); - return true; - } - assert(ret == NULL); - n_background_threads--; - post_reentrancy(tsd); - - return false; -} - -static void *background_thread_entry(void *ind_arg); - -static int -background_thread_create_signals_masked(pthread_t *thread, - const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { - /* - * Mask signals during thread creation so that the thread inherits - * an empty signal set. - */ - sigset_t set; - sigfillset(&set); - sigset_t oldset; - int mask_err = pthread_sigmask(SIG_SETMASK, &set, &oldset); - if (mask_err != 0) { - return mask_err; - } - int create_err = pthread_create_wrapper(thread, attr, start_routine, - arg); - /* - * Restore the signal mask. Failure to restore the signal mask here - * changes program behavior. - */ - int restore_err = pthread_sigmask(SIG_SETMASK, &oldset, NULL); - if (restore_err != 0) { - malloc_printf("<jemalloc>: background thread creation " - "failed (%d), and signal mask restoration failed " - "(%d)\n", create_err, restore_err); - if (opt_abort) { - abort(); - } - } - return create_err; -} - -static bool -check_background_thread_creation(tsd_t *tsd, unsigned *n_created, - bool *created_threads) { - bool ret = false; - if (likely(*n_created == n_background_threads)) { - return ret; - } - - tsdn_t *tsdn = tsd_tsdn(tsd); - malloc_mutex_unlock(tsdn, &background_thread_info[0].mtx); - for (unsigned i = 1; i < max_background_threads; i++) { - if (created_threads[i]) { - continue; - } - background_thread_info_t *info = &background_thread_info[i]; - malloc_mutex_lock(tsdn, &info->mtx); - /* - * In case of the background_thread_paused state because of - * arena reset, delay the creation. - */ - bool create = (info->state == background_thread_started); - malloc_mutex_unlock(tsdn, &info->mtx); - if (!create) { - continue; - } - - pre_reentrancy(tsd, NULL); - int err = background_thread_create_signals_masked(&info->thread, - NULL, background_thread_entry, (void *)(uintptr_t)i); - post_reentrancy(tsd); - - if (err == 0) { - (*n_created)++; - created_threads[i] = true; - } else { - malloc_printf("<jemalloc>: background thread " - "creation failed (%d)\n", err); - if (opt_abort) { - abort(); - } - } - /* Return to restart the loop since we unlocked. */ - ret = true; - break; - } - malloc_mutex_lock(tsdn, &background_thread_info[0].mtx); - - return ret; -} - -static void -background_thread0_work(tsd_t *tsd) { - /* Thread0 is also responsible for launching / terminating threads. */ - VARIABLE_ARRAY(bool, created_threads, max_background_threads); - unsigned i; - for (i = 1; i < max_background_threads; i++) { - created_threads[i] = false; - } - /* Start working, and create more threads when asked. */ - unsigned n_created = 1; - while (background_thread_info[0].state != background_thread_stopped) { - if (background_thread_pause_check(tsd_tsdn(tsd), - &background_thread_info[0])) { - continue; - } - if (check_background_thread_creation(tsd, &n_created, - (bool *)&created_threads)) { - continue; - } - background_work_sleep_once(tsd_tsdn(tsd), - &background_thread_info[0], 0); - } - - /* - * Shut down other threads at exit. Note that the ctl thread is holding - * the global background_thread mutex (and is waiting) for us. - */ - assert(!background_thread_enabled()); - for (i = 1; i < max_background_threads; i++) { - background_thread_info_t *info = &background_thread_info[i]; - assert(info->state != background_thread_paused); - if (created_threads[i]) { - background_threads_disable_single(tsd, info); - } else { - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - if (info->state != background_thread_stopped) { - /* The thread was not created. */ - assert(info->state == - background_thread_started); - n_background_threads--; - info->state = background_thread_stopped; - } - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - } - } - background_thread_info[0].state = background_thread_stopped; - assert(n_background_threads == 1); -} - -static void -background_work(tsd_t *tsd, unsigned ind) { - background_thread_info_t *info = &background_thread_info[ind]; - - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - background_thread_wakeup_time_set(tsd_tsdn(tsd), info, - BACKGROUND_THREAD_INDEFINITE_SLEEP); - if (ind == 0) { - background_thread0_work(tsd); - } else { - while (info->state != background_thread_stopped) { - if (background_thread_pause_check(tsd_tsdn(tsd), - info)) { - continue; - } - background_work_sleep_once(tsd_tsdn(tsd), info, ind); - } - } - assert(info->state == background_thread_stopped); - background_thread_wakeup_time_set(tsd_tsdn(tsd), info, 0); - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); -} - -static void * -background_thread_entry(void *ind_arg) { - unsigned thread_ind = (unsigned)(uintptr_t)ind_arg; - assert(thread_ind < max_background_threads); -#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP - pthread_setname_np(pthread_self(), "jemalloc_bg_thd"); -#elif defined(__FreeBSD__) || defined(__DragonFly__) - pthread_set_name_np(pthread_self(), "jemalloc_bg_thd"); -#endif - if (opt_percpu_arena != percpu_arena_disabled) { - set_current_thread_affinity((int)thread_ind); - } - /* - * Start periodic background work. We use internal tsd which avoids - * side effects, for example triggering new arena creation (which in - * turn triggers another background thread creation). - */ - background_work(tsd_internal_fetch(), thread_ind); - assert(pthread_equal(pthread_self(), - background_thread_info[thread_ind].thread)); - - return NULL; -} - -static void -background_thread_init(tsd_t *tsd, background_thread_info_t *info) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); - info->state = background_thread_started; - background_thread_info_init(tsd_tsdn(tsd), info); - n_background_threads++; -} - -static bool -background_thread_create_locked(tsd_t *tsd, unsigned arena_ind) { - assert(have_background_thread); - malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); - - /* We create at most NCPUs threads. */ - size_t thread_ind = arena_ind % max_background_threads; - background_thread_info_t *info = &background_thread_info[thread_ind]; - - bool need_new_thread; - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - need_new_thread = background_thread_enabled() && - (info->state == background_thread_stopped); - if (need_new_thread) { - background_thread_init(tsd, info); - } - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - if (!need_new_thread) { - return false; - } - if (arena_ind != 0) { - /* Threads are created asynchronously by Thread 0. */ - background_thread_info_t *t0 = &background_thread_info[0]; - malloc_mutex_lock(tsd_tsdn(tsd), &t0->mtx); - assert(t0->state == background_thread_started); - pthread_cond_signal(&t0->cond); - malloc_mutex_unlock(tsd_tsdn(tsd), &t0->mtx); - - return false; - } - - pre_reentrancy(tsd, NULL); - /* - * To avoid complications (besides reentrancy), create internal - * background threads with the underlying pthread_create. - */ - int err = background_thread_create_signals_masked(&info->thread, NULL, - background_thread_entry, (void *)thread_ind); - post_reentrancy(tsd); - - if (err != 0) { - malloc_printf("<jemalloc>: arena 0 background thread creation " - "failed (%d)\n", err); - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - info->state = background_thread_stopped; - n_background_threads--; - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - - return true; - } - - return false; -} - -/* Create a new background thread if needed. */ -bool -background_thread_create(tsd_t *tsd, unsigned arena_ind) { - assert(have_background_thread); - - bool ret; - malloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock); - ret = background_thread_create_locked(tsd, arena_ind); - malloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock); - - return ret; -} - -bool -background_threads_enable(tsd_t *tsd) { - assert(n_background_threads == 0); - assert(background_thread_enabled()); - malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); - - VARIABLE_ARRAY(bool, marked, max_background_threads); - unsigned nmarked; - for (unsigned i = 0; i < max_background_threads; i++) { - marked[i] = false; - } - nmarked = 0; - /* Thread 0 is required and created at the end. */ - marked[0] = true; - /* Mark the threads we need to create for thread 0. */ - unsigned narenas = narenas_total_get(); - for (unsigned i = 1; i < narenas; i++) { - if (marked[i % max_background_threads] || - arena_get(tsd_tsdn(tsd), i, false) == NULL) { - continue; - } - background_thread_info_t *info = &background_thread_info[ - i % max_background_threads]; - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - assert(info->state == background_thread_stopped); - background_thread_init(tsd, info); - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - marked[i % max_background_threads] = true; - if (++nmarked == max_background_threads) { - break; - } - } - - bool err = background_thread_create_locked(tsd, 0); - if (err) { - return true; - } - for (unsigned i = 0; i < narenas; i++) { - arena_t *arena = arena_get(tsd_tsdn(tsd), i, false); - if (arena != NULL) { - pa_shard_set_deferral_allowed(tsd_tsdn(tsd), - &arena->pa_shard, true); - } - } - return false; -} - -bool -background_threads_disable(tsd_t *tsd) { - assert(!background_thread_enabled()); - malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); - - /* Thread 0 will be responsible for terminating other threads. */ - if (background_threads_disable_single(tsd, - &background_thread_info[0])) { - return true; - } - assert(n_background_threads == 0); - unsigned narenas = narenas_total_get(); - for (unsigned i = 0; i < narenas; i++) { - arena_t *arena = arena_get(tsd_tsdn(tsd), i, false); - if (arena != NULL) { - pa_shard_set_deferral_allowed(tsd_tsdn(tsd), - &arena->pa_shard, false); - } - } - - return false; -} - -bool -background_thread_is_started(background_thread_info_t *info) { - return info->state == background_thread_started; -} - -void -background_thread_wakeup_early(background_thread_info_t *info, - nstime_t *remaining_sleep) { - /* - * This is an optimization to increase batching. At this point - * we know that background thread wakes up soon, so the time to cache - * the just freed memory is bounded and low. - */ - if (remaining_sleep != NULL && nstime_ns(remaining_sleep) < - BACKGROUND_THREAD_MIN_INTERVAL_NS) { - return; - } - pthread_cond_signal(&info->cond); -} - -void -background_thread_prefork0(tsdn_t *tsdn) { - malloc_mutex_prefork(tsdn, &background_thread_lock); - background_thread_enabled_at_fork = background_thread_enabled(); -} - -void -background_thread_prefork1(tsdn_t *tsdn) { - for (unsigned i = 0; i < max_background_threads; i++) { - malloc_mutex_prefork(tsdn, &background_thread_info[i].mtx); - } -} - -void -background_thread_postfork_parent(tsdn_t *tsdn) { - for (unsigned i = 0; i < max_background_threads; i++) { - malloc_mutex_postfork_parent(tsdn, - &background_thread_info[i].mtx); - } - malloc_mutex_postfork_parent(tsdn, &background_thread_lock); -} - -void -background_thread_postfork_child(tsdn_t *tsdn) { - for (unsigned i = 0; i < max_background_threads; i++) { - malloc_mutex_postfork_child(tsdn, - &background_thread_info[i].mtx); - } - malloc_mutex_postfork_child(tsdn, &background_thread_lock); - if (!background_thread_enabled_at_fork) { - return; - } - - /* Clear background_thread state (reset to disabled for child). */ - malloc_mutex_lock(tsdn, &background_thread_lock); - n_background_threads = 0; - background_thread_enabled_set(tsdn, false); - for (unsigned i = 0; i < max_background_threads; i++) { - background_thread_info_t *info = &background_thread_info[i]; - malloc_mutex_lock(tsdn, &info->mtx); - info->state = background_thread_stopped; - int ret = pthread_cond_init(&info->cond, NULL); - assert(ret == 0); - background_thread_info_init(tsdn, info); - malloc_mutex_unlock(tsdn, &info->mtx); - } - malloc_mutex_unlock(tsdn, &background_thread_lock); -} - -bool -background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) { - assert(config_stats); - malloc_mutex_lock(tsdn, &background_thread_lock); - if (!background_thread_enabled()) { - malloc_mutex_unlock(tsdn, &background_thread_lock); - return true; - } - - nstime_init_zero(&stats->run_interval); - memset(&stats->max_counter_per_bg_thd, 0, sizeof(mutex_prof_data_t)); - - uint64_t num_runs = 0; - stats->num_threads = n_background_threads; - for (unsigned i = 0; i < max_background_threads; i++) { - background_thread_info_t *info = &background_thread_info[i]; - if (malloc_mutex_trylock(tsdn, &info->mtx)) { - /* - * Each background thread run may take a long time; - * avoid waiting on the stats if the thread is active. - */ - continue; - } - if (info->state != background_thread_stopped) { - num_runs += info->tot_n_runs; - nstime_add(&stats->run_interval, &info->tot_sleep_time); - malloc_mutex_prof_max_update(tsdn, - &stats->max_counter_per_bg_thd, &info->mtx); - } - malloc_mutex_unlock(tsdn, &info->mtx); - } - stats->num_runs = num_runs; - if (num_runs > 0) { - nstime_idivide(&stats->run_interval, num_runs); - } - malloc_mutex_unlock(tsdn, &background_thread_lock); - - return false; -} - -#undef BACKGROUND_THREAD_NPAGES_THRESHOLD -#undef BILLION -#undef BACKGROUND_THREAD_MIN_INTERVAL_NS - -#ifdef JEMALLOC_HAVE_DLSYM -#include <dlfcn.h> -#endif - -static bool -pthread_create_fptr_init(void) { - if (pthread_create_fptr != NULL) { - return false; - } - /* - * Try the next symbol first, because 1) when use lazy_lock we have a - * wrapper for pthread_create; and 2) application may define its own - * wrapper as well (and can call malloc within the wrapper). - */ -#ifdef JEMALLOC_HAVE_DLSYM - pthread_create_fptr = dlsym(RTLD_NEXT, "pthread_create"); -#else - pthread_create_fptr = NULL; -#endif - if (pthread_create_fptr == NULL) { - if (config_lazy_lock) { - malloc_write("<jemalloc>: Error in dlsym(RTLD_NEXT, " - "\"pthread_create\")\n"); - abort(); - } else { - /* Fall back to the default symbol. */ - pthread_create_fptr = pthread_create; - } - } - - return false; -} - -/* - * When lazy lock is enabled, we need to make sure setting isthreaded before - * taking any background_thread locks. This is called early in ctl (instead of - * wait for the pthread_create calls to trigger) because the mutex is required - * before creating background threads. - */ -void -background_thread_ctl_init(tsdn_t *tsdn) { - malloc_mutex_assert_not_owner(tsdn, &background_thread_lock); -#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER - pthread_create_fptr_init(); - pthread_create_wrapper_init(); -#endif -} - -#endif /* defined(JEMALLOC_BACKGROUND_THREAD) */ - -bool -background_thread_boot0(void) { - if (!have_background_thread && opt_background_thread) { - malloc_printf("<jemalloc>: option background_thread currently " - "supports pthread only\n"); - return true; - } -#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER - if ((config_lazy_lock || opt_background_thread) && - pthread_create_fptr_init()) { - return true; - } -#endif - return false; -} - -bool -background_thread_boot1(tsdn_t *tsdn, base_t *base) { -#ifdef JEMALLOC_BACKGROUND_THREAD - assert(have_background_thread); - assert(narenas_total_get() > 0); - - if (opt_max_background_threads > MAX_BACKGROUND_THREAD_LIMIT) { - opt_max_background_threads = DEFAULT_NUM_BACKGROUND_THREAD; - } - max_background_threads = opt_max_background_threads; - - background_thread_enabled_set(tsdn, opt_background_thread); - if (malloc_mutex_init(&background_thread_lock, - "background_thread_global", - WITNESS_RANK_BACKGROUND_THREAD_GLOBAL, - malloc_mutex_rank_exclusive)) { - return true; - } - - background_thread_info = (background_thread_info_t *)base_alloc(tsdn, - base, opt_max_background_threads * - sizeof(background_thread_info_t), CACHELINE); - if (background_thread_info == NULL) { - return true; - } - - for (unsigned i = 0; i < max_background_threads; i++) { - background_thread_info_t *info = &background_thread_info[i]; - /* Thread mutex is rank_inclusive because of thread0. */ - if (malloc_mutex_init(&info->mtx, "background_thread", - WITNESS_RANK_BACKGROUND_THREAD, - malloc_mutex_address_ordered)) { - return true; - } - if (pthread_cond_init(&info->cond, NULL)) { - return true; - } - malloc_mutex_lock(tsdn, &info->mtx); - info->state = background_thread_stopped; - background_thread_info_init(tsdn, info); - malloc_mutex_unlock(tsdn, &info->mtx); - } -#endif - - return false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/base.c b/fluent-bit/lib/jemalloc-5.3.0/src/base.c deleted file mode 100644 index 7f4d6756..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/base.c +++ /dev/null @@ -1,529 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/extent_mmap.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/sz.h" - -/* - * In auto mode, arenas switch to huge pages for the base allocator on the - * second base block. a0 switches to thp on the 5th block (after 20 megabytes - * of metadata), since more metadata (e.g. rtree nodes) come from a0's base. - */ - -#define BASE_AUTO_THP_THRESHOLD 2 -#define BASE_AUTO_THP_THRESHOLD_A0 5 - -/******************************************************************************/ -/* Data. */ - -static base_t *b0; - -metadata_thp_mode_t opt_metadata_thp = METADATA_THP_DEFAULT; - -const char *metadata_thp_mode_names[] = { - "disabled", - "auto", - "always" -}; - -/******************************************************************************/ - -static inline bool -metadata_thp_madvise(void) { - return (metadata_thp_enabled() && - (init_system_thp_mode == thp_mode_default)); -} - -static void * -base_map(tsdn_t *tsdn, ehooks_t *ehooks, unsigned ind, size_t size) { - void *addr; - bool zero = true; - bool commit = true; - - /* Use huge page sizes and alignment regardless of opt_metadata_thp. */ - assert(size == HUGEPAGE_CEILING(size)); - size_t alignment = HUGEPAGE; - if (ehooks_are_default(ehooks)) { - addr = extent_alloc_mmap(NULL, size, alignment, &zero, &commit); - if (have_madvise_huge && addr) { - pages_set_thp_state(addr, size); - } - } else { - addr = ehooks_alloc(tsdn, ehooks, NULL, size, alignment, &zero, - &commit); - } - - return addr; -} - -static void -base_unmap(tsdn_t *tsdn, ehooks_t *ehooks, unsigned ind, void *addr, - size_t size) { - /* - * Cascade through dalloc, decommit, purge_forced, and purge_lazy, - * stopping at first success. This cascade is performed for consistency - * with the cascade in extent_dalloc_wrapper() because an application's - * custom hooks may not support e.g. dalloc. This function is only ever - * called as a side effect of arena destruction, so although it might - * seem pointless to do anything besides dalloc here, the application - * may in fact want the end state of all associated virtual memory to be - * in some consistent-but-allocated state. - */ - if (ehooks_are_default(ehooks)) { - if (!extent_dalloc_mmap(addr, size)) { - goto label_done; - } - if (!pages_decommit(addr, size)) { - goto label_done; - } - if (!pages_purge_forced(addr, size)) { - goto label_done; - } - if (!pages_purge_lazy(addr, size)) { - goto label_done; - } - /* Nothing worked. This should never happen. */ - not_reached(); - } else { - if (!ehooks_dalloc(tsdn, ehooks, addr, size, true)) { - goto label_done; - } - if (!ehooks_decommit(tsdn, ehooks, addr, size, 0, size)) { - goto label_done; - } - if (!ehooks_purge_forced(tsdn, ehooks, addr, size, 0, size)) { - goto label_done; - } - if (!ehooks_purge_lazy(tsdn, ehooks, addr, size, 0, size)) { - goto label_done; - } - /* Nothing worked. That's the application's problem. */ - } -label_done: - if (metadata_thp_madvise()) { - /* Set NOHUGEPAGE after unmap to avoid kernel defrag. */ - assert(((uintptr_t)addr & HUGEPAGE_MASK) == 0 && - (size & HUGEPAGE_MASK) == 0); - pages_nohuge(addr, size); - } -} - -static void -base_edata_init(size_t *extent_sn_next, edata_t *edata, void *addr, - size_t size) { - size_t sn; - - sn = *extent_sn_next; - (*extent_sn_next)++; - - edata_binit(edata, addr, size, sn); -} - -static size_t -base_get_num_blocks(base_t *base, bool with_new_block) { - base_block_t *b = base->blocks; - assert(b != NULL); - - size_t n_blocks = with_new_block ? 2 : 1; - while (b->next != NULL) { - n_blocks++; - b = b->next; - } - - return n_blocks; -} - -static void -base_auto_thp_switch(tsdn_t *tsdn, base_t *base) { - assert(opt_metadata_thp == metadata_thp_auto); - malloc_mutex_assert_owner(tsdn, &base->mtx); - if (base->auto_thp_switched) { - return; - } - /* Called when adding a new block. */ - bool should_switch; - if (base_ind_get(base) != 0) { - should_switch = (base_get_num_blocks(base, true) == - BASE_AUTO_THP_THRESHOLD); - } else { - should_switch = (base_get_num_blocks(base, true) == - BASE_AUTO_THP_THRESHOLD_A0); - } - if (!should_switch) { - return; - } - - base->auto_thp_switched = true; - assert(!config_stats || base->n_thp == 0); - /* Make the initial blocks THP lazily. */ - base_block_t *block = base->blocks; - while (block != NULL) { - assert((block->size & HUGEPAGE_MASK) == 0); - pages_huge(block, block->size); - if (config_stats) { - base->n_thp += HUGEPAGE_CEILING(block->size - - edata_bsize_get(&block->edata)) >> LG_HUGEPAGE; - } - block = block->next; - assert(block == NULL || (base_ind_get(base) == 0)); - } -} - -static void * -base_extent_bump_alloc_helper(edata_t *edata, size_t *gap_size, size_t size, - size_t alignment) { - void *ret; - - assert(alignment == ALIGNMENT_CEILING(alignment, QUANTUM)); - assert(size == ALIGNMENT_CEILING(size, alignment)); - - *gap_size = ALIGNMENT_CEILING((uintptr_t)edata_addr_get(edata), - alignment) - (uintptr_t)edata_addr_get(edata); - ret = (void *)((uintptr_t)edata_addr_get(edata) + *gap_size); - assert(edata_bsize_get(edata) >= *gap_size + size); - edata_binit(edata, (void *)((uintptr_t)edata_addr_get(edata) + - *gap_size + size), edata_bsize_get(edata) - *gap_size - size, - edata_sn_get(edata)); - return ret; -} - -static void -base_extent_bump_alloc_post(base_t *base, edata_t *edata, size_t gap_size, - void *addr, size_t size) { - if (edata_bsize_get(edata) > 0) { - /* - * Compute the index for the largest size class that does not - * exceed extent's size. - */ - szind_t index_floor = - sz_size2index(edata_bsize_get(edata) + 1) - 1; - edata_heap_insert(&base->avail[index_floor], edata); - } - - if (config_stats) { - base->allocated += size; - /* - * Add one PAGE to base_resident for every page boundary that is - * crossed by the new allocation. Adjust n_thp similarly when - * metadata_thp is enabled. - */ - base->resident += PAGE_CEILING((uintptr_t)addr + size) - - PAGE_CEILING((uintptr_t)addr - gap_size); - assert(base->allocated <= base->resident); - assert(base->resident <= base->mapped); - if (metadata_thp_madvise() && (opt_metadata_thp == - metadata_thp_always || base->auto_thp_switched)) { - base->n_thp += (HUGEPAGE_CEILING((uintptr_t)addr + size) - - HUGEPAGE_CEILING((uintptr_t)addr - gap_size)) >> - LG_HUGEPAGE; - assert(base->mapped >= base->n_thp << LG_HUGEPAGE); - } - } -} - -static void * -base_extent_bump_alloc(base_t *base, edata_t *edata, size_t size, - size_t alignment) { - void *ret; - size_t gap_size; - - ret = base_extent_bump_alloc_helper(edata, &gap_size, size, alignment); - base_extent_bump_alloc_post(base, edata, gap_size, ret, size); - return ret; -} - -/* - * Allocate a block of virtual memory that is large enough to start with a - * base_block_t header, followed by an object of specified size and alignment. - * On success a pointer to the initialized base_block_t header is returned. - */ -static base_block_t * -base_block_alloc(tsdn_t *tsdn, base_t *base, ehooks_t *ehooks, unsigned ind, - pszind_t *pind_last, size_t *extent_sn_next, size_t size, - size_t alignment) { - alignment = ALIGNMENT_CEILING(alignment, QUANTUM); - size_t usize = ALIGNMENT_CEILING(size, alignment); - size_t header_size = sizeof(base_block_t); - size_t gap_size = ALIGNMENT_CEILING(header_size, alignment) - - header_size; - /* - * Create increasingly larger blocks in order to limit the total number - * of disjoint virtual memory ranges. Choose the next size in the page - * size class series (skipping size classes that are not a multiple of - * HUGEPAGE), or a size large enough to satisfy the requested size and - * alignment, whichever is larger. - */ - size_t min_block_size = HUGEPAGE_CEILING(sz_psz2u(header_size + gap_size - + usize)); - pszind_t pind_next = (*pind_last + 1 < sz_psz2ind(SC_LARGE_MAXCLASS)) ? - *pind_last + 1 : *pind_last; - size_t next_block_size = HUGEPAGE_CEILING(sz_pind2sz(pind_next)); - size_t block_size = (min_block_size > next_block_size) ? min_block_size - : next_block_size; - base_block_t *block = (base_block_t *)base_map(tsdn, ehooks, ind, - block_size); - if (block == NULL) { - return NULL; - } - - if (metadata_thp_madvise()) { - void *addr = (void *)block; - assert(((uintptr_t)addr & HUGEPAGE_MASK) == 0 && - (block_size & HUGEPAGE_MASK) == 0); - if (opt_metadata_thp == metadata_thp_always) { - pages_huge(addr, block_size); - } else if (opt_metadata_thp == metadata_thp_auto && - base != NULL) { - /* base != NULL indicates this is not a new base. */ - malloc_mutex_lock(tsdn, &base->mtx); - base_auto_thp_switch(tsdn, base); - if (base->auto_thp_switched) { - pages_huge(addr, block_size); - } - malloc_mutex_unlock(tsdn, &base->mtx); - } - } - - *pind_last = sz_psz2ind(block_size); - block->size = block_size; - block->next = NULL; - assert(block_size >= header_size); - base_edata_init(extent_sn_next, &block->edata, - (void *)((uintptr_t)block + header_size), block_size - header_size); - return block; -} - -/* - * Allocate an extent that is at least as large as specified size, with - * specified alignment. - */ -static edata_t * -base_extent_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) { - malloc_mutex_assert_owner(tsdn, &base->mtx); - - ehooks_t *ehooks = base_ehooks_get_for_metadata(base); - /* - * Drop mutex during base_block_alloc(), because an extent hook will be - * called. - */ - malloc_mutex_unlock(tsdn, &base->mtx); - base_block_t *block = base_block_alloc(tsdn, base, ehooks, - base_ind_get(base), &base->pind_last, &base->extent_sn_next, size, - alignment); - malloc_mutex_lock(tsdn, &base->mtx); - if (block == NULL) { - return NULL; - } - block->next = base->blocks; - base->blocks = block; - if (config_stats) { - base->allocated += sizeof(base_block_t); - base->resident += PAGE_CEILING(sizeof(base_block_t)); - base->mapped += block->size; - if (metadata_thp_madvise() && - !(opt_metadata_thp == metadata_thp_auto - && !base->auto_thp_switched)) { - assert(base->n_thp > 0); - base->n_thp += HUGEPAGE_CEILING(sizeof(base_block_t)) >> - LG_HUGEPAGE; - } - assert(base->allocated <= base->resident); - assert(base->resident <= base->mapped); - assert(base->n_thp << LG_HUGEPAGE <= base->mapped); - } - return &block->edata; -} - -base_t * -b0get(void) { - return b0; -} - -base_t * -base_new(tsdn_t *tsdn, unsigned ind, const extent_hooks_t *extent_hooks, - bool metadata_use_hooks) { - pszind_t pind_last = 0; - size_t extent_sn_next = 0; - - /* - * The base will contain the ehooks eventually, but it itself is - * allocated using them. So we use some stack ehooks to bootstrap its - * memory, and then initialize the ehooks within the base_t. - */ - ehooks_t fake_ehooks; - ehooks_init(&fake_ehooks, metadata_use_hooks ? - (extent_hooks_t *)extent_hooks : - (extent_hooks_t *)&ehooks_default_extent_hooks, ind); - - base_block_t *block = base_block_alloc(tsdn, NULL, &fake_ehooks, ind, - &pind_last, &extent_sn_next, sizeof(base_t), QUANTUM); - if (block == NULL) { - return NULL; - } - - size_t gap_size; - size_t base_alignment = CACHELINE; - size_t base_size = ALIGNMENT_CEILING(sizeof(base_t), base_alignment); - base_t *base = (base_t *)base_extent_bump_alloc_helper(&block->edata, - &gap_size, base_size, base_alignment); - ehooks_init(&base->ehooks, (extent_hooks_t *)extent_hooks, ind); - ehooks_init(&base->ehooks_base, metadata_use_hooks ? - (extent_hooks_t *)extent_hooks : - (extent_hooks_t *)&ehooks_default_extent_hooks, ind); - if (malloc_mutex_init(&base->mtx, "base", WITNESS_RANK_BASE, - malloc_mutex_rank_exclusive)) { - base_unmap(tsdn, &fake_ehooks, ind, block, block->size); - return NULL; - } - base->pind_last = pind_last; - base->extent_sn_next = extent_sn_next; - base->blocks = block; - base->auto_thp_switched = false; - for (szind_t i = 0; i < SC_NSIZES; i++) { - edata_heap_new(&base->avail[i]); - } - if (config_stats) { - base->allocated = sizeof(base_block_t); - base->resident = PAGE_CEILING(sizeof(base_block_t)); - base->mapped = block->size; - base->n_thp = (opt_metadata_thp == metadata_thp_always) && - metadata_thp_madvise() ? HUGEPAGE_CEILING(sizeof(base_block_t)) - >> LG_HUGEPAGE : 0; - assert(base->allocated <= base->resident); - assert(base->resident <= base->mapped); - assert(base->n_thp << LG_HUGEPAGE <= base->mapped); - } - base_extent_bump_alloc_post(base, &block->edata, gap_size, base, - base_size); - - return base; -} - -void -base_delete(tsdn_t *tsdn, base_t *base) { - ehooks_t *ehooks = base_ehooks_get_for_metadata(base); - base_block_t *next = base->blocks; - do { - base_block_t *block = next; - next = block->next; - base_unmap(tsdn, ehooks, base_ind_get(base), block, - block->size); - } while (next != NULL); -} - -ehooks_t * -base_ehooks_get(base_t *base) { - return &base->ehooks; -} - -ehooks_t * -base_ehooks_get_for_metadata(base_t *base) { - return &base->ehooks_base; -} - -extent_hooks_t * -base_extent_hooks_set(base_t *base, extent_hooks_t *extent_hooks) { - extent_hooks_t *old_extent_hooks = - ehooks_get_extent_hooks_ptr(&base->ehooks); - ehooks_init(&base->ehooks, extent_hooks, ehooks_ind_get(&base->ehooks)); - return old_extent_hooks; -} - -static void * -base_alloc_impl(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment, - size_t *esn) { - alignment = QUANTUM_CEILING(alignment); - size_t usize = ALIGNMENT_CEILING(size, alignment); - size_t asize = usize + alignment - QUANTUM; - - edata_t *edata = NULL; - malloc_mutex_lock(tsdn, &base->mtx); - for (szind_t i = sz_size2index(asize); i < SC_NSIZES; i++) { - edata = edata_heap_remove_first(&base->avail[i]); - if (edata != NULL) { - /* Use existing space. */ - break; - } - } - if (edata == NULL) { - /* Try to allocate more space. */ - edata = base_extent_alloc(tsdn, base, usize, alignment); - } - void *ret; - if (edata == NULL) { - ret = NULL; - goto label_return; - } - - ret = base_extent_bump_alloc(base, edata, usize, alignment); - if (esn != NULL) { - *esn = (size_t)edata_sn_get(edata); - } -label_return: - malloc_mutex_unlock(tsdn, &base->mtx); - return ret; -} - -/* - * base_alloc() returns zeroed memory, which is always demand-zeroed for the - * auto arenas, in order to make multi-page sparse data structures such as radix - * tree nodes efficient with respect to physical memory usage. Upon success a - * pointer to at least size bytes with specified alignment is returned. Note - * that size is rounded up to the nearest multiple of alignment to avoid false - * sharing. - */ -void * -base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) { - return base_alloc_impl(tsdn, base, size, alignment, NULL); -} - -edata_t * -base_alloc_edata(tsdn_t *tsdn, base_t *base) { - size_t esn; - edata_t *edata = base_alloc_impl(tsdn, base, sizeof(edata_t), - EDATA_ALIGNMENT, &esn); - if (edata == NULL) { - return NULL; - } - edata_esn_set(edata, esn); - return edata; -} - -void -base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated, size_t *resident, - size_t *mapped, size_t *n_thp) { - cassert(config_stats); - - malloc_mutex_lock(tsdn, &base->mtx); - assert(base->allocated <= base->resident); - assert(base->resident <= base->mapped); - *allocated = base->allocated; - *resident = base->resident; - *mapped = base->mapped; - *n_thp = base->n_thp; - malloc_mutex_unlock(tsdn, &base->mtx); -} - -void -base_prefork(tsdn_t *tsdn, base_t *base) { - malloc_mutex_prefork(tsdn, &base->mtx); -} - -void -base_postfork_parent(tsdn_t *tsdn, base_t *base) { - malloc_mutex_postfork_parent(tsdn, &base->mtx); -} - -void -base_postfork_child(tsdn_t *tsdn, base_t *base) { - malloc_mutex_postfork_child(tsdn, &base->mtx); -} - -bool -base_boot(tsdn_t *tsdn) { - b0 = base_new(tsdn, 0, (extent_hooks_t *)&ehooks_default_extent_hooks, - /* metadata_use_hooks */ true); - return (b0 == NULL); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/bin.c b/fluent-bit/lib/jemalloc-5.3.0/src/bin.c deleted file mode 100644 index fa204587..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/bin.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/bin.h" -#include "jemalloc/internal/sc.h" -#include "jemalloc/internal/witness.h" - -bool -bin_update_shard_size(unsigned bin_shard_sizes[SC_NBINS], size_t start_size, - size_t end_size, size_t nshards) { - if (nshards > BIN_SHARDS_MAX || nshards == 0) { - return true; - } - - if (start_size > SC_SMALL_MAXCLASS) { - return false; - } - if (end_size > SC_SMALL_MAXCLASS) { - end_size = SC_SMALL_MAXCLASS; - } - - /* Compute the index since this may happen before sz init. */ - szind_t ind1 = sz_size2index_compute(start_size); - szind_t ind2 = sz_size2index_compute(end_size); - for (unsigned i = ind1; i <= ind2; i++) { - bin_shard_sizes[i] = (unsigned)nshards; - } - - return false; -} - -void -bin_shard_sizes_boot(unsigned bin_shard_sizes[SC_NBINS]) { - /* Load the default number of shards. */ - for (unsigned i = 0; i < SC_NBINS; i++) { - bin_shard_sizes[i] = N_BIN_SHARDS_DEFAULT; - } -} - -bool -bin_init(bin_t *bin) { - if (malloc_mutex_init(&bin->lock, "bin", WITNESS_RANK_BIN, - malloc_mutex_rank_exclusive)) { - return true; - } - bin->slabcur = NULL; - edata_heap_new(&bin->slabs_nonfull); - edata_list_active_init(&bin->slabs_full); - if (config_stats) { - memset(&bin->stats, 0, sizeof(bin_stats_t)); - } - return false; -} - -void -bin_prefork(tsdn_t *tsdn, bin_t *bin) { - malloc_mutex_prefork(tsdn, &bin->lock); -} - -void -bin_postfork_parent(tsdn_t *tsdn, bin_t *bin) { - malloc_mutex_postfork_parent(tsdn, &bin->lock); -} - -void -bin_postfork_child(tsdn_t *tsdn, bin_t *bin) { - malloc_mutex_postfork_child(tsdn, &bin->lock); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/bin_info.c b/fluent-bit/lib/jemalloc-5.3.0/src/bin_info.c deleted file mode 100644 index 8629ef88..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/bin_info.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/bin_info.h" - -bin_info_t bin_infos[SC_NBINS]; - -static void -bin_infos_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], - bin_info_t infos[SC_NBINS]) { - for (unsigned i = 0; i < SC_NBINS; i++) { - bin_info_t *bin_info = &infos[i]; - sc_t *sc = &sc_data->sc[i]; - bin_info->reg_size = ((size_t)1U << sc->lg_base) - + ((size_t)sc->ndelta << sc->lg_delta); - bin_info->slab_size = (sc->pgs << LG_PAGE); - bin_info->nregs = - (uint32_t)(bin_info->slab_size / bin_info->reg_size); - bin_info->n_shards = bin_shard_sizes[i]; - bitmap_info_t bitmap_info = BITMAP_INFO_INITIALIZER( - bin_info->nregs); - bin_info->bitmap_info = bitmap_info; - } -} - -void -bin_info_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) { - assert(sc_data->initialized); - bin_infos_init(sc_data, bin_shard_sizes, bin_infos); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/bitmap.c b/fluent-bit/lib/jemalloc-5.3.0/src/bitmap.c deleted file mode 100644 index 0ccedc5d..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/bitmap.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" - -/******************************************************************************/ - -#ifdef BITMAP_USE_TREE - -void -bitmap_info_init(bitmap_info_t *binfo, size_t nbits) { - unsigned i; - size_t group_count; - - assert(nbits > 0); - assert(nbits <= (ZU(1) << LG_BITMAP_MAXBITS)); - - /* - * Compute the number of groups necessary to store nbits bits, and - * progressively work upward through the levels until reaching a level - * that requires only one group. - */ - binfo->levels[0].group_offset = 0; - group_count = BITMAP_BITS2GROUPS(nbits); - for (i = 1; group_count > 1; i++) { - assert(i < BITMAP_MAX_LEVELS); - binfo->levels[i].group_offset = binfo->levels[i-1].group_offset - + group_count; - group_count = BITMAP_BITS2GROUPS(group_count); - } - binfo->levels[i].group_offset = binfo->levels[i-1].group_offset - + group_count; - assert(binfo->levels[i].group_offset <= BITMAP_GROUPS_MAX); - binfo->nlevels = i; - binfo->nbits = nbits; -} - -static size_t -bitmap_info_ngroups(const bitmap_info_t *binfo) { - return binfo->levels[binfo->nlevels].group_offset; -} - -void -bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill) { - size_t extra; - unsigned i; - - /* - * Bits are actually inverted with regard to the external bitmap - * interface. - */ - - if (fill) { - /* The "filled" bitmap starts out with all 0 bits. */ - memset(bitmap, 0, bitmap_size(binfo)); - return; - } - - /* - * The "empty" bitmap starts out with all 1 bits, except for trailing - * unused bits (if any). Note that each group uses bit 0 to correspond - * to the first logical bit in the group, so extra bits are the most - * significant bits of the last group. - */ - memset(bitmap, 0xffU, bitmap_size(binfo)); - extra = (BITMAP_GROUP_NBITS - (binfo->nbits & BITMAP_GROUP_NBITS_MASK)) - & BITMAP_GROUP_NBITS_MASK; - if (extra != 0) { - bitmap[binfo->levels[1].group_offset - 1] >>= extra; - } - for (i = 1; i < binfo->nlevels; i++) { - size_t group_count = binfo->levels[i].group_offset - - binfo->levels[i-1].group_offset; - extra = (BITMAP_GROUP_NBITS - (group_count & - BITMAP_GROUP_NBITS_MASK)) & BITMAP_GROUP_NBITS_MASK; - if (extra != 0) { - bitmap[binfo->levels[i+1].group_offset - 1] >>= extra; - } - } -} - -#else /* BITMAP_USE_TREE */ - -void -bitmap_info_init(bitmap_info_t *binfo, size_t nbits) { - assert(nbits > 0); - assert(nbits <= (ZU(1) << LG_BITMAP_MAXBITS)); - - binfo->ngroups = BITMAP_BITS2GROUPS(nbits); - binfo->nbits = nbits; -} - -static size_t -bitmap_info_ngroups(const bitmap_info_t *binfo) { - return binfo->ngroups; -} - -void -bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill) { - size_t extra; - - if (fill) { - memset(bitmap, 0, bitmap_size(binfo)); - return; - } - - memset(bitmap, 0xffU, bitmap_size(binfo)); - extra = (BITMAP_GROUP_NBITS - (binfo->nbits & BITMAP_GROUP_NBITS_MASK)) - & BITMAP_GROUP_NBITS_MASK; - if (extra != 0) { - bitmap[binfo->ngroups - 1] >>= extra; - } -} - -#endif /* BITMAP_USE_TREE */ - -size_t -bitmap_size(const bitmap_info_t *binfo) { - return (bitmap_info_ngroups(binfo) << LG_SIZEOF_BITMAP); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/buf_writer.c b/fluent-bit/lib/jemalloc-5.3.0/src/buf_writer.c deleted file mode 100644 index 7c6f7940..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/buf_writer.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/buf_writer.h" -#include "jemalloc/internal/malloc_io.h" - -static void * -buf_writer_allocate_internal_buf(tsdn_t *tsdn, size_t buf_len) { -#ifdef JEMALLOC_JET - if (buf_len > SC_LARGE_MAXCLASS) { - return NULL; - } -#else - assert(buf_len <= SC_LARGE_MAXCLASS); -#endif - return iallocztm(tsdn, buf_len, sz_size2index(buf_len), false, NULL, - true, arena_get(tsdn, 0, false), true); -} - -static void -buf_writer_free_internal_buf(tsdn_t *tsdn, void *buf) { - if (buf != NULL) { - idalloctm(tsdn, buf, NULL, NULL, true, true); - } -} - -static void -buf_writer_assert(buf_writer_t *buf_writer) { - assert(buf_writer != NULL); - assert(buf_writer->write_cb != NULL); - if (buf_writer->buf != NULL) { - assert(buf_writer->buf_size > 0); - } else { - assert(buf_writer->buf_size == 0); - assert(buf_writer->internal_buf); - } - assert(buf_writer->buf_end <= buf_writer->buf_size); -} - -bool -buf_writer_init(tsdn_t *tsdn, buf_writer_t *buf_writer, write_cb_t *write_cb, - void *cbopaque, char *buf, size_t buf_len) { - if (write_cb != NULL) { - buf_writer->write_cb = write_cb; - } else { - buf_writer->write_cb = je_malloc_message != NULL ? - je_malloc_message : wrtmessage; - } - buf_writer->cbopaque = cbopaque; - assert(buf_len >= 2); - if (buf != NULL) { - buf_writer->buf = buf; - buf_writer->internal_buf = false; - } else { - buf_writer->buf = buf_writer_allocate_internal_buf(tsdn, - buf_len); - buf_writer->internal_buf = true; - } - if (buf_writer->buf != NULL) { - buf_writer->buf_size = buf_len - 1; /* Allowing for '\0'. */ - } else { - buf_writer->buf_size = 0; - } - buf_writer->buf_end = 0; - buf_writer_assert(buf_writer); - return buf_writer->buf == NULL; -} - -void -buf_writer_flush(buf_writer_t *buf_writer) { - buf_writer_assert(buf_writer); - if (buf_writer->buf == NULL) { - return; - } - buf_writer->buf[buf_writer->buf_end] = '\0'; - buf_writer->write_cb(buf_writer->cbopaque, buf_writer->buf); - buf_writer->buf_end = 0; - buf_writer_assert(buf_writer); -} - -void -buf_writer_cb(void *buf_writer_arg, const char *s) { - buf_writer_t *buf_writer = (buf_writer_t *)buf_writer_arg; - buf_writer_assert(buf_writer); - if (buf_writer->buf == NULL) { - buf_writer->write_cb(buf_writer->cbopaque, s); - return; - } - size_t i, slen, n; - for (i = 0, slen = strlen(s); i < slen; i += n) { - if (buf_writer->buf_end == buf_writer->buf_size) { - buf_writer_flush(buf_writer); - } - size_t s_remain = slen - i; - size_t buf_remain = buf_writer->buf_size - buf_writer->buf_end; - n = s_remain < buf_remain ? s_remain : buf_remain; - memcpy(buf_writer->buf + buf_writer->buf_end, s + i, n); - buf_writer->buf_end += n; - buf_writer_assert(buf_writer); - } - assert(i == slen); -} - -void -buf_writer_terminate(tsdn_t *tsdn, buf_writer_t *buf_writer) { - buf_writer_assert(buf_writer); - buf_writer_flush(buf_writer); - if (buf_writer->internal_buf) { - buf_writer_free_internal_buf(tsdn, buf_writer->buf); - } -} - -void -buf_writer_pipe(buf_writer_t *buf_writer, read_cb_t *read_cb, - void *read_cbopaque) { - /* - * A tiny local buffer in case the buffered writer failed to allocate - * at init. - */ - static char backup_buf[16]; - static buf_writer_t backup_buf_writer; - - buf_writer_assert(buf_writer); - assert(read_cb != NULL); - if (buf_writer->buf == NULL) { - buf_writer_init(TSDN_NULL, &backup_buf_writer, - buf_writer->write_cb, buf_writer->cbopaque, backup_buf, - sizeof(backup_buf)); - buf_writer = &backup_buf_writer; - } - assert(buf_writer->buf != NULL); - ssize_t nread = 0; - do { - buf_writer->buf_end += nread; - buf_writer_assert(buf_writer); - if (buf_writer->buf_end == buf_writer->buf_size) { - buf_writer_flush(buf_writer); - } - nread = read_cb(read_cbopaque, - buf_writer->buf + buf_writer->buf_end, - buf_writer->buf_size - buf_writer->buf_end); - } while (nread > 0); - buf_writer_flush(buf_writer); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/cache_bin.c b/fluent-bit/lib/jemalloc-5.3.0/src/cache_bin.c deleted file mode 100644 index 9ae072a0..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/cache_bin.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/bit_util.h" -#include "jemalloc/internal/cache_bin.h" -#include "jemalloc/internal/safety_check.h" - -void -cache_bin_info_init(cache_bin_info_t *info, - cache_bin_sz_t ncached_max) { - assert(ncached_max <= CACHE_BIN_NCACHED_MAX); - size_t stack_size = (size_t)ncached_max * sizeof(void *); - assert(stack_size < ((size_t)1 << (sizeof(cache_bin_sz_t) * 8))); - info->ncached_max = (cache_bin_sz_t)ncached_max; -} - -void -cache_bin_info_compute_alloc(cache_bin_info_t *infos, szind_t ninfos, - size_t *size, size_t *alignment) { - /* For the total bin stack region (per tcache), reserve 2 more slots so - * that - * 1) the empty position can be safely read on the fast path before - * checking "is_empty"; and - * 2) the cur_ptr can go beyond the empty position by 1 step safely on - * the fast path (i.e. no overflow). - */ - *size = sizeof(void *) * 2; - for (szind_t i = 0; i < ninfos; i++) { - assert(infos[i].ncached_max > 0); - *size += infos[i].ncached_max * sizeof(void *); - } - - /* - * Align to at least PAGE, to minimize the # of TLBs needed by the - * smaller sizes; also helps if the larger sizes don't get used at all. - */ - *alignment = PAGE; -} - -void -cache_bin_preincrement(cache_bin_info_t *infos, szind_t ninfos, void *alloc, - size_t *cur_offset) { - if (config_debug) { - size_t computed_size; - size_t computed_alignment; - - /* Pointer should be as aligned as we asked for. */ - cache_bin_info_compute_alloc(infos, ninfos, &computed_size, - &computed_alignment); - assert(((uintptr_t)alloc & (computed_alignment - 1)) == 0); - } - - *(uintptr_t *)((uintptr_t)alloc + *cur_offset) = - cache_bin_preceding_junk; - *cur_offset += sizeof(void *); -} - -void -cache_bin_postincrement(cache_bin_info_t *infos, szind_t ninfos, void *alloc, - size_t *cur_offset) { - *(uintptr_t *)((uintptr_t)alloc + *cur_offset) = - cache_bin_trailing_junk; - *cur_offset += sizeof(void *); -} - -void -cache_bin_init(cache_bin_t *bin, cache_bin_info_t *info, void *alloc, - size_t *cur_offset) { - /* - * The full_position points to the lowest available space. Allocations - * will access the slots toward higher addresses (for the benefit of - * adjacent prefetch). - */ - void *stack_cur = (void *)((uintptr_t)alloc + *cur_offset); - void *full_position = stack_cur; - uint16_t bin_stack_size = info->ncached_max * sizeof(void *); - - *cur_offset += bin_stack_size; - void *empty_position = (void *)((uintptr_t)alloc + *cur_offset); - - /* Init to the empty position. */ - bin->stack_head = (void **)empty_position; - bin->low_bits_low_water = (uint16_t)(uintptr_t)bin->stack_head; - bin->low_bits_full = (uint16_t)(uintptr_t)full_position; - bin->low_bits_empty = (uint16_t)(uintptr_t)empty_position; - cache_bin_sz_t free_spots = cache_bin_diff(bin, - bin->low_bits_full, (uint16_t)(uintptr_t)bin->stack_head, - /* racy */ false); - assert(free_spots == bin_stack_size); - assert(cache_bin_ncached_get_local(bin, info) == 0); - assert(cache_bin_empty_position_get(bin) == empty_position); - - assert(bin_stack_size > 0 || empty_position == full_position); -} - -bool -cache_bin_still_zero_initialized(cache_bin_t *bin) { - return bin->stack_head == NULL; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/ckh.c b/fluent-bit/lib/jemalloc-5.3.0/src/ckh.c deleted file mode 100644 index 8db4319c..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/ckh.c +++ /dev/null @@ -1,569 +0,0 @@ -/* - ******************************************************************************* - * Implementation of (2^1+,2) cuckoo hashing, where 2^1+ indicates that each - * hash bucket contains 2^n cells, for n >= 1, and 2 indicates that two hash - * functions are employed. The original cuckoo hashing algorithm was described - * in: - * - * Pagh, R., F.F. Rodler (2004) Cuckoo Hashing. Journal of Algorithms - * 51(2):122-144. - * - * Generalization of cuckoo hashing was discussed in: - * - * Erlingsson, U., M. Manasse, F. McSherry (2006) A cool and practical - * alternative to traditional hash tables. In Proceedings of the 7th - * Workshop on Distributed Data and Structures (WDAS'06), Santa Clara, CA, - * January 2006. - * - * This implementation uses precisely two hash functions because that is the - * fewest that can work, and supporting multiple hashes is an implementation - * burden. Here is a reproduction of Figure 1 from Erlingsson et al. (2006) - * that shows approximate expected maximum load factors for various - * configurations: - * - * | #cells/bucket | - * #hashes | 1 | 2 | 4 | 8 | - * --------+-------+-------+-------+-------+ - * 1 | 0.006 | 0.006 | 0.03 | 0.12 | - * 2 | 0.49 | 0.86 |>0.93< |>0.96< | - * 3 | 0.91 | 0.97 | 0.98 | 0.999 | - * 4 | 0.97 | 0.99 | 0.999 | | - * - * The number of cells per bucket is chosen such that a bucket fits in one cache - * line. So, on 32- and 64-bit systems, we use (8,2) and (4,2) cuckoo hashing, - * respectively. - * - ******************************************************************************/ -#include "jemalloc/internal/jemalloc_preamble.h" - -#include "jemalloc/internal/ckh.h" - -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/hash.h" -#include "jemalloc/internal/malloc_io.h" -#include "jemalloc/internal/prng.h" -#include "jemalloc/internal/util.h" - -/******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static bool ckh_grow(tsd_t *tsd, ckh_t *ckh); -static void ckh_shrink(tsd_t *tsd, ckh_t *ckh); - -/******************************************************************************/ - -/* - * Search bucket for key and return the cell number if found; SIZE_T_MAX - * otherwise. - */ -static size_t -ckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key) { - ckhc_t *cell; - unsigned i; - - for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) { - cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i]; - if (cell->key != NULL && ckh->keycomp(key, cell->key)) { - return (bucket << LG_CKH_BUCKET_CELLS) + i; - } - } - - return SIZE_T_MAX; -} - -/* - * Search table for key and return cell number if found; SIZE_T_MAX otherwise. - */ -static size_t -ckh_isearch(ckh_t *ckh, const void *key) { - size_t hashes[2], bucket, cell; - - assert(ckh != NULL); - - ckh->hash(key, hashes); - - /* Search primary bucket. */ - bucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1); - cell = ckh_bucket_search(ckh, bucket, key); - if (cell != SIZE_T_MAX) { - return cell; - } - - /* Search secondary bucket. */ - bucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1); - cell = ckh_bucket_search(ckh, bucket, key); - return cell; -} - -static bool -ckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key, - const void *data) { - ckhc_t *cell; - unsigned offset, i; - - /* - * Cycle through the cells in the bucket, starting at a random position. - * The randomness avoids worst-case search overhead as buckets fill up. - */ - offset = (unsigned)prng_lg_range_u64(&ckh->prng_state, - LG_CKH_BUCKET_CELLS); - for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) { - cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + - ((i + offset) & ((ZU(1) << LG_CKH_BUCKET_CELLS) - 1))]; - if (cell->key == NULL) { - cell->key = key; - cell->data = data; - ckh->count++; - return false; - } - } - - return true; -} - -/* - * No space is available in bucket. Randomly evict an item, then try to find an - * alternate location for that item. Iteratively repeat this - * eviction/relocation procedure until either success or detection of an - * eviction/relocation bucket cycle. - */ -static bool -ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey, - void const **argdata) { - const void *key, *data, *tkey, *tdata; - ckhc_t *cell; - size_t hashes[2], bucket, tbucket; - unsigned i; - - bucket = argbucket; - key = *argkey; - data = *argdata; - while (true) { - /* - * Choose a random item within the bucket to evict. This is - * critical to correct function, because without (eventually) - * evicting all items within a bucket during iteration, it - * would be possible to get stuck in an infinite loop if there - * were an item for which both hashes indicated the same - * bucket. - */ - i = (unsigned)prng_lg_range_u64(&ckh->prng_state, - LG_CKH_BUCKET_CELLS); - cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i]; - assert(cell->key != NULL); - - /* Swap cell->{key,data} and {key,data} (evict). */ - tkey = cell->key; tdata = cell->data; - cell->key = key; cell->data = data; - key = tkey; data = tdata; - -#ifdef CKH_COUNT - ckh->nrelocs++; -#endif - - /* Find the alternate bucket for the evicted item. */ - ckh->hash(key, hashes); - tbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1); - if (tbucket == bucket) { - tbucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - - 1); - /* - * It may be that (tbucket == bucket) still, if the - * item's hashes both indicate this bucket. However, - * we are guaranteed to eventually escape this bucket - * during iteration, assuming pseudo-random item - * selection (true randomness would make infinite - * looping a remote possibility). The reason we can - * never get trapped forever is that there are two - * cases: - * - * 1) This bucket == argbucket, so we will quickly - * detect an eviction cycle and terminate. - * 2) An item was evicted to this bucket from another, - * which means that at least one item in this bucket - * has hashes that indicate distinct buckets. - */ - } - /* Check for a cycle. */ - if (tbucket == argbucket) { - *argkey = key; - *argdata = data; - return true; - } - - bucket = tbucket; - if (!ckh_try_bucket_insert(ckh, bucket, key, data)) { - return false; - } - } -} - -static bool -ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) { - size_t hashes[2], bucket; - const void *key = *argkey; - const void *data = *argdata; - - ckh->hash(key, hashes); - - /* Try to insert in primary bucket. */ - bucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1); - if (!ckh_try_bucket_insert(ckh, bucket, key, data)) { - return false; - } - - /* Try to insert in secondary bucket. */ - bucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1); - if (!ckh_try_bucket_insert(ckh, bucket, key, data)) { - return false; - } - - /* - * Try to find a place for this item via iterative eviction/relocation. - */ - return ckh_evict_reloc_insert(ckh, bucket, argkey, argdata); -} - -/* - * Try to rebuild the hash table from scratch by inserting all items from the - * old table into the new. - */ -static bool -ckh_rebuild(ckh_t *ckh, ckhc_t *aTab) { - size_t count, i, nins; - const void *key, *data; - - count = ckh->count; - ckh->count = 0; - for (i = nins = 0; nins < count; i++) { - if (aTab[i].key != NULL) { - key = aTab[i].key; - data = aTab[i].data; - if (ckh_try_insert(ckh, &key, &data)) { - ckh->count = count; - return true; - } - nins++; - } - } - - return false; -} - -static bool -ckh_grow(tsd_t *tsd, ckh_t *ckh) { - bool ret; - ckhc_t *tab, *ttab; - unsigned lg_prevbuckets, lg_curcells; - -#ifdef CKH_COUNT - ckh->ngrows++; -#endif - - /* - * It is possible (though unlikely, given well behaved hashes) that the - * table will have to be doubled more than once in order to create a - * usable table. - */ - lg_prevbuckets = ckh->lg_curbuckets; - lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS; - while (true) { - size_t usize; - - lg_curcells++; - usize = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE); - if (unlikely(usize == 0 - || usize > SC_LARGE_MAXCLASS)) { - ret = true; - goto label_return; - } - tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, - true, NULL, true, arena_ichoose(tsd, NULL)); - if (tab == NULL) { - ret = true; - goto label_return; - } - /* Swap in new table. */ - ttab = ckh->tab; - ckh->tab = tab; - tab = ttab; - ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS; - - if (!ckh_rebuild(ckh, tab)) { - idalloctm(tsd_tsdn(tsd), tab, NULL, NULL, true, true); - break; - } - - /* Rebuilding failed, so back out partially rebuilt table. */ - idalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true); - ckh->tab = tab; - ckh->lg_curbuckets = lg_prevbuckets; - } - - ret = false; -label_return: - return ret; -} - -static void -ckh_shrink(tsd_t *tsd, ckh_t *ckh) { - ckhc_t *tab, *ttab; - size_t usize; - unsigned lg_prevbuckets, lg_curcells; - - /* - * It is possible (though unlikely, given well behaved hashes) that the - * table rebuild will fail. - */ - lg_prevbuckets = ckh->lg_curbuckets; - lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS - 1; - usize = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE); - if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) { - return; - } - tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true, NULL, - true, arena_ichoose(tsd, NULL)); - if (tab == NULL) { - /* - * An OOM error isn't worth propagating, since it doesn't - * prevent this or future operations from proceeding. - */ - return; - } - /* Swap in new table. */ - ttab = ckh->tab; - ckh->tab = tab; - tab = ttab; - ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS; - - if (!ckh_rebuild(ckh, tab)) { - idalloctm(tsd_tsdn(tsd), tab, NULL, NULL, true, true); -#ifdef CKH_COUNT - ckh->nshrinks++; -#endif - return; - } - - /* Rebuilding failed, so back out partially rebuilt table. */ - idalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true); - ckh->tab = tab; - ckh->lg_curbuckets = lg_prevbuckets; -#ifdef CKH_COUNT - ckh->nshrinkfails++; -#endif -} - -bool -ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *ckh_hash, - ckh_keycomp_t *keycomp) { - bool ret; - size_t mincells, usize; - unsigned lg_mincells; - - assert(minitems > 0); - assert(ckh_hash != NULL); - assert(keycomp != NULL); - -#ifdef CKH_COUNT - ckh->ngrows = 0; - ckh->nshrinks = 0; - ckh->nshrinkfails = 0; - ckh->ninserts = 0; - ckh->nrelocs = 0; -#endif - ckh->prng_state = 42; /* Value doesn't really matter. */ - ckh->count = 0; - - /* - * Find the minimum power of 2 that is large enough to fit minitems - * entries. We are using (2+,2) cuckoo hashing, which has an expected - * maximum load factor of at least ~0.86, so 0.75 is a conservative load - * factor that will typically allow mincells items to fit without ever - * growing the table. - */ - assert(LG_CKH_BUCKET_CELLS > 0); - mincells = ((minitems + (3 - (minitems % 3))) / 3) << 2; - for (lg_mincells = LG_CKH_BUCKET_CELLS; - (ZU(1) << lg_mincells) < mincells; - lg_mincells++) { - /* Do nothing. */ - } - ckh->lg_minbuckets = lg_mincells - LG_CKH_BUCKET_CELLS; - ckh->lg_curbuckets = lg_mincells - LG_CKH_BUCKET_CELLS; - ckh->hash = ckh_hash; - ckh->keycomp = keycomp; - - usize = sz_sa2u(sizeof(ckhc_t) << lg_mincells, CACHELINE); - if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) { - ret = true; - goto label_return; - } - ckh->tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true, - NULL, true, arena_ichoose(tsd, NULL)); - if (ckh->tab == NULL) { - ret = true; - goto label_return; - } - - ret = false; -label_return: - return ret; -} - -void -ckh_delete(tsd_t *tsd, ckh_t *ckh) { - assert(ckh != NULL); - -#ifdef CKH_VERBOSE - malloc_printf( - "%s(%p): ngrows: %"FMTu64", nshrinks: %"FMTu64"," - " nshrinkfails: %"FMTu64", ninserts: %"FMTu64"," - " nrelocs: %"FMTu64"\n", __func__, ckh, - (unsigned long long)ckh->ngrows, - (unsigned long long)ckh->nshrinks, - (unsigned long long)ckh->nshrinkfails, - (unsigned long long)ckh->ninserts, - (unsigned long long)ckh->nrelocs); -#endif - - idalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true); - if (config_debug) { - memset(ckh, JEMALLOC_FREE_JUNK, sizeof(ckh_t)); - } -} - -size_t -ckh_count(ckh_t *ckh) { - assert(ckh != NULL); - - return ckh->count; -} - -bool -ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data) { - size_t i, ncells; - - for (i = *tabind, ncells = (ZU(1) << (ckh->lg_curbuckets + - LG_CKH_BUCKET_CELLS)); i < ncells; i++) { - if (ckh->tab[i].key != NULL) { - if (key != NULL) { - *key = (void *)ckh->tab[i].key; - } - if (data != NULL) { - *data = (void *)ckh->tab[i].data; - } - *tabind = i + 1; - return false; - } - } - - return true; -} - -bool -ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data) { - bool ret; - - assert(ckh != NULL); - assert(ckh_search(ckh, key, NULL, NULL)); - -#ifdef CKH_COUNT - ckh->ninserts++; -#endif - - while (ckh_try_insert(ckh, &key, &data)) { - if (ckh_grow(tsd, ckh)) { - ret = true; - goto label_return; - } - } - - ret = false; -label_return: - return ret; -} - -bool -ckh_remove(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key, - void **data) { - size_t cell; - - assert(ckh != NULL); - - cell = ckh_isearch(ckh, searchkey); - if (cell != SIZE_T_MAX) { - if (key != NULL) { - *key = (void *)ckh->tab[cell].key; - } - if (data != NULL) { - *data = (void *)ckh->tab[cell].data; - } - ckh->tab[cell].key = NULL; - ckh->tab[cell].data = NULL; /* Not necessary. */ - - ckh->count--; - /* Try to halve the table if it is less than 1/4 full. */ - if (ckh->count < (ZU(1) << (ckh->lg_curbuckets - + LG_CKH_BUCKET_CELLS - 2)) && ckh->lg_curbuckets - > ckh->lg_minbuckets) { - /* Ignore error due to OOM. */ - ckh_shrink(tsd, ckh); - } - - return false; - } - - return true; -} - -bool -ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data) { - size_t cell; - - assert(ckh != NULL); - - cell = ckh_isearch(ckh, searchkey); - if (cell != SIZE_T_MAX) { - if (key != NULL) { - *key = (void *)ckh->tab[cell].key; - } - if (data != NULL) { - *data = (void *)ckh->tab[cell].data; - } - return false; - } - - return true; -} - -void -ckh_string_hash(const void *key, size_t r_hash[2]) { - hash(key, strlen((const char *)key), 0x94122f33U, r_hash); -} - -bool -ckh_string_keycomp(const void *k1, const void *k2) { - assert(k1 != NULL); - assert(k2 != NULL); - - return !strcmp((char *)k1, (char *)k2); -} - -void -ckh_pointer_hash(const void *key, size_t r_hash[2]) { - union { - const void *v; - size_t i; - } u; - - assert(sizeof(u.v) == sizeof(u.i)); - u.v = key; - hash(&u.i, sizeof(u.i), 0xd983396eU, r_hash); -} - -bool -ckh_pointer_keycomp(const void *k1, const void *k2) { - return (k1 == k2); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/counter.c b/fluent-bit/lib/jemalloc-5.3.0/src/counter.c deleted file mode 100644 index 8f1ae3af..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/counter.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/counter.h" - -bool -counter_accum_init(counter_accum_t *counter, uint64_t interval) { - if (LOCKEDINT_MTX_INIT(counter->mtx, "counter_accum", - WITNESS_RANK_COUNTER_ACCUM, malloc_mutex_rank_exclusive)) { - return true; - } - locked_init_u64_unsynchronized(&counter->accumbytes, 0); - counter->interval = interval; - return false; -} - -void -counter_prefork(tsdn_t *tsdn, counter_accum_t *counter) { - LOCKEDINT_MTX_PREFORK(tsdn, counter->mtx); -} - -void -counter_postfork_parent(tsdn_t *tsdn, counter_accum_t *counter) { - LOCKEDINT_MTX_POSTFORK_PARENT(tsdn, counter->mtx); -} - -void -counter_postfork_child(tsdn_t *tsdn, counter_accum_t *counter) { - LOCKEDINT_MTX_POSTFORK_CHILD(tsdn, counter->mtx); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/ctl.c b/fluent-bit/lib/jemalloc-5.3.0/src/ctl.c deleted file mode 100644 index 135271ba..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/ctl.c +++ /dev/null @@ -1,4414 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/ctl.h" -#include "jemalloc/internal/extent_dss.h" -#include "jemalloc/internal/extent_mmap.h" -#include "jemalloc/internal/inspect.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/nstime.h" -#include "jemalloc/internal/peak_event.h" -#include "jemalloc/internal/prof_data.h" -#include "jemalloc/internal/prof_log.h" -#include "jemalloc/internal/prof_recent.h" -#include "jemalloc/internal/prof_stats.h" -#include "jemalloc/internal/prof_sys.h" -#include "jemalloc/internal/safety_check.h" -#include "jemalloc/internal/sc.h" -#include "jemalloc/internal/util.h" - -/******************************************************************************/ -/* Data. */ - -/* - * ctl_mtx protects the following: - * - ctl_stats->* - */ -static malloc_mutex_t ctl_mtx; -static bool ctl_initialized; -static ctl_stats_t *ctl_stats; -static ctl_arenas_t *ctl_arenas; - -/******************************************************************************/ -/* Helpers for named and indexed nodes. */ - -static const ctl_named_node_t * -ctl_named_node(const ctl_node_t *node) { - return ((node->named) ? (const ctl_named_node_t *)node : NULL); -} - -static const ctl_named_node_t * -ctl_named_children(const ctl_named_node_t *node, size_t index) { - const ctl_named_node_t *children = ctl_named_node(node->children); - - return (children ? &children[index] : NULL); -} - -static const ctl_indexed_node_t * -ctl_indexed_node(const ctl_node_t *node) { - return (!node->named ? (const ctl_indexed_node_t *)node : NULL); -} - -/******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -#define CTL_PROTO(n) \ -static int n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \ - void *oldp, size_t *oldlenp, void *newp, size_t newlen); - -#define INDEX_PROTO(n) \ -static const ctl_named_node_t *n##_index(tsdn_t *tsdn, \ - const size_t *mib, size_t miblen, size_t i); - -CTL_PROTO(version) -CTL_PROTO(epoch) -CTL_PROTO(background_thread) -CTL_PROTO(max_background_threads) -CTL_PROTO(thread_tcache_enabled) -CTL_PROTO(thread_tcache_flush) -CTL_PROTO(thread_peak_read) -CTL_PROTO(thread_peak_reset) -CTL_PROTO(thread_prof_name) -CTL_PROTO(thread_prof_active) -CTL_PROTO(thread_arena) -CTL_PROTO(thread_allocated) -CTL_PROTO(thread_allocatedp) -CTL_PROTO(thread_deallocated) -CTL_PROTO(thread_deallocatedp) -CTL_PROTO(thread_idle) -CTL_PROTO(config_cache_oblivious) -CTL_PROTO(config_debug) -CTL_PROTO(config_fill) -CTL_PROTO(config_lazy_lock) -CTL_PROTO(config_malloc_conf) -CTL_PROTO(config_opt_safety_checks) -CTL_PROTO(config_prof) -CTL_PROTO(config_prof_libgcc) -CTL_PROTO(config_prof_libunwind) -CTL_PROTO(config_stats) -CTL_PROTO(config_utrace) -CTL_PROTO(config_xmalloc) -CTL_PROTO(opt_abort) -CTL_PROTO(opt_abort_conf) -CTL_PROTO(opt_cache_oblivious) -CTL_PROTO(opt_trust_madvise) -CTL_PROTO(opt_confirm_conf) -CTL_PROTO(opt_hpa) -CTL_PROTO(opt_hpa_slab_max_alloc) -CTL_PROTO(opt_hpa_hugification_threshold) -CTL_PROTO(opt_hpa_hugify_delay_ms) -CTL_PROTO(opt_hpa_min_purge_interval_ms) -CTL_PROTO(opt_hpa_dirty_mult) -CTL_PROTO(opt_hpa_sec_nshards) -CTL_PROTO(opt_hpa_sec_max_alloc) -CTL_PROTO(opt_hpa_sec_max_bytes) -CTL_PROTO(opt_hpa_sec_bytes_after_flush) -CTL_PROTO(opt_hpa_sec_batch_fill_extra) -CTL_PROTO(opt_metadata_thp) -CTL_PROTO(opt_retain) -CTL_PROTO(opt_dss) -CTL_PROTO(opt_narenas) -CTL_PROTO(opt_percpu_arena) -CTL_PROTO(opt_oversize_threshold) -CTL_PROTO(opt_background_thread) -CTL_PROTO(opt_mutex_max_spin) -CTL_PROTO(opt_max_background_threads) -CTL_PROTO(opt_dirty_decay_ms) -CTL_PROTO(opt_muzzy_decay_ms) -CTL_PROTO(opt_stats_print) -CTL_PROTO(opt_stats_print_opts) -CTL_PROTO(opt_stats_interval) -CTL_PROTO(opt_stats_interval_opts) -CTL_PROTO(opt_junk) -CTL_PROTO(opt_zero) -CTL_PROTO(opt_utrace) -CTL_PROTO(opt_xmalloc) -CTL_PROTO(opt_experimental_infallible_new) -CTL_PROTO(opt_tcache) -CTL_PROTO(opt_tcache_max) -CTL_PROTO(opt_tcache_nslots_small_min) -CTL_PROTO(opt_tcache_nslots_small_max) -CTL_PROTO(opt_tcache_nslots_large) -CTL_PROTO(opt_lg_tcache_nslots_mul) -CTL_PROTO(opt_tcache_gc_incr_bytes) -CTL_PROTO(opt_tcache_gc_delay_bytes) -CTL_PROTO(opt_lg_tcache_flush_small_div) -CTL_PROTO(opt_lg_tcache_flush_large_div) -CTL_PROTO(opt_thp) -CTL_PROTO(opt_lg_extent_max_active_fit) -CTL_PROTO(opt_prof) -CTL_PROTO(opt_prof_prefix) -CTL_PROTO(opt_prof_active) -CTL_PROTO(opt_prof_thread_active_init) -CTL_PROTO(opt_lg_prof_sample) -CTL_PROTO(opt_lg_prof_interval) -CTL_PROTO(opt_prof_gdump) -CTL_PROTO(opt_prof_final) -CTL_PROTO(opt_prof_leak) -CTL_PROTO(opt_prof_leak_error) -CTL_PROTO(opt_prof_accum) -CTL_PROTO(opt_prof_recent_alloc_max) -CTL_PROTO(opt_prof_stats) -CTL_PROTO(opt_prof_sys_thread_name) -CTL_PROTO(opt_prof_time_res) -CTL_PROTO(opt_lg_san_uaf_align) -CTL_PROTO(opt_zero_realloc) -CTL_PROTO(tcache_create) -CTL_PROTO(tcache_flush) -CTL_PROTO(tcache_destroy) -CTL_PROTO(arena_i_initialized) -CTL_PROTO(arena_i_decay) -CTL_PROTO(arena_i_purge) -CTL_PROTO(arena_i_reset) -CTL_PROTO(arena_i_destroy) -CTL_PROTO(arena_i_dss) -CTL_PROTO(arena_i_oversize_threshold) -CTL_PROTO(arena_i_dirty_decay_ms) -CTL_PROTO(arena_i_muzzy_decay_ms) -CTL_PROTO(arena_i_extent_hooks) -CTL_PROTO(arena_i_retain_grow_limit) -INDEX_PROTO(arena_i) -CTL_PROTO(arenas_bin_i_size) -CTL_PROTO(arenas_bin_i_nregs) -CTL_PROTO(arenas_bin_i_slab_size) -CTL_PROTO(arenas_bin_i_nshards) -INDEX_PROTO(arenas_bin_i) -CTL_PROTO(arenas_lextent_i_size) -INDEX_PROTO(arenas_lextent_i) -CTL_PROTO(arenas_narenas) -CTL_PROTO(arenas_dirty_decay_ms) -CTL_PROTO(arenas_muzzy_decay_ms) -CTL_PROTO(arenas_quantum) -CTL_PROTO(arenas_page) -CTL_PROTO(arenas_tcache_max) -CTL_PROTO(arenas_nbins) -CTL_PROTO(arenas_nhbins) -CTL_PROTO(arenas_nlextents) -CTL_PROTO(arenas_create) -CTL_PROTO(arenas_lookup) -CTL_PROTO(prof_thread_active_init) -CTL_PROTO(prof_active) -CTL_PROTO(prof_dump) -CTL_PROTO(prof_gdump) -CTL_PROTO(prof_prefix) -CTL_PROTO(prof_reset) -CTL_PROTO(prof_interval) -CTL_PROTO(lg_prof_sample) -CTL_PROTO(prof_log_start) -CTL_PROTO(prof_log_stop) -CTL_PROTO(prof_stats_bins_i_live) -CTL_PROTO(prof_stats_bins_i_accum) -INDEX_PROTO(prof_stats_bins_i) -CTL_PROTO(prof_stats_lextents_i_live) -CTL_PROTO(prof_stats_lextents_i_accum) -INDEX_PROTO(prof_stats_lextents_i) -CTL_PROTO(stats_arenas_i_small_allocated) -CTL_PROTO(stats_arenas_i_small_nmalloc) -CTL_PROTO(stats_arenas_i_small_ndalloc) -CTL_PROTO(stats_arenas_i_small_nrequests) -CTL_PROTO(stats_arenas_i_small_nfills) -CTL_PROTO(stats_arenas_i_small_nflushes) -CTL_PROTO(stats_arenas_i_large_allocated) -CTL_PROTO(stats_arenas_i_large_nmalloc) -CTL_PROTO(stats_arenas_i_large_ndalloc) -CTL_PROTO(stats_arenas_i_large_nrequests) -CTL_PROTO(stats_arenas_i_large_nfills) -CTL_PROTO(stats_arenas_i_large_nflushes) -CTL_PROTO(stats_arenas_i_bins_j_nmalloc) -CTL_PROTO(stats_arenas_i_bins_j_ndalloc) -CTL_PROTO(stats_arenas_i_bins_j_nrequests) -CTL_PROTO(stats_arenas_i_bins_j_curregs) -CTL_PROTO(stats_arenas_i_bins_j_nfills) -CTL_PROTO(stats_arenas_i_bins_j_nflushes) -CTL_PROTO(stats_arenas_i_bins_j_nslabs) -CTL_PROTO(stats_arenas_i_bins_j_nreslabs) -CTL_PROTO(stats_arenas_i_bins_j_curslabs) -CTL_PROTO(stats_arenas_i_bins_j_nonfull_slabs) -INDEX_PROTO(stats_arenas_i_bins_j) -CTL_PROTO(stats_arenas_i_lextents_j_nmalloc) -CTL_PROTO(stats_arenas_i_lextents_j_ndalloc) -CTL_PROTO(stats_arenas_i_lextents_j_nrequests) -CTL_PROTO(stats_arenas_i_lextents_j_curlextents) -INDEX_PROTO(stats_arenas_i_lextents_j) -CTL_PROTO(stats_arenas_i_extents_j_ndirty) -CTL_PROTO(stats_arenas_i_extents_j_nmuzzy) -CTL_PROTO(stats_arenas_i_extents_j_nretained) -CTL_PROTO(stats_arenas_i_extents_j_dirty_bytes) -CTL_PROTO(stats_arenas_i_extents_j_muzzy_bytes) -CTL_PROTO(stats_arenas_i_extents_j_retained_bytes) -INDEX_PROTO(stats_arenas_i_extents_j) -CTL_PROTO(stats_arenas_i_hpa_shard_npurge_passes) -CTL_PROTO(stats_arenas_i_hpa_shard_npurges) -CTL_PROTO(stats_arenas_i_hpa_shard_nhugifies) -CTL_PROTO(stats_arenas_i_hpa_shard_ndehugifies) - -/* We have a set of stats for full slabs. */ -CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_npageslabs_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_npageslabs_huge) -CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_nactive_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_nactive_huge) -CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_ndirty_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_ndirty_huge) - -/* A parallel set for the empty slabs. */ -CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_huge) -CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_nactive_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_nactive_huge) -CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_ndirty_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_ndirty_huge) - -/* - * And one for the slabs that are neither empty nor full, but indexed by how - * full they are. - */ -CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_huge) -CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_huge) -CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_nonhuge) -CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_huge) - -INDEX_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j) -CTL_PROTO(stats_arenas_i_nthreads) -CTL_PROTO(stats_arenas_i_uptime) -CTL_PROTO(stats_arenas_i_dss) -CTL_PROTO(stats_arenas_i_dirty_decay_ms) -CTL_PROTO(stats_arenas_i_muzzy_decay_ms) -CTL_PROTO(stats_arenas_i_pactive) -CTL_PROTO(stats_arenas_i_pdirty) -CTL_PROTO(stats_arenas_i_pmuzzy) -CTL_PROTO(stats_arenas_i_mapped) -CTL_PROTO(stats_arenas_i_retained) -CTL_PROTO(stats_arenas_i_extent_avail) -CTL_PROTO(stats_arenas_i_dirty_npurge) -CTL_PROTO(stats_arenas_i_dirty_nmadvise) -CTL_PROTO(stats_arenas_i_dirty_purged) -CTL_PROTO(stats_arenas_i_muzzy_npurge) -CTL_PROTO(stats_arenas_i_muzzy_nmadvise) -CTL_PROTO(stats_arenas_i_muzzy_purged) -CTL_PROTO(stats_arenas_i_base) -CTL_PROTO(stats_arenas_i_internal) -CTL_PROTO(stats_arenas_i_metadata_thp) -CTL_PROTO(stats_arenas_i_tcache_bytes) -CTL_PROTO(stats_arenas_i_tcache_stashed_bytes) -CTL_PROTO(stats_arenas_i_resident) -CTL_PROTO(stats_arenas_i_abandoned_vm) -CTL_PROTO(stats_arenas_i_hpa_sec_bytes) -INDEX_PROTO(stats_arenas_i) -CTL_PROTO(stats_allocated) -CTL_PROTO(stats_active) -CTL_PROTO(stats_background_thread_num_threads) -CTL_PROTO(stats_background_thread_num_runs) -CTL_PROTO(stats_background_thread_run_interval) -CTL_PROTO(stats_metadata) -CTL_PROTO(stats_metadata_thp) -CTL_PROTO(stats_resident) -CTL_PROTO(stats_mapped) -CTL_PROTO(stats_retained) -CTL_PROTO(stats_zero_reallocs) -CTL_PROTO(experimental_hooks_install) -CTL_PROTO(experimental_hooks_remove) -CTL_PROTO(experimental_hooks_prof_backtrace) -CTL_PROTO(experimental_hooks_prof_dump) -CTL_PROTO(experimental_hooks_safety_check_abort) -CTL_PROTO(experimental_thread_activity_callback) -CTL_PROTO(experimental_utilization_query) -CTL_PROTO(experimental_utilization_batch_query) -CTL_PROTO(experimental_arenas_i_pactivep) -INDEX_PROTO(experimental_arenas_i) -CTL_PROTO(experimental_prof_recent_alloc_max) -CTL_PROTO(experimental_prof_recent_alloc_dump) -CTL_PROTO(experimental_batch_alloc) -CTL_PROTO(experimental_arenas_create_ext) - -#define MUTEX_STATS_CTL_PROTO_GEN(n) \ -CTL_PROTO(stats_##n##_num_ops) \ -CTL_PROTO(stats_##n##_num_wait) \ -CTL_PROTO(stats_##n##_num_spin_acq) \ -CTL_PROTO(stats_##n##_num_owner_switch) \ -CTL_PROTO(stats_##n##_total_wait_time) \ -CTL_PROTO(stats_##n##_max_wait_time) \ -CTL_PROTO(stats_##n##_max_num_thds) - -/* Global mutexes. */ -#define OP(mtx) MUTEX_STATS_CTL_PROTO_GEN(mutexes_##mtx) -MUTEX_PROF_GLOBAL_MUTEXES -#undef OP - -/* Per arena mutexes. */ -#define OP(mtx) MUTEX_STATS_CTL_PROTO_GEN(arenas_i_mutexes_##mtx) -MUTEX_PROF_ARENA_MUTEXES -#undef OP - -/* Arena bin mutexes. */ -MUTEX_STATS_CTL_PROTO_GEN(arenas_i_bins_j_mutex) -#undef MUTEX_STATS_CTL_PROTO_GEN - -CTL_PROTO(stats_mutexes_reset) - -/******************************************************************************/ -/* mallctl tree. */ - -#define NAME(n) {true}, n -#define CHILD(t, c) \ - sizeof(c##_node) / sizeof(ctl_##t##_node_t), \ - (ctl_node_t *)c##_node, \ - NULL -#define CTL(c) 0, NULL, c##_ctl - -/* - * Only handles internal indexed nodes, since there are currently no external - * ones. - */ -#define INDEX(i) {false}, i##_index - -static const ctl_named_node_t thread_tcache_node[] = { - {NAME("enabled"), CTL(thread_tcache_enabled)}, - {NAME("flush"), CTL(thread_tcache_flush)} -}; - -static const ctl_named_node_t thread_peak_node[] = { - {NAME("read"), CTL(thread_peak_read)}, - {NAME("reset"), CTL(thread_peak_reset)}, -}; - -static const ctl_named_node_t thread_prof_node[] = { - {NAME("name"), CTL(thread_prof_name)}, - {NAME("active"), CTL(thread_prof_active)} -}; - -static const ctl_named_node_t thread_node[] = { - {NAME("arena"), CTL(thread_arena)}, - {NAME("allocated"), CTL(thread_allocated)}, - {NAME("allocatedp"), CTL(thread_allocatedp)}, - {NAME("deallocated"), CTL(thread_deallocated)}, - {NAME("deallocatedp"), CTL(thread_deallocatedp)}, - {NAME("tcache"), CHILD(named, thread_tcache)}, - {NAME("peak"), CHILD(named, thread_peak)}, - {NAME("prof"), CHILD(named, thread_prof)}, - {NAME("idle"), CTL(thread_idle)} -}; - -static const ctl_named_node_t config_node[] = { - {NAME("cache_oblivious"), CTL(config_cache_oblivious)}, - {NAME("debug"), CTL(config_debug)}, - {NAME("fill"), CTL(config_fill)}, - {NAME("lazy_lock"), CTL(config_lazy_lock)}, - {NAME("malloc_conf"), CTL(config_malloc_conf)}, - {NAME("opt_safety_checks"), CTL(config_opt_safety_checks)}, - {NAME("prof"), CTL(config_prof)}, - {NAME("prof_libgcc"), CTL(config_prof_libgcc)}, - {NAME("prof_libunwind"), CTL(config_prof_libunwind)}, - {NAME("stats"), CTL(config_stats)}, - {NAME("utrace"), CTL(config_utrace)}, - {NAME("xmalloc"), CTL(config_xmalloc)} -}; - -static const ctl_named_node_t opt_node[] = { - {NAME("abort"), CTL(opt_abort)}, - {NAME("abort_conf"), CTL(opt_abort_conf)}, - {NAME("cache_oblivious"), CTL(opt_cache_oblivious)}, - {NAME("trust_madvise"), CTL(opt_trust_madvise)}, - {NAME("confirm_conf"), CTL(opt_confirm_conf)}, - {NAME("hpa"), CTL(opt_hpa)}, - {NAME("hpa_slab_max_alloc"), CTL(opt_hpa_slab_max_alloc)}, - {NAME("hpa_hugification_threshold"), - CTL(opt_hpa_hugification_threshold)}, - {NAME("hpa_hugify_delay_ms"), CTL(opt_hpa_hugify_delay_ms)}, - {NAME("hpa_min_purge_interval_ms"), CTL(opt_hpa_min_purge_interval_ms)}, - {NAME("hpa_dirty_mult"), CTL(opt_hpa_dirty_mult)}, - {NAME("hpa_sec_nshards"), CTL(opt_hpa_sec_nshards)}, - {NAME("hpa_sec_max_alloc"), CTL(opt_hpa_sec_max_alloc)}, - {NAME("hpa_sec_max_bytes"), CTL(opt_hpa_sec_max_bytes)}, - {NAME("hpa_sec_bytes_after_flush"), - CTL(opt_hpa_sec_bytes_after_flush)}, - {NAME("hpa_sec_batch_fill_extra"), - CTL(opt_hpa_sec_batch_fill_extra)}, - {NAME("metadata_thp"), CTL(opt_metadata_thp)}, - {NAME("retain"), CTL(opt_retain)}, - {NAME("dss"), CTL(opt_dss)}, - {NAME("narenas"), CTL(opt_narenas)}, - {NAME("percpu_arena"), CTL(opt_percpu_arena)}, - {NAME("oversize_threshold"), CTL(opt_oversize_threshold)}, - {NAME("mutex_max_spin"), CTL(opt_mutex_max_spin)}, - {NAME("background_thread"), CTL(opt_background_thread)}, - {NAME("max_background_threads"), CTL(opt_max_background_threads)}, - {NAME("dirty_decay_ms"), CTL(opt_dirty_decay_ms)}, - {NAME("muzzy_decay_ms"), CTL(opt_muzzy_decay_ms)}, - {NAME("stats_print"), CTL(opt_stats_print)}, - {NAME("stats_print_opts"), CTL(opt_stats_print_opts)}, - {NAME("stats_interval"), CTL(opt_stats_interval)}, - {NAME("stats_interval_opts"), CTL(opt_stats_interval_opts)}, - {NAME("junk"), CTL(opt_junk)}, - {NAME("zero"), CTL(opt_zero)}, - {NAME("utrace"), CTL(opt_utrace)}, - {NAME("xmalloc"), CTL(opt_xmalloc)}, - {NAME("experimental_infallible_new"), - CTL(opt_experimental_infallible_new)}, - {NAME("tcache"), CTL(opt_tcache)}, - {NAME("tcache_max"), CTL(opt_tcache_max)}, - {NAME("tcache_nslots_small_min"), - CTL(opt_tcache_nslots_small_min)}, - {NAME("tcache_nslots_small_max"), - CTL(opt_tcache_nslots_small_max)}, - {NAME("tcache_nslots_large"), CTL(opt_tcache_nslots_large)}, - {NAME("lg_tcache_nslots_mul"), CTL(opt_lg_tcache_nslots_mul)}, - {NAME("tcache_gc_incr_bytes"), CTL(opt_tcache_gc_incr_bytes)}, - {NAME("tcache_gc_delay_bytes"), CTL(opt_tcache_gc_delay_bytes)}, - {NAME("lg_tcache_flush_small_div"), - CTL(opt_lg_tcache_flush_small_div)}, - {NAME("lg_tcache_flush_large_div"), - CTL(opt_lg_tcache_flush_large_div)}, - {NAME("thp"), CTL(opt_thp)}, - {NAME("lg_extent_max_active_fit"), CTL(opt_lg_extent_max_active_fit)}, - {NAME("prof"), CTL(opt_prof)}, - {NAME("prof_prefix"), CTL(opt_prof_prefix)}, - {NAME("prof_active"), CTL(opt_prof_active)}, - {NAME("prof_thread_active_init"), CTL(opt_prof_thread_active_init)}, - {NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)}, - {NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)}, - {NAME("prof_gdump"), CTL(opt_prof_gdump)}, - {NAME("prof_final"), CTL(opt_prof_final)}, - {NAME("prof_leak"), CTL(opt_prof_leak)}, - {NAME("prof_leak_error"), CTL(opt_prof_leak_error)}, - {NAME("prof_accum"), CTL(opt_prof_accum)}, - {NAME("prof_recent_alloc_max"), CTL(opt_prof_recent_alloc_max)}, - {NAME("prof_stats"), CTL(opt_prof_stats)}, - {NAME("prof_sys_thread_name"), CTL(opt_prof_sys_thread_name)}, - {NAME("prof_time_resolution"), CTL(opt_prof_time_res)}, - {NAME("lg_san_uaf_align"), CTL(opt_lg_san_uaf_align)}, - {NAME("zero_realloc"), CTL(opt_zero_realloc)} -}; - -static const ctl_named_node_t tcache_node[] = { - {NAME("create"), CTL(tcache_create)}, - {NAME("flush"), CTL(tcache_flush)}, - {NAME("destroy"), CTL(tcache_destroy)} -}; - -static const ctl_named_node_t arena_i_node[] = { - {NAME("initialized"), CTL(arena_i_initialized)}, - {NAME("decay"), CTL(arena_i_decay)}, - {NAME("purge"), CTL(arena_i_purge)}, - {NAME("reset"), CTL(arena_i_reset)}, - {NAME("destroy"), CTL(arena_i_destroy)}, - {NAME("dss"), CTL(arena_i_dss)}, - /* - * Undocumented for now, since we anticipate an arena API in flux after - * we cut the last 5-series release. - */ - {NAME("oversize_threshold"), CTL(arena_i_oversize_threshold)}, - {NAME("dirty_decay_ms"), CTL(arena_i_dirty_decay_ms)}, - {NAME("muzzy_decay_ms"), CTL(arena_i_muzzy_decay_ms)}, - {NAME("extent_hooks"), CTL(arena_i_extent_hooks)}, - {NAME("retain_grow_limit"), CTL(arena_i_retain_grow_limit)} -}; -static const ctl_named_node_t super_arena_i_node[] = { - {NAME(""), CHILD(named, arena_i)} -}; - -static const ctl_indexed_node_t arena_node[] = { - {INDEX(arena_i)} -}; - -static const ctl_named_node_t arenas_bin_i_node[] = { - {NAME("size"), CTL(arenas_bin_i_size)}, - {NAME("nregs"), CTL(arenas_bin_i_nregs)}, - {NAME("slab_size"), CTL(arenas_bin_i_slab_size)}, - {NAME("nshards"), CTL(arenas_bin_i_nshards)} -}; -static const ctl_named_node_t super_arenas_bin_i_node[] = { - {NAME(""), CHILD(named, arenas_bin_i)} -}; - -static const ctl_indexed_node_t arenas_bin_node[] = { - {INDEX(arenas_bin_i)} -}; - -static const ctl_named_node_t arenas_lextent_i_node[] = { - {NAME("size"), CTL(arenas_lextent_i_size)} -}; -static const ctl_named_node_t super_arenas_lextent_i_node[] = { - {NAME(""), CHILD(named, arenas_lextent_i)} -}; - -static const ctl_indexed_node_t arenas_lextent_node[] = { - {INDEX(arenas_lextent_i)} -}; - -static const ctl_named_node_t arenas_node[] = { - {NAME("narenas"), CTL(arenas_narenas)}, - {NAME("dirty_decay_ms"), CTL(arenas_dirty_decay_ms)}, - {NAME("muzzy_decay_ms"), CTL(arenas_muzzy_decay_ms)}, - {NAME("quantum"), CTL(arenas_quantum)}, - {NAME("page"), CTL(arenas_page)}, - {NAME("tcache_max"), CTL(arenas_tcache_max)}, - {NAME("nbins"), CTL(arenas_nbins)}, - {NAME("nhbins"), CTL(arenas_nhbins)}, - {NAME("bin"), CHILD(indexed, arenas_bin)}, - {NAME("nlextents"), CTL(arenas_nlextents)}, - {NAME("lextent"), CHILD(indexed, arenas_lextent)}, - {NAME("create"), CTL(arenas_create)}, - {NAME("lookup"), CTL(arenas_lookup)} -}; - -static const ctl_named_node_t prof_stats_bins_i_node[] = { - {NAME("live"), CTL(prof_stats_bins_i_live)}, - {NAME("accum"), CTL(prof_stats_bins_i_accum)} -}; - -static const ctl_named_node_t super_prof_stats_bins_i_node[] = { - {NAME(""), CHILD(named, prof_stats_bins_i)} -}; - -static const ctl_indexed_node_t prof_stats_bins_node[] = { - {INDEX(prof_stats_bins_i)} -}; - -static const ctl_named_node_t prof_stats_lextents_i_node[] = { - {NAME("live"), CTL(prof_stats_lextents_i_live)}, - {NAME("accum"), CTL(prof_stats_lextents_i_accum)} -}; - -static const ctl_named_node_t super_prof_stats_lextents_i_node[] = { - {NAME(""), CHILD(named, prof_stats_lextents_i)} -}; - -static const ctl_indexed_node_t prof_stats_lextents_node[] = { - {INDEX(prof_stats_lextents_i)} -}; - -static const ctl_named_node_t prof_stats_node[] = { - {NAME("bins"), CHILD(indexed, prof_stats_bins)}, - {NAME("lextents"), CHILD(indexed, prof_stats_lextents)}, -}; - -static const ctl_named_node_t prof_node[] = { - {NAME("thread_active_init"), CTL(prof_thread_active_init)}, - {NAME("active"), CTL(prof_active)}, - {NAME("dump"), CTL(prof_dump)}, - {NAME("gdump"), CTL(prof_gdump)}, - {NAME("prefix"), CTL(prof_prefix)}, - {NAME("reset"), CTL(prof_reset)}, - {NAME("interval"), CTL(prof_interval)}, - {NAME("lg_sample"), CTL(lg_prof_sample)}, - {NAME("log_start"), CTL(prof_log_start)}, - {NAME("log_stop"), CTL(prof_log_stop)}, - {NAME("stats"), CHILD(named, prof_stats)} -}; - -static const ctl_named_node_t stats_arenas_i_small_node[] = { - {NAME("allocated"), CTL(stats_arenas_i_small_allocated)}, - {NAME("nmalloc"), CTL(stats_arenas_i_small_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_small_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_small_nrequests)}, - {NAME("nfills"), CTL(stats_arenas_i_small_nfills)}, - {NAME("nflushes"), CTL(stats_arenas_i_small_nflushes)} -}; - -static const ctl_named_node_t stats_arenas_i_large_node[] = { - {NAME("allocated"), CTL(stats_arenas_i_large_allocated)}, - {NAME("nmalloc"), CTL(stats_arenas_i_large_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_large_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_large_nrequests)}, - {NAME("nfills"), CTL(stats_arenas_i_large_nfills)}, - {NAME("nflushes"), CTL(stats_arenas_i_large_nflushes)} -}; - -#define MUTEX_PROF_DATA_NODE(prefix) \ -static const ctl_named_node_t stats_##prefix##_node[] = { \ - {NAME("num_ops"), \ - CTL(stats_##prefix##_num_ops)}, \ - {NAME("num_wait"), \ - CTL(stats_##prefix##_num_wait)}, \ - {NAME("num_spin_acq"), \ - CTL(stats_##prefix##_num_spin_acq)}, \ - {NAME("num_owner_switch"), \ - CTL(stats_##prefix##_num_owner_switch)}, \ - {NAME("total_wait_time"), \ - CTL(stats_##prefix##_total_wait_time)}, \ - {NAME("max_wait_time"), \ - CTL(stats_##prefix##_max_wait_time)}, \ - {NAME("max_num_thds"), \ - CTL(stats_##prefix##_max_num_thds)} \ - /* Note that # of current waiting thread not provided. */ \ -}; - -MUTEX_PROF_DATA_NODE(arenas_i_bins_j_mutex) - -static const ctl_named_node_t stats_arenas_i_bins_j_node[] = { - {NAME("nmalloc"), CTL(stats_arenas_i_bins_j_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_bins_j_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_bins_j_nrequests)}, - {NAME("curregs"), CTL(stats_arenas_i_bins_j_curregs)}, - {NAME("nfills"), CTL(stats_arenas_i_bins_j_nfills)}, - {NAME("nflushes"), CTL(stats_arenas_i_bins_j_nflushes)}, - {NAME("nslabs"), CTL(stats_arenas_i_bins_j_nslabs)}, - {NAME("nreslabs"), CTL(stats_arenas_i_bins_j_nreslabs)}, - {NAME("curslabs"), CTL(stats_arenas_i_bins_j_curslabs)}, - {NAME("nonfull_slabs"), CTL(stats_arenas_i_bins_j_nonfull_slabs)}, - {NAME("mutex"), CHILD(named, stats_arenas_i_bins_j_mutex)} -}; - -static const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = { - {NAME(""), CHILD(named, stats_arenas_i_bins_j)} -}; - -static const ctl_indexed_node_t stats_arenas_i_bins_node[] = { - {INDEX(stats_arenas_i_bins_j)} -}; - -static const ctl_named_node_t stats_arenas_i_lextents_j_node[] = { - {NAME("nmalloc"), CTL(stats_arenas_i_lextents_j_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_lextents_j_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_lextents_j_nrequests)}, - {NAME("curlextents"), CTL(stats_arenas_i_lextents_j_curlextents)} -}; -static const ctl_named_node_t super_stats_arenas_i_lextents_j_node[] = { - {NAME(""), CHILD(named, stats_arenas_i_lextents_j)} -}; - -static const ctl_indexed_node_t stats_arenas_i_lextents_node[] = { - {INDEX(stats_arenas_i_lextents_j)} -}; - -static const ctl_named_node_t stats_arenas_i_extents_j_node[] = { - {NAME("ndirty"), CTL(stats_arenas_i_extents_j_ndirty)}, - {NAME("nmuzzy"), CTL(stats_arenas_i_extents_j_nmuzzy)}, - {NAME("nretained"), CTL(stats_arenas_i_extents_j_nretained)}, - {NAME("dirty_bytes"), CTL(stats_arenas_i_extents_j_dirty_bytes)}, - {NAME("muzzy_bytes"), CTL(stats_arenas_i_extents_j_muzzy_bytes)}, - {NAME("retained_bytes"), CTL(stats_arenas_i_extents_j_retained_bytes)} -}; - -static const ctl_named_node_t super_stats_arenas_i_extents_j_node[] = { - {NAME(""), CHILD(named, stats_arenas_i_extents_j)} -}; - -static const ctl_indexed_node_t stats_arenas_i_extents_node[] = { - {INDEX(stats_arenas_i_extents_j)} -}; - -#define OP(mtx) MUTEX_PROF_DATA_NODE(arenas_i_mutexes_##mtx) -MUTEX_PROF_ARENA_MUTEXES -#undef OP - -static const ctl_named_node_t stats_arenas_i_mutexes_node[] = { -#define OP(mtx) {NAME(#mtx), CHILD(named, stats_arenas_i_mutexes_##mtx)}, -MUTEX_PROF_ARENA_MUTEXES -#undef OP -}; - -static const ctl_named_node_t stats_arenas_i_hpa_shard_full_slabs_node[] = { - {NAME("npageslabs_nonhuge"), - CTL(stats_arenas_i_hpa_shard_full_slabs_npageslabs_nonhuge)}, - {NAME("npageslabs_huge"), - CTL(stats_arenas_i_hpa_shard_full_slabs_npageslabs_huge)}, - {NAME("nactive_nonhuge"), - CTL(stats_arenas_i_hpa_shard_full_slabs_nactive_nonhuge)}, - {NAME("nactive_huge"), - CTL(stats_arenas_i_hpa_shard_full_slabs_nactive_huge)}, - {NAME("ndirty_nonhuge"), - CTL(stats_arenas_i_hpa_shard_full_slabs_ndirty_nonhuge)}, - {NAME("ndirty_huge"), - CTL(stats_arenas_i_hpa_shard_full_slabs_ndirty_huge)} -}; - -static const ctl_named_node_t stats_arenas_i_hpa_shard_empty_slabs_node[] = { - {NAME("npageslabs_nonhuge"), - CTL(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_nonhuge)}, - {NAME("npageslabs_huge"), - CTL(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_huge)}, - {NAME("nactive_nonhuge"), - CTL(stats_arenas_i_hpa_shard_empty_slabs_nactive_nonhuge)}, - {NAME("nactive_huge"), - CTL(stats_arenas_i_hpa_shard_empty_slabs_nactive_huge)}, - {NAME("ndirty_nonhuge"), - CTL(stats_arenas_i_hpa_shard_empty_slabs_ndirty_nonhuge)}, - {NAME("ndirty_huge"), - CTL(stats_arenas_i_hpa_shard_empty_slabs_ndirty_huge)} -}; - -static const ctl_named_node_t stats_arenas_i_hpa_shard_nonfull_slabs_j_node[] = { - {NAME("npageslabs_nonhuge"), - CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_nonhuge)}, - {NAME("npageslabs_huge"), - CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_huge)}, - {NAME("nactive_nonhuge"), - CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_nonhuge)}, - {NAME("nactive_huge"), - CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_huge)}, - {NAME("ndirty_nonhuge"), - CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_nonhuge)}, - {NAME("ndirty_huge"), - CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_huge)} -}; - -static const ctl_named_node_t super_stats_arenas_i_hpa_shard_nonfull_slabs_j_node[] = { - {NAME(""), - CHILD(named, stats_arenas_i_hpa_shard_nonfull_slabs_j)} -}; - -static const ctl_indexed_node_t stats_arenas_i_hpa_shard_nonfull_slabs_node[] = -{ - {INDEX(stats_arenas_i_hpa_shard_nonfull_slabs_j)} -}; - -static const ctl_named_node_t stats_arenas_i_hpa_shard_node[] = { - {NAME("full_slabs"), CHILD(named, - stats_arenas_i_hpa_shard_full_slabs)}, - {NAME("empty_slabs"), CHILD(named, - stats_arenas_i_hpa_shard_empty_slabs)}, - {NAME("nonfull_slabs"), CHILD(indexed, - stats_arenas_i_hpa_shard_nonfull_slabs)}, - - {NAME("npurge_passes"), CTL(stats_arenas_i_hpa_shard_npurge_passes)}, - {NAME("npurges"), CTL(stats_arenas_i_hpa_shard_npurges)}, - {NAME("nhugifies"), CTL(stats_arenas_i_hpa_shard_nhugifies)}, - {NAME("ndehugifies"), CTL(stats_arenas_i_hpa_shard_ndehugifies)} -}; - -static const ctl_named_node_t stats_arenas_i_node[] = { - {NAME("nthreads"), CTL(stats_arenas_i_nthreads)}, - {NAME("uptime"), CTL(stats_arenas_i_uptime)}, - {NAME("dss"), CTL(stats_arenas_i_dss)}, - {NAME("dirty_decay_ms"), CTL(stats_arenas_i_dirty_decay_ms)}, - {NAME("muzzy_decay_ms"), CTL(stats_arenas_i_muzzy_decay_ms)}, - {NAME("pactive"), CTL(stats_arenas_i_pactive)}, - {NAME("pdirty"), CTL(stats_arenas_i_pdirty)}, - {NAME("pmuzzy"), CTL(stats_arenas_i_pmuzzy)}, - {NAME("mapped"), CTL(stats_arenas_i_mapped)}, - {NAME("retained"), CTL(stats_arenas_i_retained)}, - {NAME("extent_avail"), CTL(stats_arenas_i_extent_avail)}, - {NAME("dirty_npurge"), CTL(stats_arenas_i_dirty_npurge)}, - {NAME("dirty_nmadvise"), CTL(stats_arenas_i_dirty_nmadvise)}, - {NAME("dirty_purged"), CTL(stats_arenas_i_dirty_purged)}, - {NAME("muzzy_npurge"), CTL(stats_arenas_i_muzzy_npurge)}, - {NAME("muzzy_nmadvise"), CTL(stats_arenas_i_muzzy_nmadvise)}, - {NAME("muzzy_purged"), CTL(stats_arenas_i_muzzy_purged)}, - {NAME("base"), CTL(stats_arenas_i_base)}, - {NAME("internal"), CTL(stats_arenas_i_internal)}, - {NAME("metadata_thp"), CTL(stats_arenas_i_metadata_thp)}, - {NAME("tcache_bytes"), CTL(stats_arenas_i_tcache_bytes)}, - {NAME("tcache_stashed_bytes"), - CTL(stats_arenas_i_tcache_stashed_bytes)}, - {NAME("resident"), CTL(stats_arenas_i_resident)}, - {NAME("abandoned_vm"), CTL(stats_arenas_i_abandoned_vm)}, - {NAME("hpa_sec_bytes"), CTL(stats_arenas_i_hpa_sec_bytes)}, - {NAME("small"), CHILD(named, stats_arenas_i_small)}, - {NAME("large"), CHILD(named, stats_arenas_i_large)}, - {NAME("bins"), CHILD(indexed, stats_arenas_i_bins)}, - {NAME("lextents"), CHILD(indexed, stats_arenas_i_lextents)}, - {NAME("extents"), CHILD(indexed, stats_arenas_i_extents)}, - {NAME("mutexes"), CHILD(named, stats_arenas_i_mutexes)}, - {NAME("hpa_shard"), CHILD(named, stats_arenas_i_hpa_shard)} -}; -static const ctl_named_node_t super_stats_arenas_i_node[] = { - {NAME(""), CHILD(named, stats_arenas_i)} -}; - -static const ctl_indexed_node_t stats_arenas_node[] = { - {INDEX(stats_arenas_i)} -}; - -static const ctl_named_node_t stats_background_thread_node[] = { - {NAME("num_threads"), CTL(stats_background_thread_num_threads)}, - {NAME("num_runs"), CTL(stats_background_thread_num_runs)}, - {NAME("run_interval"), CTL(stats_background_thread_run_interval)} -}; - -#define OP(mtx) MUTEX_PROF_DATA_NODE(mutexes_##mtx) -MUTEX_PROF_GLOBAL_MUTEXES -#undef OP - -static const ctl_named_node_t stats_mutexes_node[] = { -#define OP(mtx) {NAME(#mtx), CHILD(named, stats_mutexes_##mtx)}, -MUTEX_PROF_GLOBAL_MUTEXES -#undef OP - {NAME("reset"), CTL(stats_mutexes_reset)} -}; -#undef MUTEX_PROF_DATA_NODE - -static const ctl_named_node_t stats_node[] = { - {NAME("allocated"), CTL(stats_allocated)}, - {NAME("active"), CTL(stats_active)}, - {NAME("metadata"), CTL(stats_metadata)}, - {NAME("metadata_thp"), CTL(stats_metadata_thp)}, - {NAME("resident"), CTL(stats_resident)}, - {NAME("mapped"), CTL(stats_mapped)}, - {NAME("retained"), CTL(stats_retained)}, - {NAME("background_thread"), - CHILD(named, stats_background_thread)}, - {NAME("mutexes"), CHILD(named, stats_mutexes)}, - {NAME("arenas"), CHILD(indexed, stats_arenas)}, - {NAME("zero_reallocs"), CTL(stats_zero_reallocs)}, -}; - -static const ctl_named_node_t experimental_hooks_node[] = { - {NAME("install"), CTL(experimental_hooks_install)}, - {NAME("remove"), CTL(experimental_hooks_remove)}, - {NAME("prof_backtrace"), CTL(experimental_hooks_prof_backtrace)}, - {NAME("prof_dump"), CTL(experimental_hooks_prof_dump)}, - {NAME("safety_check_abort"), CTL(experimental_hooks_safety_check_abort)}, -}; - -static const ctl_named_node_t experimental_thread_node[] = { - {NAME("activity_callback"), - CTL(experimental_thread_activity_callback)} -}; - -static const ctl_named_node_t experimental_utilization_node[] = { - {NAME("query"), CTL(experimental_utilization_query)}, - {NAME("batch_query"), CTL(experimental_utilization_batch_query)} -}; - -static const ctl_named_node_t experimental_arenas_i_node[] = { - {NAME("pactivep"), CTL(experimental_arenas_i_pactivep)} -}; -static const ctl_named_node_t super_experimental_arenas_i_node[] = { - {NAME(""), CHILD(named, experimental_arenas_i)} -}; - -static const ctl_indexed_node_t experimental_arenas_node[] = { - {INDEX(experimental_arenas_i)} -}; - -static const ctl_named_node_t experimental_prof_recent_node[] = { - {NAME("alloc_max"), CTL(experimental_prof_recent_alloc_max)}, - {NAME("alloc_dump"), CTL(experimental_prof_recent_alloc_dump)}, -}; - -static const ctl_named_node_t experimental_node[] = { - {NAME("hooks"), CHILD(named, experimental_hooks)}, - {NAME("utilization"), CHILD(named, experimental_utilization)}, - {NAME("arenas"), CHILD(indexed, experimental_arenas)}, - {NAME("arenas_create_ext"), CTL(experimental_arenas_create_ext)}, - {NAME("prof_recent"), CHILD(named, experimental_prof_recent)}, - {NAME("batch_alloc"), CTL(experimental_batch_alloc)}, - {NAME("thread"), CHILD(named, experimental_thread)} -}; - -static const ctl_named_node_t root_node[] = { - {NAME("version"), CTL(version)}, - {NAME("epoch"), CTL(epoch)}, - {NAME("background_thread"), CTL(background_thread)}, - {NAME("max_background_threads"), CTL(max_background_threads)}, - {NAME("thread"), CHILD(named, thread)}, - {NAME("config"), CHILD(named, config)}, - {NAME("opt"), CHILD(named, opt)}, - {NAME("tcache"), CHILD(named, tcache)}, - {NAME("arena"), CHILD(indexed, arena)}, - {NAME("arenas"), CHILD(named, arenas)}, - {NAME("prof"), CHILD(named, prof)}, - {NAME("stats"), CHILD(named, stats)}, - {NAME("experimental"), CHILD(named, experimental)} -}; -static const ctl_named_node_t super_root_node[] = { - {NAME(""), CHILD(named, root)} -}; - -#undef NAME -#undef CHILD -#undef CTL -#undef INDEX - -/******************************************************************************/ - -/* - * Sets *dst + *src non-atomically. This is safe, since everything is - * synchronized by the ctl mutex. - */ -static void -ctl_accum_locked_u64(locked_u64_t *dst, locked_u64_t *src) { - locked_inc_u64_unsynchronized(dst, - locked_read_u64_unsynchronized(src)); -} - -static void -ctl_accum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) { - size_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED); - size_t cur_src = atomic_load_zu(src, ATOMIC_RELAXED); - atomic_store_zu(dst, cur_dst + cur_src, ATOMIC_RELAXED); -} - -/******************************************************************************/ - -static unsigned -arenas_i2a_impl(size_t i, bool compat, bool validate) { - unsigned a; - - switch (i) { - case MALLCTL_ARENAS_ALL: - a = 0; - break; - case MALLCTL_ARENAS_DESTROYED: - a = 1; - break; - default: - if (compat && i == ctl_arenas->narenas) { - /* - * Provide deprecated backward compatibility for - * accessing the merged stats at index narenas rather - * than via MALLCTL_ARENAS_ALL. This is scheduled for - * removal in 6.0.0. - */ - a = 0; - } else if (validate && i >= ctl_arenas->narenas) { - a = UINT_MAX; - } else { - /* - * This function should never be called for an index - * more than one past the range of indices that have - * initialized ctl data. - */ - assert(i < ctl_arenas->narenas || (!validate && i == - ctl_arenas->narenas)); - a = (unsigned)i + 2; - } - break; - } - - return a; -} - -static unsigned -arenas_i2a(size_t i) { - return arenas_i2a_impl(i, true, false); -} - -static ctl_arena_t * -arenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) { - ctl_arena_t *ret; - - assert(!compat || !init); - - ret = ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)]; - if (init && ret == NULL) { - if (config_stats) { - struct container_s { - ctl_arena_t ctl_arena; - ctl_arena_stats_t astats; - }; - struct container_s *cont = - (struct container_s *)base_alloc(tsd_tsdn(tsd), - b0get(), sizeof(struct container_s), QUANTUM); - if (cont == NULL) { - return NULL; - } - ret = &cont->ctl_arena; - ret->astats = &cont->astats; - } else { - ret = (ctl_arena_t *)base_alloc(tsd_tsdn(tsd), b0get(), - sizeof(ctl_arena_t), QUANTUM); - if (ret == NULL) { - return NULL; - } - } - ret->arena_ind = (unsigned)i; - ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)] = ret; - } - - assert(ret == NULL || arenas_i2a(ret->arena_ind) == arenas_i2a(i)); - return ret; -} - -static ctl_arena_t * -arenas_i(size_t i) { - ctl_arena_t *ret = arenas_i_impl(tsd_fetch(), i, true, false); - assert(ret != NULL); - return ret; -} - -static void -ctl_arena_clear(ctl_arena_t *ctl_arena) { - ctl_arena->nthreads = 0; - ctl_arena->dss = dss_prec_names[dss_prec_limit]; - ctl_arena->dirty_decay_ms = -1; - ctl_arena->muzzy_decay_ms = -1; - ctl_arena->pactive = 0; - ctl_arena->pdirty = 0; - ctl_arena->pmuzzy = 0; - if (config_stats) { - memset(&ctl_arena->astats->astats, 0, sizeof(arena_stats_t)); - ctl_arena->astats->allocated_small = 0; - ctl_arena->astats->nmalloc_small = 0; - ctl_arena->astats->ndalloc_small = 0; - ctl_arena->astats->nrequests_small = 0; - ctl_arena->astats->nfills_small = 0; - ctl_arena->astats->nflushes_small = 0; - memset(ctl_arena->astats->bstats, 0, SC_NBINS * - sizeof(bin_stats_data_t)); - memset(ctl_arena->astats->lstats, 0, (SC_NSIZES - SC_NBINS) * - sizeof(arena_stats_large_t)); - memset(ctl_arena->astats->estats, 0, SC_NPSIZES * - sizeof(pac_estats_t)); - memset(&ctl_arena->astats->hpastats, 0, - sizeof(hpa_shard_stats_t)); - memset(&ctl_arena->astats->secstats, 0, - sizeof(sec_stats_t)); - } -} - -static void -ctl_arena_stats_amerge(tsdn_t *tsdn, ctl_arena_t *ctl_arena, arena_t *arena) { - unsigned i; - - if (config_stats) { - arena_stats_merge(tsdn, arena, &ctl_arena->nthreads, - &ctl_arena->dss, &ctl_arena->dirty_decay_ms, - &ctl_arena->muzzy_decay_ms, &ctl_arena->pactive, - &ctl_arena->pdirty, &ctl_arena->pmuzzy, - &ctl_arena->astats->astats, ctl_arena->astats->bstats, - ctl_arena->astats->lstats, ctl_arena->astats->estats, - &ctl_arena->astats->hpastats, &ctl_arena->astats->secstats); - - for (i = 0; i < SC_NBINS; i++) { - bin_stats_t *bstats = - &ctl_arena->astats->bstats[i].stats_data; - ctl_arena->astats->allocated_small += bstats->curregs * - sz_index2size(i); - ctl_arena->astats->nmalloc_small += bstats->nmalloc; - ctl_arena->astats->ndalloc_small += bstats->ndalloc; - ctl_arena->astats->nrequests_small += bstats->nrequests; - ctl_arena->astats->nfills_small += bstats->nfills; - ctl_arena->astats->nflushes_small += bstats->nflushes; - } - } else { - arena_basic_stats_merge(tsdn, arena, &ctl_arena->nthreads, - &ctl_arena->dss, &ctl_arena->dirty_decay_ms, - &ctl_arena->muzzy_decay_ms, &ctl_arena->pactive, - &ctl_arena->pdirty, &ctl_arena->pmuzzy); - } -} - -static void -ctl_arena_stats_sdmerge(ctl_arena_t *ctl_sdarena, ctl_arena_t *ctl_arena, - bool destroyed) { - unsigned i; - - if (!destroyed) { - ctl_sdarena->nthreads += ctl_arena->nthreads; - ctl_sdarena->pactive += ctl_arena->pactive; - ctl_sdarena->pdirty += ctl_arena->pdirty; - ctl_sdarena->pmuzzy += ctl_arena->pmuzzy; - } else { - assert(ctl_arena->nthreads == 0); - assert(ctl_arena->pactive == 0); - assert(ctl_arena->pdirty == 0); - assert(ctl_arena->pmuzzy == 0); - } - - if (config_stats) { - ctl_arena_stats_t *sdstats = ctl_sdarena->astats; - ctl_arena_stats_t *astats = ctl_arena->astats; - - if (!destroyed) { - sdstats->astats.mapped += astats->astats.mapped; - sdstats->astats.pa_shard_stats.pac_stats.retained - += astats->astats.pa_shard_stats.pac_stats.retained; - sdstats->astats.pa_shard_stats.edata_avail - += astats->astats.pa_shard_stats.edata_avail; - } - - ctl_accum_locked_u64( - &sdstats->astats.pa_shard_stats.pac_stats.decay_dirty.npurge, - &astats->astats.pa_shard_stats.pac_stats.decay_dirty.npurge); - ctl_accum_locked_u64( - &sdstats->astats.pa_shard_stats.pac_stats.decay_dirty.nmadvise, - &astats->astats.pa_shard_stats.pac_stats.decay_dirty.nmadvise); - ctl_accum_locked_u64( - &sdstats->astats.pa_shard_stats.pac_stats.decay_dirty.purged, - &astats->astats.pa_shard_stats.pac_stats.decay_dirty.purged); - - ctl_accum_locked_u64( - &sdstats->astats.pa_shard_stats.pac_stats.decay_muzzy.npurge, - &astats->astats.pa_shard_stats.pac_stats.decay_muzzy.npurge); - ctl_accum_locked_u64( - &sdstats->astats.pa_shard_stats.pac_stats.decay_muzzy.nmadvise, - &astats->astats.pa_shard_stats.pac_stats.decay_muzzy.nmadvise); - ctl_accum_locked_u64( - &sdstats->astats.pa_shard_stats.pac_stats.decay_muzzy.purged, - &astats->astats.pa_shard_stats.pac_stats.decay_muzzy.purged); - -#define OP(mtx) malloc_mutex_prof_merge( \ - &(sdstats->astats.mutex_prof_data[ \ - arena_prof_mutex_##mtx]), \ - &(astats->astats.mutex_prof_data[ \ - arena_prof_mutex_##mtx])); -MUTEX_PROF_ARENA_MUTEXES -#undef OP - if (!destroyed) { - sdstats->astats.base += astats->astats.base; - sdstats->astats.resident += astats->astats.resident; - sdstats->astats.metadata_thp += astats->astats.metadata_thp; - ctl_accum_atomic_zu(&sdstats->astats.internal, - &astats->astats.internal); - } else { - assert(atomic_load_zu( - &astats->astats.internal, ATOMIC_RELAXED) == 0); - } - - if (!destroyed) { - sdstats->allocated_small += astats->allocated_small; - } else { - assert(astats->allocated_small == 0); - } - sdstats->nmalloc_small += astats->nmalloc_small; - sdstats->ndalloc_small += astats->ndalloc_small; - sdstats->nrequests_small += astats->nrequests_small; - sdstats->nfills_small += astats->nfills_small; - sdstats->nflushes_small += astats->nflushes_small; - - if (!destroyed) { - sdstats->astats.allocated_large += - astats->astats.allocated_large; - } else { - assert(astats->astats.allocated_large == 0); - } - sdstats->astats.nmalloc_large += astats->astats.nmalloc_large; - sdstats->astats.ndalloc_large += astats->astats.ndalloc_large; - sdstats->astats.nrequests_large - += astats->astats.nrequests_large; - sdstats->astats.nflushes_large += astats->astats.nflushes_large; - ctl_accum_atomic_zu( - &sdstats->astats.pa_shard_stats.pac_stats.abandoned_vm, - &astats->astats.pa_shard_stats.pac_stats.abandoned_vm); - - sdstats->astats.tcache_bytes += astats->astats.tcache_bytes; - sdstats->astats.tcache_stashed_bytes += - astats->astats.tcache_stashed_bytes; - - if (ctl_arena->arena_ind == 0) { - sdstats->astats.uptime = astats->astats.uptime; - } - - /* Merge bin stats. */ - for (i = 0; i < SC_NBINS; i++) { - bin_stats_t *bstats = &astats->bstats[i].stats_data; - bin_stats_t *merged = &sdstats->bstats[i].stats_data; - merged->nmalloc += bstats->nmalloc; - merged->ndalloc += bstats->ndalloc; - merged->nrequests += bstats->nrequests; - if (!destroyed) { - merged->curregs += bstats->curregs; - } else { - assert(bstats->curregs == 0); - } - merged->nfills += bstats->nfills; - merged->nflushes += bstats->nflushes; - merged->nslabs += bstats->nslabs; - merged->reslabs += bstats->reslabs; - if (!destroyed) { - merged->curslabs += bstats->curslabs; - merged->nonfull_slabs += bstats->nonfull_slabs; - } else { - assert(bstats->curslabs == 0); - assert(bstats->nonfull_slabs == 0); - } - malloc_mutex_prof_merge(&sdstats->bstats[i].mutex_data, - &astats->bstats[i].mutex_data); - } - - /* Merge stats for large allocations. */ - for (i = 0; i < SC_NSIZES - SC_NBINS; i++) { - ctl_accum_locked_u64(&sdstats->lstats[i].nmalloc, - &astats->lstats[i].nmalloc); - ctl_accum_locked_u64(&sdstats->lstats[i].ndalloc, - &astats->lstats[i].ndalloc); - ctl_accum_locked_u64(&sdstats->lstats[i].nrequests, - &astats->lstats[i].nrequests); - if (!destroyed) { - sdstats->lstats[i].curlextents += - astats->lstats[i].curlextents; - } else { - assert(astats->lstats[i].curlextents == 0); - } - } - - /* Merge extents stats. */ - for (i = 0; i < SC_NPSIZES; i++) { - sdstats->estats[i].ndirty += astats->estats[i].ndirty; - sdstats->estats[i].nmuzzy += astats->estats[i].nmuzzy; - sdstats->estats[i].nretained - += astats->estats[i].nretained; - sdstats->estats[i].dirty_bytes - += astats->estats[i].dirty_bytes; - sdstats->estats[i].muzzy_bytes - += astats->estats[i].muzzy_bytes; - sdstats->estats[i].retained_bytes - += astats->estats[i].retained_bytes; - } - - /* Merge HPA stats. */ - hpa_shard_stats_accum(&sdstats->hpastats, &astats->hpastats); - sec_stats_accum(&sdstats->secstats, &astats->secstats); - } -} - -static void -ctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, ctl_arena_t *ctl_sdarena, - unsigned i, bool destroyed) { - ctl_arena_t *ctl_arena = arenas_i(i); - - ctl_arena_clear(ctl_arena); - ctl_arena_stats_amerge(tsdn, ctl_arena, arena); - /* Merge into sum stats as well. */ - ctl_arena_stats_sdmerge(ctl_sdarena, ctl_arena, destroyed); -} - -static unsigned -ctl_arena_init(tsd_t *tsd, const arena_config_t *config) { - unsigned arena_ind; - ctl_arena_t *ctl_arena; - - if ((ctl_arena = ql_last(&ctl_arenas->destroyed, destroyed_link)) != - NULL) { - ql_remove(&ctl_arenas->destroyed, ctl_arena, destroyed_link); - arena_ind = ctl_arena->arena_ind; - } else { - arena_ind = ctl_arenas->narenas; - } - - /* Trigger stats allocation. */ - if (arenas_i_impl(tsd, arena_ind, false, true) == NULL) { - return UINT_MAX; - } - - /* Initialize new arena. */ - if (arena_init(tsd_tsdn(tsd), arena_ind, config) == NULL) { - return UINT_MAX; - } - - if (arena_ind == ctl_arenas->narenas) { - ctl_arenas->narenas++; - } - - return arena_ind; -} - -static void -ctl_background_thread_stats_read(tsdn_t *tsdn) { - background_thread_stats_t *stats = &ctl_stats->background_thread; - if (!have_background_thread || - background_thread_stats_read(tsdn, stats)) { - memset(stats, 0, sizeof(background_thread_stats_t)); - nstime_init_zero(&stats->run_interval); - } - malloc_mutex_prof_copy( - &ctl_stats->mutex_prof_data[global_prof_mutex_max_per_bg_thd], - &stats->max_counter_per_bg_thd); -} - -static void -ctl_refresh(tsdn_t *tsdn) { - unsigned i; - ctl_arena_t *ctl_sarena = arenas_i(MALLCTL_ARENAS_ALL); - VARIABLE_ARRAY(arena_t *, tarenas, ctl_arenas->narenas); - - /* - * Clear sum stats, since they will be merged into by - * ctl_arena_refresh(). - */ - ctl_arena_clear(ctl_sarena); - - for (i = 0; i < ctl_arenas->narenas; i++) { - tarenas[i] = arena_get(tsdn, i, false); - } - - for (i = 0; i < ctl_arenas->narenas; i++) { - ctl_arena_t *ctl_arena = arenas_i(i); - bool initialized = (tarenas[i] != NULL); - - ctl_arena->initialized = initialized; - if (initialized) { - ctl_arena_refresh(tsdn, tarenas[i], ctl_sarena, i, - false); - } - } - - if (config_stats) { - ctl_stats->allocated = ctl_sarena->astats->allocated_small + - ctl_sarena->astats->astats.allocated_large; - ctl_stats->active = (ctl_sarena->pactive << LG_PAGE); - ctl_stats->metadata = ctl_sarena->astats->astats.base + - atomic_load_zu(&ctl_sarena->astats->astats.internal, - ATOMIC_RELAXED); - ctl_stats->resident = ctl_sarena->astats->astats.resident; - ctl_stats->metadata_thp = - ctl_sarena->astats->astats.metadata_thp; - ctl_stats->mapped = ctl_sarena->astats->astats.mapped; - ctl_stats->retained = ctl_sarena->astats->astats - .pa_shard_stats.pac_stats.retained; - - ctl_background_thread_stats_read(tsdn); - -#define READ_GLOBAL_MUTEX_PROF_DATA(i, mtx) \ - malloc_mutex_lock(tsdn, &mtx); \ - malloc_mutex_prof_read(tsdn, &ctl_stats->mutex_prof_data[i], &mtx); \ - malloc_mutex_unlock(tsdn, &mtx); - - if (config_prof && opt_prof) { - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_prof, bt2gctx_mtx); - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_prof_thds_data, tdatas_mtx); - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_prof_dump, prof_dump_mtx); - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_prof_recent_alloc, - prof_recent_alloc_mtx); - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_prof_recent_dump, - prof_recent_dump_mtx); - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_prof_stats, prof_stats_mtx); - } - if (have_background_thread) { - READ_GLOBAL_MUTEX_PROF_DATA( - global_prof_mutex_background_thread, - background_thread_lock); - } else { - memset(&ctl_stats->mutex_prof_data[ - global_prof_mutex_background_thread], 0, - sizeof(mutex_prof_data_t)); - } - /* We own ctl mutex already. */ - malloc_mutex_prof_read(tsdn, - &ctl_stats->mutex_prof_data[global_prof_mutex_ctl], - &ctl_mtx); -#undef READ_GLOBAL_MUTEX_PROF_DATA - } - ctl_arenas->epoch++; -} - -static bool -ctl_init(tsd_t *tsd) { - bool ret; - tsdn_t *tsdn = tsd_tsdn(tsd); - - malloc_mutex_lock(tsdn, &ctl_mtx); - if (!ctl_initialized) { - ctl_arena_t *ctl_sarena, *ctl_darena; - unsigned i; - - /* - * Allocate demand-zeroed space for pointers to the full - * range of supported arena indices. - */ - if (ctl_arenas == NULL) { - ctl_arenas = (ctl_arenas_t *)base_alloc(tsdn, - b0get(), sizeof(ctl_arenas_t), QUANTUM); - if (ctl_arenas == NULL) { - ret = true; - goto label_return; - } - } - - if (config_stats && ctl_stats == NULL) { - ctl_stats = (ctl_stats_t *)base_alloc(tsdn, b0get(), - sizeof(ctl_stats_t), QUANTUM); - if (ctl_stats == NULL) { - ret = true; - goto label_return; - } - } - - /* - * Allocate space for the current full range of arenas - * here rather than doing it lazily elsewhere, in order - * to limit when OOM-caused errors can occur. - */ - if ((ctl_sarena = arenas_i_impl(tsd, MALLCTL_ARENAS_ALL, false, - true)) == NULL) { - ret = true; - goto label_return; - } - ctl_sarena->initialized = true; - - if ((ctl_darena = arenas_i_impl(tsd, MALLCTL_ARENAS_DESTROYED, - false, true)) == NULL) { - ret = true; - goto label_return; - } - ctl_arena_clear(ctl_darena); - /* - * Don't toggle ctl_darena to initialized until an arena is - * actually destroyed, so that arena.<i>.initialized can be used - * to query whether the stats are relevant. - */ - - ctl_arenas->narenas = narenas_total_get(); - for (i = 0; i < ctl_arenas->narenas; i++) { - if (arenas_i_impl(tsd, i, false, true) == NULL) { - ret = true; - goto label_return; - } - } - - ql_new(&ctl_arenas->destroyed); - ctl_refresh(tsdn); - - ctl_initialized = true; - } - - ret = false; -label_return: - malloc_mutex_unlock(tsdn, &ctl_mtx); - return ret; -} - -static int -ctl_lookup(tsdn_t *tsdn, const ctl_named_node_t *starting_node, - const char *name, const ctl_named_node_t **ending_nodep, size_t *mibp, - size_t *depthp) { - int ret; - const char *elm, *tdot, *dot; - size_t elen, i, j; - const ctl_named_node_t *node; - - elm = name; - /* Equivalent to strchrnul(). */ - dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : strchr(elm, '\0'); - elen = (size_t)((uintptr_t)dot - (uintptr_t)elm); - if (elen == 0) { - ret = ENOENT; - goto label_return; - } - node = starting_node; - for (i = 0; i < *depthp; i++) { - assert(node); - assert(node->nchildren > 0); - if (ctl_named_node(node->children) != NULL) { - const ctl_named_node_t *pnode = node; - - /* Children are named. */ - for (j = 0; j < node->nchildren; j++) { - const ctl_named_node_t *child = - ctl_named_children(node, j); - if (strlen(child->name) == elen && - strncmp(elm, child->name, elen) == 0) { - node = child; - mibp[i] = j; - break; - } - } - if (node == pnode) { - ret = ENOENT; - goto label_return; - } - } else { - uintmax_t index; - const ctl_indexed_node_t *inode; - - /* Children are indexed. */ - index = malloc_strtoumax(elm, NULL, 10); - if (index == UINTMAX_MAX || index > SIZE_T_MAX) { - ret = ENOENT; - goto label_return; - } - - inode = ctl_indexed_node(node->children); - node = inode->index(tsdn, mibp, *depthp, (size_t)index); - if (node == NULL) { - ret = ENOENT; - goto label_return; - } - - mibp[i] = (size_t)index; - } - - /* Reached the end? */ - if (node->ctl != NULL || *dot == '\0') { - /* Terminal node. */ - if (*dot != '\0') { - /* - * The name contains more elements than are - * in this path through the tree. - */ - ret = ENOENT; - goto label_return; - } - /* Complete lookup successful. */ - *depthp = i + 1; - break; - } - - /* Update elm. */ - elm = &dot[1]; - dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : - strchr(elm, '\0'); - elen = (size_t)((uintptr_t)dot - (uintptr_t)elm); - } - if (ending_nodep != NULL) { - *ending_nodep = node; - } - - ret = 0; -label_return: - return ret; -} - -int -ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) { - int ret; - size_t depth; - size_t mib[CTL_MAX_DEPTH]; - const ctl_named_node_t *node; - - if (!ctl_initialized && ctl_init(tsd)) { - ret = EAGAIN; - goto label_return; - } - - depth = CTL_MAX_DEPTH; - ret = ctl_lookup(tsd_tsdn(tsd), super_root_node, name, &node, mib, - &depth); - if (ret != 0) { - goto label_return; - } - - if (node != NULL && node->ctl) { - ret = node->ctl(tsd, mib, depth, oldp, oldlenp, newp, newlen); - } else { - /* The name refers to a partial path through the ctl tree. */ - ret = ENOENT; - } - -label_return: - return(ret); -} - -int -ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp) { - int ret; - - if (!ctl_initialized && ctl_init(tsd)) { - ret = EAGAIN; - goto label_return; - } - - ret = ctl_lookup(tsd_tsdn(tsd), super_root_node, name, NULL, mibp, - miblenp); -label_return: - return(ret); -} - -static int -ctl_lookupbymib(tsdn_t *tsdn, const ctl_named_node_t **ending_nodep, - const size_t *mib, size_t miblen) { - int ret; - - const ctl_named_node_t *node = super_root_node; - for (size_t i = 0; i < miblen; i++) { - assert(node); - assert(node->nchildren > 0); - if (ctl_named_node(node->children) != NULL) { - /* Children are named. */ - if (node->nchildren <= mib[i]) { - ret = ENOENT; - goto label_return; - } - node = ctl_named_children(node, mib[i]); - } else { - const ctl_indexed_node_t *inode; - - /* Indexed element. */ - inode = ctl_indexed_node(node->children); - node = inode->index(tsdn, mib, miblen, mib[i]); - if (node == NULL) { - ret = ENOENT; - goto label_return; - } - } - } - assert(ending_nodep != NULL); - *ending_nodep = node; - ret = 0; - -label_return: - return(ret); -} - -int -ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - const ctl_named_node_t *node; - - if (!ctl_initialized && ctl_init(tsd)) { - ret = EAGAIN; - goto label_return; - } - - ret = ctl_lookupbymib(tsd_tsdn(tsd), &node, mib, miblen); - if (ret != 0) { - goto label_return; - } - - /* Call the ctl function. */ - if (node && node->ctl) { - ret = node->ctl(tsd, mib, miblen, oldp, oldlenp, newp, newlen); - } else { - /* Partial MIB. */ - ret = ENOENT; - } - -label_return: - return(ret); -} - -int -ctl_mibnametomib(tsd_t *tsd, size_t *mib, size_t miblen, const char *name, - size_t *miblenp) { - int ret; - const ctl_named_node_t *node; - - if (!ctl_initialized && ctl_init(tsd)) { - ret = EAGAIN; - goto label_return; - } - - ret = ctl_lookupbymib(tsd_tsdn(tsd), &node, mib, miblen); - if (ret != 0) { - goto label_return; - } - if (node == NULL || node->ctl != NULL) { - ret = ENOENT; - goto label_return; - } - - assert(miblenp != NULL); - assert(*miblenp >= miblen); - *miblenp -= miblen; - ret = ctl_lookup(tsd_tsdn(tsd), node, name, NULL, mib + miblen, - miblenp); - *miblenp += miblen; -label_return: - return(ret); -} - -int -ctl_bymibname(tsd_t *tsd, size_t *mib, size_t miblen, const char *name, - size_t *miblenp, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - const ctl_named_node_t *node; - - if (!ctl_initialized && ctl_init(tsd)) { - ret = EAGAIN; - goto label_return; - } - - ret = ctl_lookupbymib(tsd_tsdn(tsd), &node, mib, miblen); - if (ret != 0) { - goto label_return; - } - if (node == NULL || node->ctl != NULL) { - ret = ENOENT; - goto label_return; - } - - assert(miblenp != NULL); - assert(*miblenp >= miblen); - *miblenp -= miblen; - /* - * The same node supplies the starting node and stores the ending node. - */ - ret = ctl_lookup(tsd_tsdn(tsd), node, name, &node, mib + miblen, - miblenp); - *miblenp += miblen; - if (ret != 0) { - goto label_return; - } - - if (node != NULL && node->ctl) { - ret = node->ctl(tsd, mib, *miblenp, oldp, oldlenp, newp, - newlen); - } else { - /* The name refers to a partial path through the ctl tree. */ - ret = ENOENT; - } - -label_return: - return(ret); -} - -bool -ctl_boot(void) { - if (malloc_mutex_init(&ctl_mtx, "ctl", WITNESS_RANK_CTL, - malloc_mutex_rank_exclusive)) { - return true; - } - - ctl_initialized = false; - - return false; -} - -void -ctl_prefork(tsdn_t *tsdn) { - malloc_mutex_prefork(tsdn, &ctl_mtx); -} - -void -ctl_postfork_parent(tsdn_t *tsdn) { - malloc_mutex_postfork_parent(tsdn, &ctl_mtx); -} - -void -ctl_postfork_child(tsdn_t *tsdn) { - malloc_mutex_postfork_child(tsdn, &ctl_mtx); -} - -void -ctl_mtx_assert_held(tsdn_t *tsdn) { - malloc_mutex_assert_owner(tsdn, &ctl_mtx); -} - -/******************************************************************************/ -/* *_ctl() functions. */ - -#define READONLY() do { \ - if (newp != NULL || newlen != 0) { \ - ret = EPERM; \ - goto label_return; \ - } \ -} while (0) - -#define WRITEONLY() do { \ - if (oldp != NULL || oldlenp != NULL) { \ - ret = EPERM; \ - goto label_return; \ - } \ -} while (0) - -/* Can read or write, but not both. */ -#define READ_XOR_WRITE() do { \ - if ((oldp != NULL && oldlenp != NULL) && (newp != NULL || \ - newlen != 0)) { \ - ret = EPERM; \ - goto label_return; \ - } \ -} while (0) - -/* Can neither read nor write. */ -#define NEITHER_READ_NOR_WRITE() do { \ - if (oldp != NULL || oldlenp != NULL || newp != NULL || \ - newlen != 0) { \ - ret = EPERM; \ - goto label_return; \ - } \ -} while (0) - -/* Verify that the space provided is enough. */ -#define VERIFY_READ(t) do { \ - if (oldp == NULL || oldlenp == NULL || *oldlenp != sizeof(t)) { \ - *oldlenp = 0; \ - ret = EINVAL; \ - goto label_return; \ - } \ -} while (0) - -#define READ(v, t) do { \ - if (oldp != NULL && oldlenp != NULL) { \ - if (*oldlenp != sizeof(t)) { \ - size_t copylen = (sizeof(t) <= *oldlenp) \ - ? sizeof(t) : *oldlenp; \ - memcpy(oldp, (void *)&(v), copylen); \ - *oldlenp = copylen; \ - ret = EINVAL; \ - goto label_return; \ - } \ - *(t *)oldp = (v); \ - } \ -} while (0) - -#define WRITE(v, t) do { \ - if (newp != NULL) { \ - if (newlen != sizeof(t)) { \ - ret = EINVAL; \ - goto label_return; \ - } \ - (v) = *(t *)newp; \ - } \ -} while (0) - -#define ASSURED_WRITE(v, t) do { \ - if (newp == NULL || newlen != sizeof(t)) { \ - ret = EINVAL; \ - goto label_return; \ - } \ - (v) = *(t *)newp; \ -} while (0) - -#define MIB_UNSIGNED(v, i) do { \ - if (mib[i] > UINT_MAX) { \ - ret = EFAULT; \ - goto label_return; \ - } \ - v = (unsigned)mib[i]; \ -} while (0) - -/* - * There's a lot of code duplication in the following macros due to limitations - * in how nested cpp macros are expanded. - */ -#define CTL_RO_CLGEN(c, l, n, v, t) \ -static int \ -n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, \ - size_t *oldlenp, void *newp, size_t newlen) { \ - int ret; \ - t oldval; \ - \ - if (!(c)) { \ - return ENOENT; \ - } \ - if (l) { \ - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); \ - } \ - READONLY(); \ - oldval = (v); \ - READ(oldval, t); \ - \ - ret = 0; \ -label_return: \ - if (l) { \ - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); \ - } \ - return ret; \ -} - -#define CTL_RO_CGEN(c, n, v, t) \ -static int \ -n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \ - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \ - int ret; \ - t oldval; \ - \ - if (!(c)) { \ - return ENOENT; \ - } \ - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); \ - READONLY(); \ - oldval = (v); \ - READ(oldval, t); \ - \ - ret = 0; \ -label_return: \ - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); \ - return ret; \ -} - -#define CTL_RO_GEN(n, v, t) \ -static int \ -n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, \ - size_t *oldlenp, void *newp, size_t newlen) { \ - int ret; \ - t oldval; \ - \ - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); \ - READONLY(); \ - oldval = (v); \ - READ(oldval, t); \ - \ - ret = 0; \ -label_return: \ - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); \ - return ret; \ -} - -/* - * ctl_mtx is not acquired, under the assumption that no pertinent data will - * mutate during the call. - */ -#define CTL_RO_NL_CGEN(c, n, v, t) \ -static int \ -n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \ - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \ - int ret; \ - t oldval; \ - \ - if (!(c)) { \ - return ENOENT; \ - } \ - READONLY(); \ - oldval = (v); \ - READ(oldval, t); \ - \ - ret = 0; \ -label_return: \ - return ret; \ -} - -#define CTL_RO_NL_GEN(n, v, t) \ -static int \ -n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \ - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \ - int ret; \ - t oldval; \ - \ - READONLY(); \ - oldval = (v); \ - READ(oldval, t); \ - \ - ret = 0; \ -label_return: \ - return ret; \ -} - -#define CTL_RO_CONFIG_GEN(n, t) \ -static int \ -n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \ - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \ - int ret; \ - t oldval; \ - \ - READONLY(); \ - oldval = n; \ - READ(oldval, t); \ - \ - ret = 0; \ -label_return: \ - return ret; \ -} - -/******************************************************************************/ - -CTL_RO_NL_GEN(version, JEMALLOC_VERSION, const char *) - -static int -epoch_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - UNUSED uint64_t newval; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - WRITE(newval, uint64_t); - if (newp != NULL) { - ctl_refresh(tsd_tsdn(tsd)); - } - READ(ctl_arenas->epoch, uint64_t); - - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -background_thread_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) { - int ret; - bool oldval; - - if (!have_background_thread) { - return ENOENT; - } - background_thread_ctl_init(tsd_tsdn(tsd)); - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - malloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock); - if (newp == NULL) { - oldval = background_thread_enabled(); - READ(oldval, bool); - } else { - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - oldval = background_thread_enabled(); - READ(oldval, bool); - - bool newval = *(bool *)newp; - if (newval == oldval) { - ret = 0; - goto label_return; - } - - background_thread_enabled_set(tsd_tsdn(tsd), newval); - if (newval) { - if (background_threads_enable(tsd)) { - ret = EFAULT; - goto label_return; - } - } else { - if (background_threads_disable(tsd)) { - ret = EFAULT; - goto label_return; - } - } - } - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock); - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - - return ret; -} - -static int -max_background_threads_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - size_t oldval; - - if (!have_background_thread) { - return ENOENT; - } - background_thread_ctl_init(tsd_tsdn(tsd)); - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - malloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock); - if (newp == NULL) { - oldval = max_background_threads; - READ(oldval, size_t); - } else { - if (newlen != sizeof(size_t)) { - ret = EINVAL; - goto label_return; - } - oldval = max_background_threads; - READ(oldval, size_t); - - size_t newval = *(size_t *)newp; - if (newval == oldval) { - ret = 0; - goto label_return; - } - if (newval > opt_max_background_threads) { - ret = EINVAL; - goto label_return; - } - - if (background_thread_enabled()) { - background_thread_enabled_set(tsd_tsdn(tsd), false); - if (background_threads_disable(tsd)) { - ret = EFAULT; - goto label_return; - } - max_background_threads = newval; - background_thread_enabled_set(tsd_tsdn(tsd), true); - if (background_threads_enable(tsd)) { - ret = EFAULT; - goto label_return; - } - } else { - max_background_threads = newval; - } - } - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock); - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - - return ret; -} - -/******************************************************************************/ - -CTL_RO_CONFIG_GEN(config_cache_oblivious, bool) -CTL_RO_CONFIG_GEN(config_debug, bool) -CTL_RO_CONFIG_GEN(config_fill, bool) -CTL_RO_CONFIG_GEN(config_lazy_lock, bool) -CTL_RO_CONFIG_GEN(config_malloc_conf, const char *) -CTL_RO_CONFIG_GEN(config_opt_safety_checks, bool) -CTL_RO_CONFIG_GEN(config_prof, bool) -CTL_RO_CONFIG_GEN(config_prof_libgcc, bool) -CTL_RO_CONFIG_GEN(config_prof_libunwind, bool) -CTL_RO_CONFIG_GEN(config_stats, bool) -CTL_RO_CONFIG_GEN(config_utrace, bool) -CTL_RO_CONFIG_GEN(config_xmalloc, bool) - -/******************************************************************************/ - -CTL_RO_NL_GEN(opt_abort, opt_abort, bool) -CTL_RO_NL_GEN(opt_abort_conf, opt_abort_conf, bool) -CTL_RO_NL_GEN(opt_cache_oblivious, opt_cache_oblivious, bool) -CTL_RO_NL_GEN(opt_trust_madvise, opt_trust_madvise, bool) -CTL_RO_NL_GEN(opt_confirm_conf, opt_confirm_conf, bool) - -/* HPA options. */ -CTL_RO_NL_GEN(opt_hpa, opt_hpa, bool) -CTL_RO_NL_GEN(opt_hpa_hugification_threshold, - opt_hpa_opts.hugification_threshold, size_t) -CTL_RO_NL_GEN(opt_hpa_hugify_delay_ms, opt_hpa_opts.hugify_delay_ms, uint64_t) -CTL_RO_NL_GEN(opt_hpa_min_purge_interval_ms, opt_hpa_opts.min_purge_interval_ms, - uint64_t) - -/* - * This will have to change before we publicly document this option; fxp_t and - * its representation are internal implementation details. - */ -CTL_RO_NL_GEN(opt_hpa_dirty_mult, opt_hpa_opts.dirty_mult, fxp_t) -CTL_RO_NL_GEN(opt_hpa_slab_max_alloc, opt_hpa_opts.slab_max_alloc, size_t) - -/* HPA SEC options */ -CTL_RO_NL_GEN(opt_hpa_sec_nshards, opt_hpa_sec_opts.nshards, size_t) -CTL_RO_NL_GEN(opt_hpa_sec_max_alloc, opt_hpa_sec_opts.max_alloc, size_t) -CTL_RO_NL_GEN(opt_hpa_sec_max_bytes, opt_hpa_sec_opts.max_bytes, size_t) -CTL_RO_NL_GEN(opt_hpa_sec_bytes_after_flush, opt_hpa_sec_opts.bytes_after_flush, - size_t) -CTL_RO_NL_GEN(opt_hpa_sec_batch_fill_extra, opt_hpa_sec_opts.batch_fill_extra, - size_t) - -CTL_RO_NL_GEN(opt_metadata_thp, metadata_thp_mode_names[opt_metadata_thp], - const char *) -CTL_RO_NL_GEN(opt_retain, opt_retain, bool) -CTL_RO_NL_GEN(opt_dss, opt_dss, const char *) -CTL_RO_NL_GEN(opt_narenas, opt_narenas, unsigned) -CTL_RO_NL_GEN(opt_percpu_arena, percpu_arena_mode_names[opt_percpu_arena], - const char *) -CTL_RO_NL_GEN(opt_mutex_max_spin, opt_mutex_max_spin, int64_t) -CTL_RO_NL_GEN(opt_oversize_threshold, opt_oversize_threshold, size_t) -CTL_RO_NL_GEN(opt_background_thread, opt_background_thread, bool) -CTL_RO_NL_GEN(opt_max_background_threads, opt_max_background_threads, size_t) -CTL_RO_NL_GEN(opt_dirty_decay_ms, opt_dirty_decay_ms, ssize_t) -CTL_RO_NL_GEN(opt_muzzy_decay_ms, opt_muzzy_decay_ms, ssize_t) -CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool) -CTL_RO_NL_GEN(opt_stats_print_opts, opt_stats_print_opts, const char *) -CTL_RO_NL_GEN(opt_stats_interval, opt_stats_interval, int64_t) -CTL_RO_NL_GEN(opt_stats_interval_opts, opt_stats_interval_opts, const char *) -CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *) -CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool) -CTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool) -CTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool) -CTL_RO_NL_CGEN(config_enable_cxx, opt_experimental_infallible_new, - opt_experimental_infallible_new, bool) -CTL_RO_NL_GEN(opt_tcache, opt_tcache, bool) -CTL_RO_NL_GEN(opt_tcache_max, opt_tcache_max, size_t) -CTL_RO_NL_GEN(opt_tcache_nslots_small_min, opt_tcache_nslots_small_min, - unsigned) -CTL_RO_NL_GEN(opt_tcache_nslots_small_max, opt_tcache_nslots_small_max, - unsigned) -CTL_RO_NL_GEN(opt_tcache_nslots_large, opt_tcache_nslots_large, unsigned) -CTL_RO_NL_GEN(opt_lg_tcache_nslots_mul, opt_lg_tcache_nslots_mul, ssize_t) -CTL_RO_NL_GEN(opt_tcache_gc_incr_bytes, opt_tcache_gc_incr_bytes, size_t) -CTL_RO_NL_GEN(opt_tcache_gc_delay_bytes, opt_tcache_gc_delay_bytes, size_t) -CTL_RO_NL_GEN(opt_lg_tcache_flush_small_div, opt_lg_tcache_flush_small_div, - unsigned) -CTL_RO_NL_GEN(opt_lg_tcache_flush_large_div, opt_lg_tcache_flush_large_div, - unsigned) -CTL_RO_NL_GEN(opt_thp, thp_mode_names[opt_thp], const char *) -CTL_RO_NL_GEN(opt_lg_extent_max_active_fit, opt_lg_extent_max_active_fit, - size_t) -CTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *) -CTL_RO_NL_CGEN(config_prof, opt_prof_active, opt_prof_active, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_thread_active_init, - opt_prof_thread_active_init, bool) -CTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t) -CTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool) -CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t) -CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_leak_error, opt_prof_leak_error, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_recent_alloc_max, - opt_prof_recent_alloc_max, ssize_t) -CTL_RO_NL_CGEN(config_prof, opt_prof_stats, opt_prof_stats, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_sys_thread_name, opt_prof_sys_thread_name, - bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_time_res, - prof_time_res_mode_names[opt_prof_time_res], const char *) -CTL_RO_NL_CGEN(config_uaf_detection, opt_lg_san_uaf_align, - opt_lg_san_uaf_align, ssize_t) -CTL_RO_NL_GEN(opt_zero_realloc, - zero_realloc_mode_names[opt_zero_realloc_action], const char *) - -/******************************************************************************/ - -static int -thread_arena_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - arena_t *oldarena; - unsigned newind, oldind; - - oldarena = arena_choose(tsd, NULL); - if (oldarena == NULL) { - return EAGAIN; - } - newind = oldind = arena_ind_get(oldarena); - WRITE(newind, unsigned); - READ(oldind, unsigned); - - if (newind != oldind) { - arena_t *newarena; - - if (newind >= narenas_total_get()) { - /* New arena index is out of range. */ - ret = EFAULT; - goto label_return; - } - - if (have_percpu_arena && - PERCPU_ARENA_ENABLED(opt_percpu_arena)) { - if (newind < percpu_arena_ind_limit(opt_percpu_arena)) { - /* - * If perCPU arena is enabled, thread_arena - * control is not allowed for the auto arena - * range. - */ - ret = EPERM; - goto label_return; - } - } - - /* Initialize arena if necessary. */ - newarena = arena_get(tsd_tsdn(tsd), newind, true); - if (newarena == NULL) { - ret = EAGAIN; - goto label_return; - } - /* Set new arena/tcache associations. */ - arena_migrate(tsd, oldarena, newarena); - if (tcache_available(tsd)) { - tcache_arena_reassociate(tsd_tsdn(tsd), - tsd_tcache_slowp_get(tsd), tsd_tcachep_get(tsd), - newarena); - } - } - - ret = 0; -label_return: - return ret; -} - -CTL_RO_NL_GEN(thread_allocated, tsd_thread_allocated_get(tsd), uint64_t) -CTL_RO_NL_GEN(thread_allocatedp, tsd_thread_allocatedp_get(tsd), uint64_t *) -CTL_RO_NL_GEN(thread_deallocated, tsd_thread_deallocated_get(tsd), uint64_t) -CTL_RO_NL_GEN(thread_deallocatedp, tsd_thread_deallocatedp_get(tsd), uint64_t *) - -static int -thread_tcache_enabled_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - bool oldval; - - oldval = tcache_enabled_get(tsd); - if (newp != NULL) { - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - tcache_enabled_set(tsd, *(bool *)newp); - } - READ(oldval, bool); - - ret = 0; -label_return: - return ret; -} - -static int -thread_tcache_flush_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - - if (!tcache_available(tsd)) { - ret = EFAULT; - goto label_return; - } - - NEITHER_READ_NOR_WRITE(); - - tcache_flush(tsd); - - ret = 0; -label_return: - return ret; -} - -static int -thread_peak_read_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - if (!config_stats) { - return ENOENT; - } - READONLY(); - peak_event_update(tsd); - uint64_t result = peak_event_max(tsd); - READ(result, uint64_t); - ret = 0; -label_return: - return ret; -} - -static int -thread_peak_reset_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - if (!config_stats) { - return ENOENT; - } - NEITHER_READ_NOR_WRITE(); - peak_event_zero(tsd); - ret = 0; -label_return: - return ret; -} - -static int -thread_prof_name_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - - if (!config_prof || !opt_prof) { - return ENOENT; - } - - READ_XOR_WRITE(); - - if (newp != NULL) { - if (newlen != sizeof(const char *)) { - ret = EINVAL; - goto label_return; - } - - if ((ret = prof_thread_name_set(tsd, *(const char **)newp)) != - 0) { - goto label_return; - } - } else { - const char *oldname = prof_thread_name_get(tsd); - READ(oldname, const char *); - } - - ret = 0; -label_return: - return ret; -} - -static int -thread_prof_active_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - bool oldval; - - if (!config_prof) { - return ENOENT; - } - - oldval = opt_prof ? prof_thread_active_get(tsd) : false; - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - if (prof_thread_active_set(tsd, *(bool *)newp)) { - ret = EAGAIN; - goto label_return; - } - } - READ(oldval, bool); - - ret = 0; -label_return: - return ret; -} - -static int -thread_idle_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - - NEITHER_READ_NOR_WRITE(); - - if (tcache_available(tsd)) { - tcache_flush(tsd); - } - /* - * This heuristic is perhaps not the most well-considered. But it - * matches the only idling policy we have experience with in the status - * quo. Over time we should investigate more principled approaches. - */ - if (opt_narenas > ncpus * 2) { - arena_t *arena = arena_choose(tsd, NULL); - if (arena != NULL) { - arena_decay(tsd_tsdn(tsd), arena, false, true); - } - /* - * The missing arena case is not actually an error; a thread - * might be idle before it associates itself to one. This is - * unusual, but not wrong. - */ - } - - ret = 0; -label_return: - return ret; -} - -/******************************************************************************/ - -static int -tcache_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned tcache_ind; - - READONLY(); - VERIFY_READ(unsigned); - if (tcaches_create(tsd, b0get(), &tcache_ind)) { - ret = EFAULT; - goto label_return; - } - READ(tcache_ind, unsigned); - - ret = 0; -label_return: - return ret; -} - -static int -tcache_flush_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned tcache_ind; - - WRITEONLY(); - ASSURED_WRITE(tcache_ind, unsigned); - tcaches_flush(tsd, tcache_ind); - - ret = 0; -label_return: - return ret; -} - -static int -tcache_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned tcache_ind; - - WRITEONLY(); - ASSURED_WRITE(tcache_ind, unsigned); - tcaches_destroy(tsd, tcache_ind); - - ret = 0; -label_return: - return ret; -} - -/******************************************************************************/ - -static int -arena_i_initialized_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - tsdn_t *tsdn = tsd_tsdn(tsd); - unsigned arena_ind; - bool initialized; - - READONLY(); - MIB_UNSIGNED(arena_ind, 1); - - malloc_mutex_lock(tsdn, &ctl_mtx); - initialized = arenas_i(arena_ind)->initialized; - malloc_mutex_unlock(tsdn, &ctl_mtx); - - READ(initialized, bool); - - ret = 0; -label_return: - return ret; -} - -static void -arena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) { - malloc_mutex_lock(tsdn, &ctl_mtx); - { - unsigned narenas = ctl_arenas->narenas; - - /* - * Access via index narenas is deprecated, and scheduled for - * removal in 6.0.0. - */ - if (arena_ind == MALLCTL_ARENAS_ALL || arena_ind == narenas) { - unsigned i; - VARIABLE_ARRAY(arena_t *, tarenas, narenas); - - for (i = 0; i < narenas; i++) { - tarenas[i] = arena_get(tsdn, i, false); - } - - /* - * No further need to hold ctl_mtx, since narenas and - * tarenas contain everything needed below. - */ - malloc_mutex_unlock(tsdn, &ctl_mtx); - - for (i = 0; i < narenas; i++) { - if (tarenas[i] != NULL) { - arena_decay(tsdn, tarenas[i], false, - all); - } - } - } else { - arena_t *tarena; - - assert(arena_ind < narenas); - - tarena = arena_get(tsdn, arena_ind, false); - - /* No further need to hold ctl_mtx. */ - malloc_mutex_unlock(tsdn, &ctl_mtx); - - if (tarena != NULL) { - arena_decay(tsdn, tarena, false, all); - } - } - } -} - -static int -arena_i_decay_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - - NEITHER_READ_NOR_WRITE(); - MIB_UNSIGNED(arena_ind, 1); - arena_i_decay(tsd_tsdn(tsd), arena_ind, false); - - ret = 0; -label_return: - return ret; -} - -static int -arena_i_purge_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - - NEITHER_READ_NOR_WRITE(); - MIB_UNSIGNED(arena_ind, 1); - arena_i_decay(tsd_tsdn(tsd), arena_ind, true); - - ret = 0; -label_return: - return ret; -} - -static int -arena_i_reset_destroy_helper(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen, unsigned *arena_ind, - arena_t **arena) { - int ret; - - NEITHER_READ_NOR_WRITE(); - MIB_UNSIGNED(*arena_ind, 1); - - *arena = arena_get(tsd_tsdn(tsd), *arena_ind, false); - if (*arena == NULL || arena_is_auto(*arena)) { - ret = EFAULT; - goto label_return; - } - - ret = 0; -label_return: - return ret; -} - -static void -arena_reset_prepare_background_thread(tsd_t *tsd, unsigned arena_ind) { - /* Temporarily disable the background thread during arena reset. */ - if (have_background_thread) { - malloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock); - if (background_thread_enabled()) { - background_thread_info_t *info = - background_thread_info_get(arena_ind); - assert(info->state == background_thread_started); - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - info->state = background_thread_paused; - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - } - } -} - -static void -arena_reset_finish_background_thread(tsd_t *tsd, unsigned arena_ind) { - if (have_background_thread) { - if (background_thread_enabled()) { - background_thread_info_t *info = - background_thread_info_get(arena_ind); - assert(info->state == background_thread_paused); - malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); - info->state = background_thread_started; - malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); - } - malloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock); - } -} - -static int -arena_i_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - arena_t *arena; - - ret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp, - newp, newlen, &arena_ind, &arena); - if (ret != 0) { - return ret; - } - - arena_reset_prepare_background_thread(tsd, arena_ind); - arena_reset(tsd, arena); - arena_reset_finish_background_thread(tsd, arena_ind); - - return ret; -} - -static int -arena_i_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - arena_t *arena; - ctl_arena_t *ctl_darena, *ctl_arena; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - - ret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp, - newp, newlen, &arena_ind, &arena); - if (ret != 0) { - goto label_return; - } - - if (arena_nthreads_get(arena, false) != 0 || arena_nthreads_get(arena, - true) != 0) { - ret = EFAULT; - goto label_return; - } - - arena_reset_prepare_background_thread(tsd, arena_ind); - /* Merge stats after resetting and purging arena. */ - arena_reset(tsd, arena); - arena_decay(tsd_tsdn(tsd), arena, false, true); - ctl_darena = arenas_i(MALLCTL_ARENAS_DESTROYED); - ctl_darena->initialized = true; - ctl_arena_refresh(tsd_tsdn(tsd), arena, ctl_darena, arena_ind, true); - /* Destroy arena. */ - arena_destroy(tsd, arena); - ctl_arena = arenas_i(arena_ind); - ctl_arena->initialized = false; - /* Record arena index for later recycling via arenas.create. */ - ql_elm_new(ctl_arena, destroyed_link); - ql_tail_insert(&ctl_arenas->destroyed, ctl_arena, destroyed_link); - arena_reset_finish_background_thread(tsd, arena_ind); - - assert(ret == 0); -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - - return ret; -} - -static int -arena_i_dss_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - const char *dss = NULL; - unsigned arena_ind; - dss_prec_t dss_prec_old = dss_prec_limit; - dss_prec_t dss_prec = dss_prec_limit; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - WRITE(dss, const char *); - MIB_UNSIGNED(arena_ind, 1); - if (dss != NULL) { - int i; - bool match = false; - - for (i = 0; i < dss_prec_limit; i++) { - if (strcmp(dss_prec_names[i], dss) == 0) { - dss_prec = i; - match = true; - break; - } - } - - if (!match) { - ret = EINVAL; - goto label_return; - } - } - - /* - * Access via index narenas is deprecated, and scheduled for removal in - * 6.0.0. - */ - if (arena_ind == MALLCTL_ARENAS_ALL || arena_ind == - ctl_arenas->narenas) { - if (dss_prec != dss_prec_limit && - extent_dss_prec_set(dss_prec)) { - ret = EFAULT; - goto label_return; - } - dss_prec_old = extent_dss_prec_get(); - } else { - arena_t *arena = arena_get(tsd_tsdn(tsd), arena_ind, false); - if (arena == NULL || (dss_prec != dss_prec_limit && - arena_dss_prec_set(arena, dss_prec))) { - ret = EFAULT; - goto label_return; - } - dss_prec_old = arena_dss_prec_get(arena); - } - - dss = dss_prec_names[dss_prec_old]; - READ(dss, const char *); - - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -arena_i_oversize_threshold_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - unsigned arena_ind; - MIB_UNSIGNED(arena_ind, 1); - - arena_t *arena = arena_get(tsd_tsdn(tsd), arena_ind, false); - if (arena == NULL) { - ret = EFAULT; - goto label_return; - } - - if (oldp != NULL && oldlenp != NULL) { - size_t oldval = atomic_load_zu( - &arena->pa_shard.pac.oversize_threshold, ATOMIC_RELAXED); - READ(oldval, size_t); - } - if (newp != NULL) { - if (newlen != sizeof(size_t)) { - ret = EINVAL; - goto label_return; - } - atomic_store_zu(&arena->pa_shard.pac.oversize_threshold, - *(size_t *)newp, ATOMIC_RELAXED); - } - ret = 0; -label_return: - return ret; -} - -static int -arena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen, bool dirty) { - int ret; - unsigned arena_ind; - arena_t *arena; - - MIB_UNSIGNED(arena_ind, 1); - arena = arena_get(tsd_tsdn(tsd), arena_ind, false); - if (arena == NULL) { - ret = EFAULT; - goto label_return; - } - extent_state_t state = dirty ? extent_state_dirty : extent_state_muzzy; - - if (oldp != NULL && oldlenp != NULL) { - size_t oldval = arena_decay_ms_get(arena, state); - READ(oldval, ssize_t); - } - if (newp != NULL) { - if (newlen != sizeof(ssize_t)) { - ret = EINVAL; - goto label_return; - } - if (arena_is_huge(arena_ind) && *(ssize_t *)newp > 0) { - /* - * By default the huge arena purges eagerly. If it is - * set to non-zero decay time afterwards, background - * thread might be needed. - */ - if (background_thread_create(tsd, arena_ind)) { - ret = EFAULT; - goto label_return; - } - } - - if (arena_decay_ms_set(tsd_tsdn(tsd), arena, state, - *(ssize_t *)newp)) { - ret = EFAULT; - goto label_return; - } - } - - ret = 0; -label_return: - return ret; -} - -static int -arena_i_dirty_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - return arena_i_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp, - newlen, true); -} - -static int -arena_i_muzzy_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - return arena_i_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp, - newlen, false); -} - -static int -arena_i_extent_hooks_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - arena_t *arena; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - MIB_UNSIGNED(arena_ind, 1); - if (arena_ind < narenas_total_get()) { - extent_hooks_t *old_extent_hooks; - arena = arena_get(tsd_tsdn(tsd), arena_ind, false); - if (arena == NULL) { - if (arena_ind >= narenas_auto) { - ret = EFAULT; - goto label_return; - } - old_extent_hooks = - (extent_hooks_t *)&ehooks_default_extent_hooks; - READ(old_extent_hooks, extent_hooks_t *); - if (newp != NULL) { - /* Initialize a new arena as a side effect. */ - extent_hooks_t *new_extent_hooks - JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_extent_hooks, extent_hooks_t *); - arena_config_t config = arena_config_default; - config.extent_hooks = new_extent_hooks; - - arena = arena_init(tsd_tsdn(tsd), arena_ind, - &config); - if (arena == NULL) { - ret = EFAULT; - goto label_return; - } - } - } else { - if (newp != NULL) { - extent_hooks_t *new_extent_hooks - JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_extent_hooks, extent_hooks_t *); - old_extent_hooks = arena_set_extent_hooks(tsd, - arena, new_extent_hooks); - READ(old_extent_hooks, extent_hooks_t *); - } else { - old_extent_hooks = - ehooks_get_extent_hooks_ptr( - arena_get_ehooks(arena)); - READ(old_extent_hooks, extent_hooks_t *); - } - } - } else { - ret = EFAULT; - goto label_return; - } - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -arena_i_retain_grow_limit_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - unsigned arena_ind; - arena_t *arena; - - if (!opt_retain) { - /* Only relevant when retain is enabled. */ - return ENOENT; - } - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - MIB_UNSIGNED(arena_ind, 1); - if (arena_ind < narenas_total_get() && (arena = - arena_get(tsd_tsdn(tsd), arena_ind, false)) != NULL) { - size_t old_limit, new_limit; - if (newp != NULL) { - WRITE(new_limit, size_t); - } - bool err = arena_retain_grow_limit_get_set(tsd, arena, - &old_limit, newp != NULL ? &new_limit : NULL); - if (!err) { - READ(old_limit, size_t); - ret = 0; - } else { - ret = EFAULT; - } - } else { - ret = EFAULT; - } -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static const ctl_named_node_t * -arena_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, - size_t i) { - const ctl_named_node_t *ret; - - malloc_mutex_lock(tsdn, &ctl_mtx); - switch (i) { - case MALLCTL_ARENAS_ALL: - case MALLCTL_ARENAS_DESTROYED: - break; - default: - if (i > ctl_arenas->narenas) { - ret = NULL; - goto label_return; - } - break; - } - - ret = super_arena_i_node; -label_return: - malloc_mutex_unlock(tsdn, &ctl_mtx); - return ret; -} - -/******************************************************************************/ - -static int -arenas_narenas_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned narenas; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - READONLY(); - narenas = ctl_arenas->narenas; - READ(narenas, unsigned); - - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -arenas_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen, bool dirty) { - int ret; - - if (oldp != NULL && oldlenp != NULL) { - size_t oldval = (dirty ? arena_dirty_decay_ms_default_get() : - arena_muzzy_decay_ms_default_get()); - READ(oldval, ssize_t); - } - if (newp != NULL) { - if (newlen != sizeof(ssize_t)) { - ret = EINVAL; - goto label_return; - } - if (dirty ? arena_dirty_decay_ms_default_set(*(ssize_t *)newp) - : arena_muzzy_decay_ms_default_set(*(ssize_t *)newp)) { - ret = EFAULT; - goto label_return; - } - } - - ret = 0; -label_return: - return ret; -} - -static int -arenas_dirty_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - return arenas_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp, - newlen, true); -} - -static int -arenas_muzzy_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - return arenas_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp, - newlen, false); -} - -CTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t) -CTL_RO_NL_GEN(arenas_page, PAGE, size_t) -CTL_RO_NL_GEN(arenas_tcache_max, tcache_maxclass, size_t) -CTL_RO_NL_GEN(arenas_nbins, SC_NBINS, unsigned) -CTL_RO_NL_GEN(arenas_nhbins, nhbins, unsigned) -CTL_RO_NL_GEN(arenas_bin_i_size, bin_infos[mib[2]].reg_size, size_t) -CTL_RO_NL_GEN(arenas_bin_i_nregs, bin_infos[mib[2]].nregs, uint32_t) -CTL_RO_NL_GEN(arenas_bin_i_slab_size, bin_infos[mib[2]].slab_size, size_t) -CTL_RO_NL_GEN(arenas_bin_i_nshards, bin_infos[mib[2]].n_shards, uint32_t) -static const ctl_named_node_t * -arenas_bin_i_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t i) { - if (i > SC_NBINS) { - return NULL; - } - return super_arenas_bin_i_node; -} - -CTL_RO_NL_GEN(arenas_nlextents, SC_NSIZES - SC_NBINS, unsigned) -CTL_RO_NL_GEN(arenas_lextent_i_size, sz_index2size(SC_NBINS+(szind_t)mib[2]), - size_t) -static const ctl_named_node_t * -arenas_lextent_i_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t i) { - if (i > SC_NSIZES - SC_NBINS) { - return NULL; - } - return super_arenas_lextent_i_node; -} - -static int -arenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - - VERIFY_READ(unsigned); - arena_config_t config = arena_config_default; - WRITE(config.extent_hooks, extent_hooks_t *); - if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) { - ret = EAGAIN; - goto label_return; - } - READ(arena_ind, unsigned); - - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -experimental_arenas_create_ext_ctl(tsd_t *tsd, - const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned arena_ind; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - - arena_config_t config = arena_config_default; - VERIFY_READ(unsigned); - WRITE(config, arena_config_t); - - if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) { - ret = EAGAIN; - goto label_return; - } - READ(arena_ind, unsigned); - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -arenas_lookup_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - unsigned arena_ind; - void *ptr; - edata_t *edata; - arena_t *arena; - - ptr = NULL; - ret = EINVAL; - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - WRITE(ptr, void *); - edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr); - if (edata == NULL) { - goto label_return; - } - - arena = arena_get_from_edata(edata); - if (arena == NULL) { - goto label_return; - } - - arena_ind = arena_ind_get(arena); - READ(arena_ind, unsigned); - - ret = 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -/******************************************************************************/ - -static int -prof_thread_active_init_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - bool oldval; - - if (!config_prof) { - return ENOENT; - } - - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - oldval = prof_thread_active_init_set(tsd_tsdn(tsd), - *(bool *)newp); - } else { - oldval = opt_prof ? prof_thread_active_init_get(tsd_tsdn(tsd)) : - false; - } - READ(oldval, bool); - - ret = 0; -label_return: - return ret; -} - -static int -prof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - bool oldval; - - if (!config_prof) { - ret = ENOENT; - goto label_return; - } - - if (newp != NULL) { - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - bool val = *(bool *)newp; - if (!opt_prof) { - if (val) { - ret = ENOENT; - goto label_return; - } else { - /* No change needed (already off). */ - oldval = false; - } - } else { - oldval = prof_active_set(tsd_tsdn(tsd), val); - } - } else { - oldval = opt_prof ? prof_active_get(tsd_tsdn(tsd)) : false; - } - READ(oldval, bool); - - ret = 0; -label_return: - return ret; -} - -static int -prof_dump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - const char *filename = NULL; - - if (!config_prof || !opt_prof) { - return ENOENT; - } - - WRITEONLY(); - WRITE(filename, const char *); - - if (prof_mdump(tsd, filename)) { - ret = EFAULT; - goto label_return; - } - - ret = 0; -label_return: - return ret; -} - -static int -prof_gdump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - bool oldval; - - if (!config_prof) { - return ENOENT; - } - - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - oldval = prof_gdump_set(tsd_tsdn(tsd), *(bool *)newp); - } else { - oldval = opt_prof ? prof_gdump_get(tsd_tsdn(tsd)) : false; - } - READ(oldval, bool); - - ret = 0; -label_return: - return ret; -} - -static int -prof_prefix_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - const char *prefix = NULL; - - if (!config_prof || !opt_prof) { - return ENOENT; - } - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - WRITEONLY(); - WRITE(prefix, const char *); - - ret = prof_prefix_set(tsd_tsdn(tsd), prefix) ? EFAULT : 0; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -prof_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - size_t lg_sample = lg_prof_sample; - - if (!config_prof || !opt_prof) { - return ENOENT; - } - - WRITEONLY(); - WRITE(lg_sample, size_t); - if (lg_sample >= (sizeof(uint64_t) << 3)) { - lg_sample = (sizeof(uint64_t) << 3) - 1; - } - - prof_reset(tsd, lg_sample); - - ret = 0; -label_return: - return ret; -} - -CTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t) -CTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t) - -static int -prof_log_start_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - const char *filename = NULL; - - if (!config_prof || !opt_prof) { - return ENOENT; - } - - WRITEONLY(); - WRITE(filename, const char *); - - if (prof_log_start(tsd_tsdn(tsd), filename)) { - ret = EFAULT; - goto label_return; - } - - ret = 0; -label_return: - return ret; -} - -static int -prof_log_stop_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) { - if (!config_prof || !opt_prof) { - return ENOENT; - } - - if (prof_log_stop(tsd_tsdn(tsd))) { - return EFAULT; - } - - return 0; -} - -static int -experimental_hooks_prof_backtrace_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (oldp == NULL && newp == NULL) { - ret = EINVAL; - goto label_return; - } - if (oldp != NULL) { - prof_backtrace_hook_t old_hook = - prof_backtrace_hook_get(); - READ(old_hook, prof_backtrace_hook_t); - } - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - prof_backtrace_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_hook, prof_backtrace_hook_t); - if (new_hook == NULL) { - ret = EINVAL; - goto label_return; - } - prof_backtrace_hook_set(new_hook); - } - ret = 0; -label_return: - return ret; -} - -static int -experimental_hooks_prof_dump_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (oldp == NULL && newp == NULL) { - ret = EINVAL; - goto label_return; - } - if (oldp != NULL) { - prof_dump_hook_t old_hook = - prof_dump_hook_get(); - READ(old_hook, prof_dump_hook_t); - } - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - prof_dump_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_hook, prof_dump_hook_t); - prof_dump_hook_set(new_hook); - } - ret = 0; -label_return: - return ret; -} - -/* For integration test purpose only. No plan to move out of experimental. */ -static int -experimental_hooks_safety_check_abort_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - WRITEONLY(); - if (newp != NULL) { - if (newlen != sizeof(safety_check_abort_hook_t)) { - ret = EINVAL; - goto label_return; - } - safety_check_abort_hook_t hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(hook, safety_check_abort_hook_t); - safety_check_set_abort(hook); - } - ret = 0; -label_return: - return ret; -} - -/******************************************************************************/ - -CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats->allocated, size_t) -CTL_RO_CGEN(config_stats, stats_active, ctl_stats->active, size_t) -CTL_RO_CGEN(config_stats, stats_metadata, ctl_stats->metadata, size_t) -CTL_RO_CGEN(config_stats, stats_metadata_thp, ctl_stats->metadata_thp, size_t) -CTL_RO_CGEN(config_stats, stats_resident, ctl_stats->resident, size_t) -CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats->mapped, size_t) -CTL_RO_CGEN(config_stats, stats_retained, ctl_stats->retained, size_t) - -CTL_RO_CGEN(config_stats, stats_background_thread_num_threads, - ctl_stats->background_thread.num_threads, size_t) -CTL_RO_CGEN(config_stats, stats_background_thread_num_runs, - ctl_stats->background_thread.num_runs, uint64_t) -CTL_RO_CGEN(config_stats, stats_background_thread_run_interval, - nstime_ns(&ctl_stats->background_thread.run_interval), uint64_t) - -CTL_RO_CGEN(config_stats, stats_zero_reallocs, - atomic_load_zu(&zero_realloc_count, ATOMIC_RELAXED), size_t) - -CTL_RO_GEN(stats_arenas_i_dss, arenas_i(mib[2])->dss, const char *) -CTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms, - ssize_t) -CTL_RO_GEN(stats_arenas_i_muzzy_decay_ms, arenas_i(mib[2])->muzzy_decay_ms, - ssize_t) -CTL_RO_GEN(stats_arenas_i_nthreads, arenas_i(mib[2])->nthreads, unsigned) -CTL_RO_GEN(stats_arenas_i_uptime, - nstime_ns(&arenas_i(mib[2])->astats->astats.uptime), uint64_t) -CTL_RO_GEN(stats_arenas_i_pactive, arenas_i(mib[2])->pactive, size_t) -CTL_RO_GEN(stats_arenas_i_pdirty, arenas_i(mib[2])->pdirty, size_t) -CTL_RO_GEN(stats_arenas_i_pmuzzy, arenas_i(mib[2])->pmuzzy, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_mapped, - arenas_i(mib[2])->astats->astats.mapped, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_retained, - arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.retained, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_extent_avail, - arenas_i(mib[2])->astats->astats.pa_shard_stats.edata_avail, size_t) - -CTL_RO_CGEN(config_stats, stats_arenas_i_dirty_npurge, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_dirty.npurge), - uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_dirty_nmadvise, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_dirty.nmadvise), - uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_dirty_purged, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_dirty.purged), - uint64_t) - -CTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_npurge, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_muzzy.npurge), - uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_nmadvise, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_muzzy.nmadvise), - uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_purged, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_muzzy.purged), - uint64_t) - -CTL_RO_CGEN(config_stats, stats_arenas_i_base, - arenas_i(mib[2])->astats->astats.base, - size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_internal, - atomic_load_zu(&arenas_i(mib[2])->astats->astats.internal, ATOMIC_RELAXED), - size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_thp, - arenas_i(mib[2])->astats->astats.metadata_thp, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_tcache_bytes, - arenas_i(mib[2])->astats->astats.tcache_bytes, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_tcache_stashed_bytes, - arenas_i(mib[2])->astats->astats.tcache_stashed_bytes, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_resident, - arenas_i(mib[2])->astats->astats.resident, - size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_abandoned_vm, - atomic_load_zu( - &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.abandoned_vm, - ATOMIC_RELAXED), size_t) - -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_sec_bytes, - arenas_i(mib[2])->astats->secstats.bytes, size_t) - -CTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated, - arenas_i(mib[2])->astats->allocated_small, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc, - arenas_i(mib[2])->astats->nmalloc_small, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_small_ndalloc, - arenas_i(mib[2])->astats->ndalloc_small, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_small_nrequests, - arenas_i(mib[2])->astats->nrequests_small, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_small_nfills, - arenas_i(mib[2])->astats->nfills_small, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_small_nflushes, - arenas_i(mib[2])->astats->nflushes_small, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_large_allocated, - arenas_i(mib[2])->astats->astats.allocated_large, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_large_nmalloc, - arenas_i(mib[2])->astats->astats.nmalloc_large, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc, - arenas_i(mib[2])->astats->astats.ndalloc_large, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests, - arenas_i(mib[2])->astats->astats.nrequests_large, uint64_t) -/* - * Note: "nmalloc_large" here instead of "nfills" in the read. This is - * intentional (large has no batch fill). - */ -CTL_RO_CGEN(config_stats, stats_arenas_i_large_nfills, - arenas_i(mib[2])->astats->astats.nmalloc_large, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_large_nflushes, - arenas_i(mib[2])->astats->astats.nflushes_large, uint64_t) - -/* Lock profiling related APIs below. */ -#define RO_MUTEX_CTL_GEN(n, l) \ -CTL_RO_CGEN(config_stats, stats_##n##_num_ops, \ - l.n_lock_ops, uint64_t) \ -CTL_RO_CGEN(config_stats, stats_##n##_num_wait, \ - l.n_wait_times, uint64_t) \ -CTL_RO_CGEN(config_stats, stats_##n##_num_spin_acq, \ - l.n_spin_acquired, uint64_t) \ -CTL_RO_CGEN(config_stats, stats_##n##_num_owner_switch, \ - l.n_owner_switches, uint64_t) \ -CTL_RO_CGEN(config_stats, stats_##n##_total_wait_time, \ - nstime_ns(&l.tot_wait_time), uint64_t) \ -CTL_RO_CGEN(config_stats, stats_##n##_max_wait_time, \ - nstime_ns(&l.max_wait_time), uint64_t) \ -CTL_RO_CGEN(config_stats, stats_##n##_max_num_thds, \ - l.max_n_thds, uint32_t) - -/* Global mutexes. */ -#define OP(mtx) \ - RO_MUTEX_CTL_GEN(mutexes_##mtx, \ - ctl_stats->mutex_prof_data[global_prof_mutex_##mtx]) -MUTEX_PROF_GLOBAL_MUTEXES -#undef OP - -/* Per arena mutexes */ -#define OP(mtx) RO_MUTEX_CTL_GEN(arenas_i_mutexes_##mtx, \ - arenas_i(mib[2])->astats->astats.mutex_prof_data[arena_prof_mutex_##mtx]) -MUTEX_PROF_ARENA_MUTEXES -#undef OP - -/* tcache bin mutex */ -RO_MUTEX_CTL_GEN(arenas_i_bins_j_mutex, - arenas_i(mib[2])->astats->bstats[mib[4]].mutex_data) -#undef RO_MUTEX_CTL_GEN - -/* Resets all mutex stats, including global, arena and bin mutexes. */ -static int -stats_mutexes_reset_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) { - if (!config_stats) { - return ENOENT; - } - - tsdn_t *tsdn = tsd_tsdn(tsd); - -#define MUTEX_PROF_RESET(mtx) \ - malloc_mutex_lock(tsdn, &mtx); \ - malloc_mutex_prof_data_reset(tsdn, &mtx); \ - malloc_mutex_unlock(tsdn, &mtx); - - /* Global mutexes: ctl and prof. */ - MUTEX_PROF_RESET(ctl_mtx); - if (have_background_thread) { - MUTEX_PROF_RESET(background_thread_lock); - } - if (config_prof && opt_prof) { - MUTEX_PROF_RESET(bt2gctx_mtx); - MUTEX_PROF_RESET(tdatas_mtx); - MUTEX_PROF_RESET(prof_dump_mtx); - MUTEX_PROF_RESET(prof_recent_alloc_mtx); - MUTEX_PROF_RESET(prof_recent_dump_mtx); - MUTEX_PROF_RESET(prof_stats_mtx); - } - - /* Per arena mutexes. */ - unsigned n = narenas_total_get(); - - for (unsigned i = 0; i < n; i++) { - arena_t *arena = arena_get(tsdn, i, false); - if (!arena) { - continue; - } - MUTEX_PROF_RESET(arena->large_mtx); - MUTEX_PROF_RESET(arena->pa_shard.edata_cache.mtx); - MUTEX_PROF_RESET(arena->pa_shard.pac.ecache_dirty.mtx); - MUTEX_PROF_RESET(arena->pa_shard.pac.ecache_muzzy.mtx); - MUTEX_PROF_RESET(arena->pa_shard.pac.ecache_retained.mtx); - MUTEX_PROF_RESET(arena->pa_shard.pac.decay_dirty.mtx); - MUTEX_PROF_RESET(arena->pa_shard.pac.decay_muzzy.mtx); - MUTEX_PROF_RESET(arena->tcache_ql_mtx); - MUTEX_PROF_RESET(arena->base->mtx); - - for (szind_t j = 0; j < SC_NBINS; j++) { - for (unsigned k = 0; k < bin_infos[j].n_shards; k++) { - bin_t *bin = arena_get_bin(arena, j, k); - MUTEX_PROF_RESET(bin->lock); - } - } - } -#undef MUTEX_PROF_RESET - return 0; -} - -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nmalloc, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.ndalloc, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nrequests, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.curregs, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nfills, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nfills, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nflushes, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nflushes, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nslabs, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nslabs, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreslabs, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.reslabs, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curslabs, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.curslabs, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nonfull_slabs, - arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nonfull_slabs, size_t) - -static const ctl_named_node_t * -stats_arenas_i_bins_j_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t j) { - if (j > SC_NBINS) { - return NULL; - } - return super_stats_arenas_i_bins_j_node; -} - -CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nmalloc, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->lstats[mib[4]].nmalloc), uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_ndalloc, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->lstats[mib[4]].ndalloc), uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nrequests, - locked_read_u64_unsynchronized( - &arenas_i(mib[2])->astats->lstats[mib[4]].nrequests), uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_curlextents, - arenas_i(mib[2])->astats->lstats[mib[4]].curlextents, size_t) - -static const ctl_named_node_t * -stats_arenas_i_lextents_j_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t j) { - if (j > SC_NSIZES - SC_NBINS) { - return NULL; - } - return super_stats_arenas_i_lextents_j_node; -} - -CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_ndirty, - arenas_i(mib[2])->astats->estats[mib[4]].ndirty, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_nmuzzy, - arenas_i(mib[2])->astats->estats[mib[4]].nmuzzy, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_nretained, - arenas_i(mib[2])->astats->estats[mib[4]].nretained, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_dirty_bytes, - arenas_i(mib[2])->astats->estats[mib[4]].dirty_bytes, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_muzzy_bytes, - arenas_i(mib[2])->astats->estats[mib[4]].muzzy_bytes, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_retained_bytes, - arenas_i(mib[2])->astats->estats[mib[4]].retained_bytes, size_t); - -static const ctl_named_node_t * -stats_arenas_i_extents_j_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t j) { - if (j >= SC_NPSIZES) { - return NULL; - } - return super_stats_arenas_i_extents_j_node; -} - -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_npurge_passes, - arenas_i(mib[2])->astats->hpastats.nonderived_stats.npurge_passes, uint64_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_npurges, - arenas_i(mib[2])->astats->hpastats.nonderived_stats.npurges, uint64_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nhugifies, - arenas_i(mib[2])->astats->hpastats.nonderived_stats.nhugifies, uint64_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_ndehugifies, - arenas_i(mib[2])->astats->hpastats.nonderived_stats.ndehugifies, uint64_t); - -/* Full, nonhuge */ -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_npageslabs_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[0].npageslabs, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_nactive_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[0].nactive, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_ndirty_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[0].ndirty, size_t); - -/* Full, huge */ -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_npageslabs_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[1].npageslabs, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_nactive_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[1].nactive, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_ndirty_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[1].ndirty, size_t); - -/* Empty, nonhuge */ -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_npageslabs_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[0].npageslabs, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_nactive_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[0].nactive, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_ndirty_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[0].ndirty, size_t); - -/* Empty, huge */ -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_npageslabs_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[1].npageslabs, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_nactive_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[1].nactive, size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_ndirty_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[1].ndirty, size_t); - -/* Nonfull, nonhuge */ -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][0].npageslabs, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][0].nactive, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_nonhuge, - arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][0].ndirty, - size_t); - -/* Nonfull, huge */ -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][1].npageslabs, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][1].nactive, - size_t); -CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_huge, - arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][1].ndirty, - size_t); - -static const ctl_named_node_t * -stats_arenas_i_hpa_shard_nonfull_slabs_j_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t j) { - if (j >= PSSET_NPSIZES) { - return NULL; - } - return super_stats_arenas_i_hpa_shard_nonfull_slabs_j_node; -} - -static bool -ctl_arenas_i_verify(size_t i) { - size_t a = arenas_i2a_impl(i, true, true); - if (a == UINT_MAX || !ctl_arenas->arenas[a]->initialized) { - return true; - } - - return false; -} - -static const ctl_named_node_t * -stats_arenas_i_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t i) { - const ctl_named_node_t *ret; - - malloc_mutex_lock(tsdn, &ctl_mtx); - if (ctl_arenas_i_verify(i)) { - ret = NULL; - goto label_return; - } - - ret = super_stats_arenas_i_node; -label_return: - malloc_mutex_unlock(tsdn, &ctl_mtx); - return ret; -} - -static int -experimental_hooks_install_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - if (oldp == NULL || oldlenp == NULL|| newp == NULL) { - ret = EINVAL; - goto label_return; - } - /* - * Note: this is a *private* struct. This is an experimental interface; - * forcing the user to know the jemalloc internals well enough to - * extract the ABI hopefully ensures nobody gets too comfortable with - * this API, which can change at a moment's notice. - */ - hooks_t hooks; - WRITE(hooks, hooks_t); - void *handle = hook_install(tsd_tsdn(tsd), &hooks); - if (handle == NULL) { - ret = EAGAIN; - goto label_return; - } - READ(handle, void *); - - ret = 0; -label_return: - return ret; -} - -static int -experimental_hooks_remove_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - WRITEONLY(); - void *handle = NULL; - WRITE(handle, void *); - if (handle == NULL) { - ret = EINVAL; - goto label_return; - } - hook_remove(tsd_tsdn(tsd), handle); - ret = 0; -label_return: - return ret; -} - -static int -experimental_thread_activity_callback_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (!config_stats) { - return ENOENT; - } - - activity_callback_thunk_t t_old = tsd_activity_callback_thunk_get(tsd); - READ(t_old, activity_callback_thunk_t); - - if (newp != NULL) { - /* - * This initialization is unnecessary. If it's omitted, though, - * clang gets confused and warns on the subsequent use of t_new. - */ - activity_callback_thunk_t t_new = {NULL, NULL}; - WRITE(t_new, activity_callback_thunk_t); - tsd_activity_callback_thunk_set(tsd, t_new); - } - ret = 0; -label_return: - return ret; -} - -/* - * Output six memory utilization entries for an input pointer, the first one of - * type (void *) and the remaining five of type size_t, describing the following - * (in the same order): - * - * (a) memory address of the extent a potential reallocation would go into, - * == the five fields below describe about the extent the pointer resides in == - * (b) number of free regions in the extent, - * (c) number of regions in the extent, - * (d) size of the extent in terms of bytes, - * (e) total number of free regions in the bin the extent belongs to, and - * (f) total number of regions in the bin the extent belongs to. - * - * Note that "(e)" and "(f)" are only available when stats are enabled; - * otherwise their values are undefined. - * - * This API is mainly intended for small class allocations, where extents are - * used as slab. Note that if the bin the extent belongs to is completely - * full, "(a)" will be NULL. - * - * In case of large class allocations, "(a)" will be NULL, and "(e)" and "(f)" - * will be zero (if stats are enabled; otherwise undefined). The other three - * fields will be properly set though the values are trivial: "(b)" will be 0, - * "(c)" will be 1, and "(d)" will be the usable size. - * - * The input pointer and size are respectively passed in by newp and newlen, - * and the output fields and size are respectively oldp and *oldlenp. - * - * It can be beneficial to define the following macros to make it easier to - * access the output: - * - * #define SLABCUR_READ(out) (*(void **)out) - * #define COUNTS(out) ((size_t *)((void **)out + 1)) - * #define NFREE_READ(out) COUNTS(out)[0] - * #define NREGS_READ(out) COUNTS(out)[1] - * #define SIZE_READ(out) COUNTS(out)[2] - * #define BIN_NFREE_READ(out) COUNTS(out)[3] - * #define BIN_NREGS_READ(out) COUNTS(out)[4] - * - * and then write e.g. NFREE_READ(oldp) to fetch the output. See the unit test - * test_query in test/unit/extent_util.c for an example. - * - * For a typical defragmentation workflow making use of this API for - * understanding the fragmentation level, please refer to the comment for - * experimental_utilization_batch_query_ctl. - * - * It's up to the application how to determine the significance of - * fragmentation relying on the outputs returned. Possible choices are: - * - * (a) if extent utilization ratio is below certain threshold, - * (b) if extent memory consumption is above certain threshold, - * (c) if extent utilization ratio is significantly below bin utilization ratio, - * (d) if input pointer deviates a lot from potential reallocation address, or - * (e) some selection/combination of the above. - * - * The caller needs to make sure that the input/output arguments are valid, - * in particular, that the size of the output is correct, i.e.: - * - * *oldlenp = sizeof(void *) + sizeof(size_t) * 5 - * - * Otherwise, the function immediately returns EINVAL without touching anything. - * - * In the rare case where there's no associated extent found for the input - * pointer, the function zeros out all output fields and return. Please refer - * to the comment for experimental_utilization_batch_query_ctl to understand the - * motivation from C++. - */ -static int -experimental_utilization_query_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - assert(sizeof(inspect_extent_util_stats_verbose_t) - == sizeof(void *) + sizeof(size_t) * 5); - - if (oldp == NULL || oldlenp == NULL - || *oldlenp != sizeof(inspect_extent_util_stats_verbose_t) - || newp == NULL) { - ret = EINVAL; - goto label_return; - } - - void *ptr = NULL; - WRITE(ptr, void *); - inspect_extent_util_stats_verbose_t *util_stats - = (inspect_extent_util_stats_verbose_t *)oldp; - inspect_extent_util_stats_verbose_get(tsd_tsdn(tsd), ptr, - &util_stats->nfree, &util_stats->nregs, &util_stats->size, - &util_stats->bin_nfree, &util_stats->bin_nregs, - &util_stats->slabcur_addr); - ret = 0; - -label_return: - return ret; -} - -/* - * Given an input array of pointers, output three memory utilization entries of - * type size_t for each input pointer about the extent it resides in: - * - * (a) number of free regions in the extent, - * (b) number of regions in the extent, and - * (c) size of the extent in terms of bytes. - * - * This API is mainly intended for small class allocations, where extents are - * used as slab. In case of large class allocations, the outputs are trivial: - * "(a)" will be 0, "(b)" will be 1, and "(c)" will be the usable size. - * - * Note that multiple input pointers may reside on a same extent so the output - * fields may contain duplicates. - * - * The format of the input/output looks like: - * - * input[0]: 1st_pointer_to_query | output[0]: 1st_extent_n_free_regions - * | output[1]: 1st_extent_n_regions - * | output[2]: 1st_extent_size - * input[1]: 2nd_pointer_to_query | output[3]: 2nd_extent_n_free_regions - * | output[4]: 2nd_extent_n_regions - * | output[5]: 2nd_extent_size - * ... | ... - * - * The input array and size are respectively passed in by newp and newlen, and - * the output array and size are respectively oldp and *oldlenp. - * - * It can be beneficial to define the following macros to make it easier to - * access the output: - * - * #define NFREE_READ(out, i) out[(i) * 3] - * #define NREGS_READ(out, i) out[(i) * 3 + 1] - * #define SIZE_READ(out, i) out[(i) * 3 + 2] - * - * and then write e.g. NFREE_READ(oldp, i) to fetch the output. See the unit - * test test_batch in test/unit/extent_util.c for a concrete example. - * - * A typical workflow would be composed of the following steps: - * - * (1) flush tcache: mallctl("thread.tcache.flush", ...) - * (2) initialize input array of pointers to query fragmentation - * (3) allocate output array to hold utilization statistics - * (4) query utilization: mallctl("experimental.utilization.batch_query", ...) - * (5) (optional) decide if it's worthwhile to defragment; otherwise stop here - * (6) disable tcache: mallctl("thread.tcache.enabled", ...) - * (7) defragment allocations with significant fragmentation, e.g.: - * for each allocation { - * if it's fragmented { - * malloc(...); - * memcpy(...); - * free(...); - * } - * } - * (8) enable tcache: mallctl("thread.tcache.enabled", ...) - * - * The application can determine the significance of fragmentation themselves - * relying on the statistics returned, both at the overall level i.e. step "(5)" - * and at individual allocation level i.e. within step "(7)". Possible choices - * are: - * - * (a) whether memory utilization ratio is below certain threshold, - * (b) whether memory consumption is above certain threshold, or - * (c) some combination of the two. - * - * The caller needs to make sure that the input/output arrays are valid and - * their sizes are proper as well as matched, meaning: - * - * (a) newlen = n_pointers * sizeof(const void *) - * (b) *oldlenp = n_pointers * sizeof(size_t) * 3 - * (c) n_pointers > 0 - * - * Otherwise, the function immediately returns EINVAL without touching anything. - * - * In the rare case where there's no associated extent found for some pointers, - * rather than immediately terminating the computation and raising an error, - * the function simply zeros out the corresponding output fields and continues - * the computation until all input pointers are handled. The motivations of - * such a design are as follows: - * - * (a) The function always either processes nothing or processes everything, and - * never leaves the output half touched and half untouched. - * - * (b) It facilitates usage needs especially common in C++. A vast variety of - * C++ objects are instantiated with multiple dynamic memory allocations. For - * example, std::string and std::vector typically use at least two allocations, - * one for the metadata and one for the actual content. Other types may use - * even more allocations. When inquiring about utilization statistics, the - * caller often wants to examine into all such allocations, especially internal - * one(s), rather than just the topmost one. The issue comes when some - * implementations do certain optimizations to reduce/aggregate some internal - * allocations, e.g. putting short strings directly into the metadata, and such - * decisions are not known to the caller. Therefore, we permit pointers to - * memory usages that may not be returned by previous malloc calls, and we - * provide the caller a convenient way to identify such cases. - */ -static int -experimental_utilization_batch_query_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - assert(sizeof(inspect_extent_util_stats_t) == sizeof(size_t) * 3); - - const size_t len = newlen / sizeof(const void *); - if (oldp == NULL || oldlenp == NULL || newp == NULL || newlen == 0 - || newlen != len * sizeof(const void *) - || *oldlenp != len * sizeof(inspect_extent_util_stats_t)) { - ret = EINVAL; - goto label_return; - } - - void **ptrs = (void **)newp; - inspect_extent_util_stats_t *util_stats = - (inspect_extent_util_stats_t *)oldp; - size_t i; - for (i = 0; i < len; ++i) { - inspect_extent_util_stats_get(tsd_tsdn(tsd), ptrs[i], - &util_stats[i].nfree, &util_stats[i].nregs, - &util_stats[i].size); - } - ret = 0; - -label_return: - return ret; -} - -static const ctl_named_node_t * -experimental_arenas_i_index(tsdn_t *tsdn, const size_t *mib, - size_t miblen, size_t i) { - const ctl_named_node_t *ret; - - malloc_mutex_lock(tsdn, &ctl_mtx); - if (ctl_arenas_i_verify(i)) { - ret = NULL; - goto label_return; - } - ret = super_experimental_arenas_i_node; -label_return: - malloc_mutex_unlock(tsdn, &ctl_mtx); - return ret; -} - -static int -experimental_arenas_i_pactivep_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - if (!config_stats) { - return ENOENT; - } - if (oldp == NULL || oldlenp == NULL || *oldlenp != sizeof(size_t *)) { - return EINVAL; - } - - unsigned arena_ind; - arena_t *arena; - int ret; - size_t *pactivep; - - malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); - READONLY(); - MIB_UNSIGNED(arena_ind, 2); - if (arena_ind < narenas_total_get() && (arena = - arena_get(tsd_tsdn(tsd), arena_ind, false)) != NULL) { -#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS) || \ - defined(JEMALLOC_GCC_SYNC_ATOMICS) || defined(_MSC_VER) - /* Expose the underlying counter for fast read. */ - pactivep = (size_t *)&(arena->pa_shard.nactive.repr); - READ(pactivep, size_t *); - ret = 0; -#else - ret = EFAULT; -#endif - } else { - ret = EFAULT; - } -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); - return ret; -} - -static int -experimental_prof_recent_alloc_max_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (!(config_prof && opt_prof)) { - ret = ENOENT; - goto label_return; - } - - ssize_t old_max; - if (newp != NULL) { - if (newlen != sizeof(ssize_t)) { - ret = EINVAL; - goto label_return; - } - ssize_t max = *(ssize_t *)newp; - if (max < -1) { - ret = EINVAL; - goto label_return; - } - old_max = prof_recent_alloc_max_ctl_write(tsd, max); - } else { - old_max = prof_recent_alloc_max_ctl_read(); - } - READ(old_max, ssize_t); - - ret = 0; - -label_return: - return ret; -} - -typedef struct write_cb_packet_s write_cb_packet_t; -struct write_cb_packet_s { - write_cb_t *write_cb; - void *cbopaque; -}; - -static int -experimental_prof_recent_alloc_dump_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (!(config_prof && opt_prof)) { - ret = ENOENT; - goto label_return; - } - - assert(sizeof(write_cb_packet_t) == sizeof(void *) * 2); - - WRITEONLY(); - write_cb_packet_t write_cb_packet; - ASSURED_WRITE(write_cb_packet, write_cb_packet_t); - - prof_recent_alloc_dump(tsd, write_cb_packet.write_cb, - write_cb_packet.cbopaque); - - ret = 0; - -label_return: - return ret; -} - -typedef struct batch_alloc_packet_s batch_alloc_packet_t; -struct batch_alloc_packet_s { - void **ptrs; - size_t num; - size_t size; - int flags; -}; - -static int -experimental_batch_alloc_ctl(tsd_t *tsd, const size_t *mib, - size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - VERIFY_READ(size_t); - - batch_alloc_packet_t batch_alloc_packet; - ASSURED_WRITE(batch_alloc_packet, batch_alloc_packet_t); - size_t filled = batch_alloc(batch_alloc_packet.ptrs, - batch_alloc_packet.num, batch_alloc_packet.size, - batch_alloc_packet.flags); - READ(filled, size_t); - - ret = 0; - -label_return: - return ret; -} - -static int -prof_stats_bins_i_live_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned binind; - prof_stats_t stats; - - if (!(config_prof && opt_prof && opt_prof_stats)) { - ret = ENOENT; - goto label_return; - } - - READONLY(); - MIB_UNSIGNED(binind, 3); - if (binind >= SC_NBINS) { - ret = EINVAL; - goto label_return; - } - prof_stats_get_live(tsd, (szind_t)binind, &stats); - READ(stats, prof_stats_t); - - ret = 0; -label_return: - return ret; -} - -static int -prof_stats_bins_i_accum_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned binind; - prof_stats_t stats; - - if (!(config_prof && opt_prof && opt_prof_stats)) { - ret = ENOENT; - goto label_return; - } - - READONLY(); - MIB_UNSIGNED(binind, 3); - if (binind >= SC_NBINS) { - ret = EINVAL; - goto label_return; - } - prof_stats_get_accum(tsd, (szind_t)binind, &stats); - READ(stats, prof_stats_t); - - ret = 0; -label_return: - return ret; -} - -static const ctl_named_node_t * -prof_stats_bins_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, - size_t i) { - if (!(config_prof && opt_prof && opt_prof_stats)) { - return NULL; - } - if (i >= SC_NBINS) { - return NULL; - } - return super_prof_stats_bins_i_node; -} - -static int -prof_stats_lextents_i_live_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned lextent_ind; - prof_stats_t stats; - - if (!(config_prof && opt_prof && opt_prof_stats)) { - ret = ENOENT; - goto label_return; - } - - READONLY(); - MIB_UNSIGNED(lextent_ind, 3); - if (lextent_ind >= SC_NSIZES - SC_NBINS) { - ret = EINVAL; - goto label_return; - } - prof_stats_get_live(tsd, (szind_t)(lextent_ind + SC_NBINS), &stats); - READ(stats, prof_stats_t); - - ret = 0; -label_return: - return ret; -} - -static int -prof_stats_lextents_i_accum_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - unsigned lextent_ind; - prof_stats_t stats; - - if (!(config_prof && opt_prof && opt_prof_stats)) { - ret = ENOENT; - goto label_return; - } - - READONLY(); - MIB_UNSIGNED(lextent_ind, 3); - if (lextent_ind >= SC_NSIZES - SC_NBINS) { - ret = EINVAL; - goto label_return; - } - prof_stats_get_accum(tsd, (szind_t)(lextent_ind + SC_NBINS), &stats); - READ(stats, prof_stats_t); - - ret = 0; -label_return: - return ret; -} - -static const ctl_named_node_t * -prof_stats_lextents_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, - size_t i) { - if (!(config_prof && opt_prof && opt_prof_stats)) { - return NULL; - } - if (i >= SC_NSIZES - SC_NBINS) { - return NULL; - } - return super_prof_stats_lextents_i_node; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/decay.c b/fluent-bit/lib/jemalloc-5.3.0/src/decay.c deleted file mode 100644 index d801b2bc..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/decay.c +++ /dev/null @@ -1,295 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/decay.h" - -static const uint64_t h_steps[SMOOTHSTEP_NSTEPS] = { -#define STEP(step, h, x, y) \ - h, - SMOOTHSTEP -#undef STEP -}; - -/* - * Generate a new deadline that is uniformly random within the next epoch after - * the current one. - */ -void -decay_deadline_init(decay_t *decay) { - nstime_copy(&decay->deadline, &decay->epoch); - nstime_add(&decay->deadline, &decay->interval); - if (decay_ms_read(decay) > 0) { - nstime_t jitter; - - nstime_init(&jitter, prng_range_u64(&decay->jitter_state, - nstime_ns(&decay->interval))); - nstime_add(&decay->deadline, &jitter); - } -} - -void -decay_reinit(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms) { - atomic_store_zd(&decay->time_ms, decay_ms, ATOMIC_RELAXED); - if (decay_ms > 0) { - nstime_init(&decay->interval, (uint64_t)decay_ms * - KQU(1000000)); - nstime_idivide(&decay->interval, SMOOTHSTEP_NSTEPS); - } - - nstime_copy(&decay->epoch, cur_time); - decay->jitter_state = (uint64_t)(uintptr_t)decay; - decay_deadline_init(decay); - decay->nunpurged = 0; - memset(decay->backlog, 0, SMOOTHSTEP_NSTEPS * sizeof(size_t)); -} - -bool -decay_init(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms) { - if (config_debug) { - for (size_t i = 0; i < sizeof(decay_t); i++) { - assert(((char *)decay)[i] == 0); - } - decay->ceil_npages = 0; - } - if (malloc_mutex_init(&decay->mtx, "decay", WITNESS_RANK_DECAY, - malloc_mutex_rank_exclusive)) { - return true; - } - decay->purging = false; - decay_reinit(decay, cur_time, decay_ms); - return false; -} - -bool -decay_ms_valid(ssize_t decay_ms) { - if (decay_ms < -1) { - return false; - } - if (decay_ms == -1 || (uint64_t)decay_ms <= NSTIME_SEC_MAX * - KQU(1000)) { - return true; - } - return false; -} - -static void -decay_maybe_update_time(decay_t *decay, nstime_t *new_time) { - if (unlikely(!nstime_monotonic() && nstime_compare(&decay->epoch, - new_time) > 0)) { - /* - * Time went backwards. Move the epoch back in time and - * generate a new deadline, with the expectation that time - * typically flows forward for long enough periods of time that - * epochs complete. Unfortunately, this strategy is susceptible - * to clock jitter triggering premature epoch advances, but - * clock jitter estimation and compensation isn't feasible here - * because calls into this code are event-driven. - */ - nstime_copy(&decay->epoch, new_time); - decay_deadline_init(decay); - } else { - /* Verify that time does not go backwards. */ - assert(nstime_compare(&decay->epoch, new_time) <= 0); - } -} - -static size_t -decay_backlog_npages_limit(const decay_t *decay) { - /* - * For each element of decay_backlog, multiply by the corresponding - * fixed-point smoothstep decay factor. Sum the products, then divide - * to round down to the nearest whole number of pages. - */ - uint64_t sum = 0; - for (unsigned i = 0; i < SMOOTHSTEP_NSTEPS; i++) { - sum += decay->backlog[i] * h_steps[i]; - } - size_t npages_limit_backlog = (size_t)(sum >> SMOOTHSTEP_BFP); - - return npages_limit_backlog; -} - -/* - * Update backlog, assuming that 'nadvance_u64' time intervals have passed. - * Trailing 'nadvance_u64' records should be erased and 'current_npages' is - * placed as the newest record. - */ -static void -decay_backlog_update(decay_t *decay, uint64_t nadvance_u64, - size_t current_npages) { - if (nadvance_u64 >= SMOOTHSTEP_NSTEPS) { - memset(decay->backlog, 0, (SMOOTHSTEP_NSTEPS-1) * - sizeof(size_t)); - } else { - size_t nadvance_z = (size_t)nadvance_u64; - - assert((uint64_t)nadvance_z == nadvance_u64); - - memmove(decay->backlog, &decay->backlog[nadvance_z], - (SMOOTHSTEP_NSTEPS - nadvance_z) * sizeof(size_t)); - if (nadvance_z > 1) { - memset(&decay->backlog[SMOOTHSTEP_NSTEPS - - nadvance_z], 0, (nadvance_z-1) * sizeof(size_t)); - } - } - - size_t npages_delta = (current_npages > decay->nunpurged) ? - current_npages - decay->nunpurged : 0; - decay->backlog[SMOOTHSTEP_NSTEPS-1] = npages_delta; - - if (config_debug) { - if (current_npages > decay->ceil_npages) { - decay->ceil_npages = current_npages; - } - size_t npages_limit = decay_backlog_npages_limit(decay); - assert(decay->ceil_npages >= npages_limit); - if (decay->ceil_npages > npages_limit) { - decay->ceil_npages = npages_limit; - } - } -} - -static inline bool -decay_deadline_reached(const decay_t *decay, const nstime_t *time) { - return (nstime_compare(&decay->deadline, time) <= 0); -} - -uint64_t -decay_npages_purge_in(decay_t *decay, nstime_t *time, size_t npages_new) { - uint64_t decay_interval_ns = decay_epoch_duration_ns(decay); - size_t n_epoch = (size_t)(nstime_ns(time) / decay_interval_ns); - - uint64_t npages_purge; - if (n_epoch >= SMOOTHSTEP_NSTEPS) { - npages_purge = npages_new; - } else { - uint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1]; - assert(h_steps_max >= - h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]); - npages_purge = npages_new * (h_steps_max - - h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]); - npages_purge >>= SMOOTHSTEP_BFP; - } - return npages_purge; -} - -bool -decay_maybe_advance_epoch(decay_t *decay, nstime_t *new_time, - size_t npages_current) { - /* Handle possible non-monotonicity of time. */ - decay_maybe_update_time(decay, new_time); - - if (!decay_deadline_reached(decay, new_time)) { - return false; - } - nstime_t delta; - nstime_copy(&delta, new_time); - nstime_subtract(&delta, &decay->epoch); - - uint64_t nadvance_u64 = nstime_divide(&delta, &decay->interval); - assert(nadvance_u64 > 0); - - /* Add nadvance_u64 decay intervals to epoch. */ - nstime_copy(&delta, &decay->interval); - nstime_imultiply(&delta, nadvance_u64); - nstime_add(&decay->epoch, &delta); - - /* Set a new deadline. */ - decay_deadline_init(decay); - - /* Update the backlog. */ - decay_backlog_update(decay, nadvance_u64, npages_current); - - decay->npages_limit = decay_backlog_npages_limit(decay); - decay->nunpurged = (decay->npages_limit > npages_current) ? - decay->npages_limit : npages_current; - - return true; -} - -/* - * Calculate how many pages should be purged after 'interval'. - * - * First, calculate how many pages should remain at the moment, then subtract - * the number of pages that should remain after 'interval'. The difference is - * how many pages should be purged until then. - * - * The number of pages that should remain at a specific moment is calculated - * like this: pages(now) = sum(backlog[i] * h_steps[i]). After 'interval' - * passes, backlog would shift 'interval' positions to the left and sigmoid - * curve would be applied starting with backlog[interval]. - * - * The implementation doesn't directly map to the description, but it's - * essentially the same calculation, optimized to avoid iterating over - * [interval..SMOOTHSTEP_NSTEPS) twice. - */ -static inline size_t -decay_npurge_after_interval(decay_t *decay, size_t interval) { - size_t i; - uint64_t sum = 0; - for (i = 0; i < interval; i++) { - sum += decay->backlog[i] * h_steps[i]; - } - for (; i < SMOOTHSTEP_NSTEPS; i++) { - sum += decay->backlog[i] * - (h_steps[i] - h_steps[i - interval]); - } - - return (size_t)(sum >> SMOOTHSTEP_BFP); -} - -uint64_t decay_ns_until_purge(decay_t *decay, size_t npages_current, - uint64_t npages_threshold) { - if (!decay_gradually(decay)) { - return DECAY_UNBOUNDED_TIME_TO_PURGE; - } - uint64_t decay_interval_ns = decay_epoch_duration_ns(decay); - assert(decay_interval_ns > 0); - if (npages_current == 0) { - unsigned i; - for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) { - if (decay->backlog[i] > 0) { - break; - } - } - if (i == SMOOTHSTEP_NSTEPS) { - /* No dirty pages recorded. Sleep indefinitely. */ - return DECAY_UNBOUNDED_TIME_TO_PURGE; - } - } - if (npages_current <= npages_threshold) { - /* Use max interval. */ - return decay_interval_ns * SMOOTHSTEP_NSTEPS; - } - - /* Minimal 2 intervals to ensure reaching next epoch deadline. */ - size_t lb = 2; - size_t ub = SMOOTHSTEP_NSTEPS; - - size_t npurge_lb, npurge_ub; - npurge_lb = decay_npurge_after_interval(decay, lb); - if (npurge_lb > npages_threshold) { - return decay_interval_ns * lb; - } - npurge_ub = decay_npurge_after_interval(decay, ub); - if (npurge_ub < npages_threshold) { - return decay_interval_ns * ub; - } - - unsigned n_search = 0; - size_t target, npurge; - while ((npurge_lb + npages_threshold < npurge_ub) && (lb + 2 < ub)) { - target = (lb + ub) / 2; - npurge = decay_npurge_after_interval(decay, target); - if (npurge > npages_threshold) { - ub = target; - npurge_ub = npurge; - } else { - lb = target; - npurge_lb = npurge; - } - assert(n_search < lg_floor(SMOOTHSTEP_NSTEPS) + 1); - ++n_search; - } - return decay_interval_ns * (ub + lb) / 2; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/div.c b/fluent-bit/lib/jemalloc-5.3.0/src/div.c deleted file mode 100644 index 808892a1..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/div.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" - -#include "jemalloc/internal/div.h" - -#include "jemalloc/internal/assert.h" - -/* - * Suppose we have n = q * d, all integers. We know n and d, and want q = n / d. - * - * For any k, we have (here, all division is exact; not C-style rounding): - * floor(ceil(2^k / d) * n / 2^k) = floor((2^k + r) / d * n / 2^k), where - * r = (-2^k) mod d. - * - * Expanding this out: - * ... = floor(2^k / d * n / 2^k + r / d * n / 2^k) - * = floor(n / d + (r / d) * (n / 2^k)). - * - * The fractional part of n / d is 0 (because of the assumption that d divides n - * exactly), so we have: - * ... = n / d + floor((r / d) * (n / 2^k)) - * - * So that our initial expression is equal to the quantity we seek, so long as - * (r / d) * (n / 2^k) < 1. - * - * r is a remainder mod d, so r < d and r / d < 1 always. We can make - * n / 2 ^ k < 1 by setting k = 32. This gets us a value of magic that works. - */ - -void -div_init(div_info_t *div_info, size_t d) { - /* Nonsensical. */ - assert(d != 0); - /* - * This would make the value of magic too high to fit into a uint32_t - * (we would want magic = 2^32 exactly). This would mess with code gen - * on 32-bit machines. - */ - assert(d != 1); - - uint64_t two_to_k = ((uint64_t)1 << 32); - uint32_t magic = (uint32_t)(two_to_k / d); - - /* - * We want magic = ceil(2^k / d), but C gives us floor. We have to - * increment it unless the result was exact (i.e. unless d is a power of - * two). - */ - if (two_to_k % d != 0) { - magic++; - } - div_info->magic = magic; -#ifdef JEMALLOC_DEBUG - div_info->d = d; -#endif -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/ecache.c b/fluent-bit/lib/jemalloc-5.3.0/src/ecache.c deleted file mode 100644 index a242227d..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/ecache.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/san.h" - -bool -ecache_init(tsdn_t *tsdn, ecache_t *ecache, extent_state_t state, unsigned ind, - bool delay_coalesce) { - if (malloc_mutex_init(&ecache->mtx, "extents", WITNESS_RANK_EXTENTS, - malloc_mutex_rank_exclusive)) { - return true; - } - ecache->state = state; - ecache->ind = ind; - ecache->delay_coalesce = delay_coalesce; - eset_init(&ecache->eset, state); - eset_init(&ecache->guarded_eset, state); - - return false; -} - -void -ecache_prefork(tsdn_t *tsdn, ecache_t *ecache) { - malloc_mutex_prefork(tsdn, &ecache->mtx); -} - -void -ecache_postfork_parent(tsdn_t *tsdn, ecache_t *ecache) { - malloc_mutex_postfork_parent(tsdn, &ecache->mtx); -} - -void -ecache_postfork_child(tsdn_t *tsdn, ecache_t *ecache) { - malloc_mutex_postfork_child(tsdn, &ecache->mtx); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/edata.c b/fluent-bit/lib/jemalloc-5.3.0/src/edata.c deleted file mode 100644 index 82b6f565..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/edata.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -ph_gen(, edata_avail, edata_t, avail_link, - edata_esnead_comp) -ph_gen(, edata_heap, edata_t, heap_link, edata_snad_comp) diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/edata_cache.c b/fluent-bit/lib/jemalloc-5.3.0/src/edata_cache.c deleted file mode 100644 index 6bc1848c..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/edata_cache.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -bool -edata_cache_init(edata_cache_t *edata_cache, base_t *base) { - edata_avail_new(&edata_cache->avail); - /* - * This is not strictly necessary, since the edata_cache_t is only - * created inside an arena, which is zeroed on creation. But this is - * handy as a safety measure. - */ - atomic_store_zu(&edata_cache->count, 0, ATOMIC_RELAXED); - if (malloc_mutex_init(&edata_cache->mtx, "edata_cache", - WITNESS_RANK_EDATA_CACHE, malloc_mutex_rank_exclusive)) { - return true; - } - edata_cache->base = base; - return false; -} - -edata_t * -edata_cache_get(tsdn_t *tsdn, edata_cache_t *edata_cache) { - malloc_mutex_lock(tsdn, &edata_cache->mtx); - edata_t *edata = edata_avail_first(&edata_cache->avail); - if (edata == NULL) { - malloc_mutex_unlock(tsdn, &edata_cache->mtx); - return base_alloc_edata(tsdn, edata_cache->base); - } - edata_avail_remove(&edata_cache->avail, edata); - atomic_load_sub_store_zu(&edata_cache->count, 1); - malloc_mutex_unlock(tsdn, &edata_cache->mtx); - return edata; -} - -void -edata_cache_put(tsdn_t *tsdn, edata_cache_t *edata_cache, edata_t *edata) { - malloc_mutex_lock(tsdn, &edata_cache->mtx); - edata_avail_insert(&edata_cache->avail, edata); - atomic_load_add_store_zu(&edata_cache->count, 1); - malloc_mutex_unlock(tsdn, &edata_cache->mtx); -} - -void -edata_cache_prefork(tsdn_t *tsdn, edata_cache_t *edata_cache) { - malloc_mutex_prefork(tsdn, &edata_cache->mtx); -} - -void -edata_cache_postfork_parent(tsdn_t *tsdn, edata_cache_t *edata_cache) { - malloc_mutex_postfork_parent(tsdn, &edata_cache->mtx); -} - -void -edata_cache_postfork_child(tsdn_t *tsdn, edata_cache_t *edata_cache) { - malloc_mutex_postfork_child(tsdn, &edata_cache->mtx); -} - -void -edata_cache_fast_init(edata_cache_fast_t *ecs, edata_cache_t *fallback) { - edata_list_inactive_init(&ecs->list); - ecs->fallback = fallback; - ecs->disabled = false; -} - -static void -edata_cache_fast_try_fill_from_fallback(tsdn_t *tsdn, - edata_cache_fast_t *ecs) { - edata_t *edata; - malloc_mutex_lock(tsdn, &ecs->fallback->mtx); - for (int i = 0; i < EDATA_CACHE_FAST_FILL; i++) { - edata = edata_avail_remove_first(&ecs->fallback->avail); - if (edata == NULL) { - break; - } - edata_list_inactive_append(&ecs->list, edata); - atomic_load_sub_store_zu(&ecs->fallback->count, 1); - } - malloc_mutex_unlock(tsdn, &ecs->fallback->mtx); -} - -edata_t * -edata_cache_fast_get(tsdn_t *tsdn, edata_cache_fast_t *ecs) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_EDATA_CACHE, 0); - - if (ecs->disabled) { - assert(edata_list_inactive_first(&ecs->list) == NULL); - return edata_cache_get(tsdn, ecs->fallback); - } - - edata_t *edata = edata_list_inactive_first(&ecs->list); - if (edata != NULL) { - edata_list_inactive_remove(&ecs->list, edata); - return edata; - } - /* Slow path; requires synchronization. */ - edata_cache_fast_try_fill_from_fallback(tsdn, ecs); - edata = edata_list_inactive_first(&ecs->list); - if (edata != NULL) { - edata_list_inactive_remove(&ecs->list, edata); - } else { - /* - * Slowest path (fallback was also empty); allocate something - * new. - */ - edata = base_alloc_edata(tsdn, ecs->fallback->base); - } - return edata; -} - -static void -edata_cache_fast_flush_all(tsdn_t *tsdn, edata_cache_fast_t *ecs) { - /* - * You could imagine smarter cache management policies (like - * only flushing down to some threshold in anticipation of - * future get requests). But just flushing everything provides - * a good opportunity to defrag too, and lets us share code between the - * flush and disable pathways. - */ - edata_t *edata; - size_t nflushed = 0; - malloc_mutex_lock(tsdn, &ecs->fallback->mtx); - while ((edata = edata_list_inactive_first(&ecs->list)) != NULL) { - edata_list_inactive_remove(&ecs->list, edata); - edata_avail_insert(&ecs->fallback->avail, edata); - nflushed++; - } - atomic_load_add_store_zu(&ecs->fallback->count, nflushed); - malloc_mutex_unlock(tsdn, &ecs->fallback->mtx); -} - -void -edata_cache_fast_put(tsdn_t *tsdn, edata_cache_fast_t *ecs, edata_t *edata) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_EDATA_CACHE, 0); - - if (ecs->disabled) { - assert(edata_list_inactive_first(&ecs->list) == NULL); - edata_cache_put(tsdn, ecs->fallback, edata); - return; - } - - /* - * Prepend rather than append, to do LIFO ordering in the hopes of some - * cache locality. - */ - edata_list_inactive_prepend(&ecs->list, edata); -} - -void -edata_cache_fast_disable(tsdn_t *tsdn, edata_cache_fast_t *ecs) { - edata_cache_fast_flush_all(tsdn, ecs); - ecs->disabled = true; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/ehooks.c b/fluent-bit/lib/jemalloc-5.3.0/src/ehooks.c deleted file mode 100644 index 383e9de6..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/ehooks.c +++ /dev/null @@ -1,275 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/ehooks.h" -#include "jemalloc/internal/extent_mmap.h" - -void -ehooks_init(ehooks_t *ehooks, extent_hooks_t *extent_hooks, unsigned ind) { - /* All other hooks are optional; this one is not. */ - assert(extent_hooks->alloc != NULL); - ehooks->ind = ind; - ehooks_set_extent_hooks_ptr(ehooks, extent_hooks); -} - -/* - * If the caller specifies (!*zero), it is still possible to receive zeroed - * memory, in which case *zero is toggled to true. arena_extent_alloc() takes - * advantage of this to avoid demanding zeroed extents, but taking advantage of - * them if they are returned. - */ -static void * -extent_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size, - size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec) { - void *ret; - - assert(size != 0); - assert(alignment != 0); - - /* "primary" dss. */ - if (have_dss && dss_prec == dss_prec_primary && (ret = - extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero, - commit)) != NULL) { - return ret; - } - /* mmap. */ - if ((ret = extent_alloc_mmap(new_addr, size, alignment, zero, commit)) - != NULL) { - return ret; - } - /* "secondary" dss. */ - if (have_dss && dss_prec == dss_prec_secondary && (ret = - extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero, - commit)) != NULL) { - return ret; - } - - /* All strategies for allocation failed. */ - return NULL; -} - -void * -ehooks_default_alloc_impl(tsdn_t *tsdn, void *new_addr, size_t size, - size_t alignment, bool *zero, bool *commit, unsigned arena_ind) { - arena_t *arena = arena_get(tsdn, arena_ind, false); - /* NULL arena indicates arena_create. */ - assert(arena != NULL || alignment == HUGEPAGE); - dss_prec_t dss = (arena == NULL) ? dss_prec_disabled : - (dss_prec_t)atomic_load_u(&arena->dss_prec, ATOMIC_RELAXED); - void *ret = extent_alloc_core(tsdn, arena, new_addr, size, alignment, - zero, commit, dss); - if (have_madvise_huge && ret) { - pages_set_thp_state(ret, size); - } - return ret; -} - -static void * -ehooks_default_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size, - size_t alignment, bool *zero, bool *commit, unsigned arena_ind) { - return ehooks_default_alloc_impl(tsdn_fetch(), new_addr, size, - ALIGNMENT_CEILING(alignment, PAGE), zero, commit, arena_ind); -} - -bool -ehooks_default_dalloc_impl(void *addr, size_t size) { - if (!have_dss || !extent_in_dss(addr)) { - return extent_dalloc_mmap(addr, size); - } - return true; -} - -static bool -ehooks_default_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size, - bool committed, unsigned arena_ind) { - return ehooks_default_dalloc_impl(addr, size); -} - -void -ehooks_default_destroy_impl(void *addr, size_t size) { - if (!have_dss || !extent_in_dss(addr)) { - pages_unmap(addr, size); - } -} - -static void -ehooks_default_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size, - bool committed, unsigned arena_ind) { - ehooks_default_destroy_impl(addr, size); -} - -bool -ehooks_default_commit_impl(void *addr, size_t offset, size_t length) { - return pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset), - length); -} - -static bool -ehooks_default_commit(extent_hooks_t *extent_hooks, void *addr, size_t size, - size_t offset, size_t length, unsigned arena_ind) { - return ehooks_default_commit_impl(addr, offset, length); -} - -bool -ehooks_default_decommit_impl(void *addr, size_t offset, size_t length) { - return pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset), - length); -} - -static bool -ehooks_default_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size, - size_t offset, size_t length, unsigned arena_ind) { - return ehooks_default_decommit_impl(addr, offset, length); -} - -#ifdef PAGES_CAN_PURGE_LAZY -bool -ehooks_default_purge_lazy_impl(void *addr, size_t offset, size_t length) { - return pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset), - length); -} - -static bool -ehooks_default_purge_lazy(extent_hooks_t *extent_hooks, void *addr, size_t size, - size_t offset, size_t length, unsigned arena_ind) { - assert(addr != NULL); - assert((offset & PAGE_MASK) == 0); - assert(length != 0); - assert((length & PAGE_MASK) == 0); - return ehooks_default_purge_lazy_impl(addr, offset, length); -} -#endif - -#ifdef PAGES_CAN_PURGE_FORCED -bool -ehooks_default_purge_forced_impl(void *addr, size_t offset, size_t length) { - return pages_purge_forced((void *)((uintptr_t)addr + - (uintptr_t)offset), length); -} - -static bool -ehooks_default_purge_forced(extent_hooks_t *extent_hooks, void *addr, - size_t size, size_t offset, size_t length, unsigned arena_ind) { - assert(addr != NULL); - assert((offset & PAGE_MASK) == 0); - assert(length != 0); - assert((length & PAGE_MASK) == 0); - return ehooks_default_purge_forced_impl(addr, offset, length); -} -#endif - -bool -ehooks_default_split_impl() { - if (!maps_coalesce) { - /* - * Without retain, only whole regions can be purged (required by - * MEM_RELEASE on Windows) -- therefore disallow splitting. See - * comments in extent_head_no_merge(). - */ - return !opt_retain; - } - - return false; -} - -static bool -ehooks_default_split(extent_hooks_t *extent_hooks, void *addr, size_t size, - size_t size_a, size_t size_b, bool committed, unsigned arena_ind) { - return ehooks_default_split_impl(); -} - -bool -ehooks_default_merge_impl(tsdn_t *tsdn, void *addr_a, void *addr_b) { - assert(addr_a < addr_b); - /* - * For non-DSS cases -- - * a) W/o maps_coalesce, merge is not always allowed (Windows): - * 1) w/o retain, never merge (first branch below). - * 2) with retain, only merge extents from the same VirtualAlloc - * region (in which case MEM_DECOMMIT is utilized for purging). - * - * b) With maps_coalesce, it's always possible to merge. - * 1) w/o retain, always allow merge (only about dirty / muzzy). - * 2) with retain, to preserve the SN / first-fit, merge is still - * disallowed if b is a head extent, i.e. no merging across - * different mmap regions. - * - * a2) and b2) are implemented in emap_try_acquire_edata_neighbor, and - * sanity checked in the second branch below. - */ - if (!maps_coalesce && !opt_retain) { - return true; - } - if (config_debug) { - edata_t *a = emap_edata_lookup(tsdn, &arena_emap_global, - addr_a); - bool head_a = edata_is_head_get(a); - edata_t *b = emap_edata_lookup(tsdn, &arena_emap_global, - addr_b); - bool head_b = edata_is_head_get(b); - emap_assert_mapped(tsdn, &arena_emap_global, a); - emap_assert_mapped(tsdn, &arena_emap_global, b); - assert(extent_neighbor_head_state_mergeable(head_a, head_b, - /* forward */ true)); - } - if (have_dss && !extent_dss_mergeable(addr_a, addr_b)) { - return true; - } - - return false; -} - -bool -ehooks_default_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a, - void *addr_b, size_t size_b, bool committed, unsigned arena_ind) { - tsdn_t *tsdn = tsdn_fetch(); - - return ehooks_default_merge_impl(tsdn, addr_a, addr_b); -} - -void -ehooks_default_zero_impl(void *addr, size_t size) { - /* - * By default, we try to zero out memory using OS-provided demand-zeroed - * pages. If the user has specifically requested hugepages, though, we - * don't want to purge in the middle of a hugepage (which would break it - * up), so we act conservatively and use memset. - */ - bool needs_memset = true; - if (opt_thp != thp_mode_always) { - needs_memset = pages_purge_forced(addr, size); - } - if (needs_memset) { - memset(addr, 0, size); - } -} - -void -ehooks_default_guard_impl(void *guard1, void *guard2) { - pages_mark_guards(guard1, guard2); -} - -void -ehooks_default_unguard_impl(void *guard1, void *guard2) { - pages_unmark_guards(guard1, guard2); -} - -const extent_hooks_t ehooks_default_extent_hooks = { - ehooks_default_alloc, - ehooks_default_dalloc, - ehooks_default_destroy, - ehooks_default_commit, - ehooks_default_decommit, -#ifdef PAGES_CAN_PURGE_LAZY - ehooks_default_purge_lazy, -#else - NULL, -#endif -#ifdef PAGES_CAN_PURGE_FORCED - ehooks_default_purge_forced, -#else - NULL, -#endif - ehooks_default_split, - ehooks_default_merge -}; diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/emap.c b/fluent-bit/lib/jemalloc-5.3.0/src/emap.c deleted file mode 100644 index 9cc95a72..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/emap.c +++ /dev/null @@ -1,386 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/emap.h" - -enum emap_lock_result_e { - emap_lock_result_success, - emap_lock_result_failure, - emap_lock_result_no_extent -}; -typedef enum emap_lock_result_e emap_lock_result_t; - -bool -emap_init(emap_t *emap, base_t *base, bool zeroed) { - return rtree_new(&emap->rtree, base, zeroed); -} - -void -emap_update_edata_state(tsdn_t *tsdn, emap_t *emap, edata_t *edata, - extent_state_t state) { - witness_assert_positive_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE); - - edata_state_set(edata, state); - - EMAP_DECLARE_RTREE_CTX; - rtree_leaf_elm_t *elm1 = rtree_leaf_elm_lookup(tsdn, &emap->rtree, - rtree_ctx, (uintptr_t)edata_base_get(edata), /* dependent */ true, - /* init_missing */ false); - assert(elm1 != NULL); - rtree_leaf_elm_t *elm2 = edata_size_get(edata) == PAGE ? NULL : - rtree_leaf_elm_lookup(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_last_get(edata), /* dependent */ true, - /* init_missing */ false); - - rtree_leaf_elm_state_update(tsdn, &emap->rtree, elm1, elm2, state); - - emap_assert_mapped(tsdn, emap, edata); -} - -static inline edata_t * -emap_try_acquire_edata_neighbor_impl(tsdn_t *tsdn, emap_t *emap, edata_t *edata, - extent_pai_t pai, extent_state_t expected_state, bool forward, - bool expanding) { - witness_assert_positive_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE); - assert(!edata_guarded_get(edata)); - assert(!expanding || forward); - assert(!edata_state_in_transition(expected_state)); - assert(expected_state == extent_state_dirty || - expected_state == extent_state_muzzy || - expected_state == extent_state_retained); - - void *neighbor_addr = forward ? edata_past_get(edata) : - edata_before_get(edata); - /* - * This is subtle; the rtree code asserts that its input pointer is - * non-NULL, and this is a useful thing to check. But it's possible - * that edata corresponds to an address of (void *)PAGE (in practice, - * this has only been observed on FreeBSD when address-space - * randomization is on, but it could in principle happen anywhere). In - * this case, edata_before_get(edata) is NULL, triggering the assert. - */ - if (neighbor_addr == NULL) { - return NULL; - } - - EMAP_DECLARE_RTREE_CTX; - rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &emap->rtree, - rtree_ctx, (uintptr_t)neighbor_addr, /* dependent*/ false, - /* init_missing */ false); - if (elm == NULL) { - return NULL; - } - - rtree_contents_t neighbor_contents = rtree_leaf_elm_read(tsdn, - &emap->rtree, elm, /* dependent */ true); - if (!extent_can_acquire_neighbor(edata, neighbor_contents, pai, - expected_state, forward, expanding)) { - return NULL; - } - - /* From this point, the neighbor edata can be safely acquired. */ - edata_t *neighbor = neighbor_contents.edata; - assert(edata_state_get(neighbor) == expected_state); - emap_update_edata_state(tsdn, emap, neighbor, extent_state_merging); - if (expanding) { - extent_assert_can_expand(edata, neighbor); - } else { - extent_assert_can_coalesce(edata, neighbor); - } - - return neighbor; -} - -edata_t * -emap_try_acquire_edata_neighbor(tsdn_t *tsdn, emap_t *emap, edata_t *edata, - extent_pai_t pai, extent_state_t expected_state, bool forward) { - return emap_try_acquire_edata_neighbor_impl(tsdn, emap, edata, pai, - expected_state, forward, /* expand */ false); -} - -edata_t * -emap_try_acquire_edata_neighbor_expand(tsdn_t *tsdn, emap_t *emap, - edata_t *edata, extent_pai_t pai, extent_state_t expected_state) { - /* Try expanding forward. */ - return emap_try_acquire_edata_neighbor_impl(tsdn, emap, edata, pai, - expected_state, /* forward */ true, /* expand */ true); -} - -void -emap_release_edata(tsdn_t *tsdn, emap_t *emap, edata_t *edata, - extent_state_t new_state) { - assert(emap_edata_in_transition(tsdn, emap, edata)); - assert(emap_edata_is_acquired(tsdn, emap, edata)); - - emap_update_edata_state(tsdn, emap, edata, new_state); -} - -static bool -emap_rtree_leaf_elms_lookup(tsdn_t *tsdn, emap_t *emap, rtree_ctx_t *rtree_ctx, - const edata_t *edata, bool dependent, bool init_missing, - rtree_leaf_elm_t **r_elm_a, rtree_leaf_elm_t **r_elm_b) { - *r_elm_a = rtree_leaf_elm_lookup(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_base_get(edata), dependent, init_missing); - if (!dependent && *r_elm_a == NULL) { - return true; - } - assert(*r_elm_a != NULL); - - *r_elm_b = rtree_leaf_elm_lookup(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_last_get(edata), dependent, init_missing); - if (!dependent && *r_elm_b == NULL) { - return true; - } - assert(*r_elm_b != NULL); - - return false; -} - -static void -emap_rtree_write_acquired(tsdn_t *tsdn, emap_t *emap, rtree_leaf_elm_t *elm_a, - rtree_leaf_elm_t *elm_b, edata_t *edata, szind_t szind, bool slab) { - rtree_contents_t contents; - contents.edata = edata; - contents.metadata.szind = szind; - contents.metadata.slab = slab; - contents.metadata.is_head = (edata == NULL) ? false : - edata_is_head_get(edata); - contents.metadata.state = (edata == NULL) ? 0 : edata_state_get(edata); - rtree_leaf_elm_write(tsdn, &emap->rtree, elm_a, contents); - if (elm_b != NULL) { - rtree_leaf_elm_write(tsdn, &emap->rtree, elm_b, contents); - } -} - -bool -emap_register_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata, - szind_t szind, bool slab) { - assert(edata_state_get(edata) == extent_state_active); - EMAP_DECLARE_RTREE_CTX; - - rtree_leaf_elm_t *elm_a, *elm_b; - bool err = emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, edata, - false, true, &elm_a, &elm_b); - if (err) { - return true; - } - assert(rtree_leaf_elm_read(tsdn, &emap->rtree, elm_a, - /* dependent */ false).edata == NULL); - assert(rtree_leaf_elm_read(tsdn, &emap->rtree, elm_b, - /* dependent */ false).edata == NULL); - emap_rtree_write_acquired(tsdn, emap, elm_a, elm_b, edata, szind, slab); - return false; -} - -/* Invoked *after* emap_register_boundary. */ -void -emap_register_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata, - szind_t szind) { - EMAP_DECLARE_RTREE_CTX; - - assert(edata_slab_get(edata)); - assert(edata_state_get(edata) == extent_state_active); - - if (config_debug) { - /* Making sure the boundary is registered already. */ - rtree_leaf_elm_t *elm_a, *elm_b; - bool err = emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, - edata, /* dependent */ true, /* init_missing */ false, - &elm_a, &elm_b); - assert(!err); - rtree_contents_t contents_a, contents_b; - contents_a = rtree_leaf_elm_read(tsdn, &emap->rtree, elm_a, - /* dependent */ true); - contents_b = rtree_leaf_elm_read(tsdn, &emap->rtree, elm_b, - /* dependent */ true); - assert(contents_a.edata == edata && contents_b.edata == edata); - assert(contents_a.metadata.slab && contents_b.metadata.slab); - } - - rtree_contents_t contents; - contents.edata = edata; - contents.metadata.szind = szind; - contents.metadata.slab = true; - contents.metadata.state = extent_state_active; - contents.metadata.is_head = false; /* Not allowed to access. */ - - assert(edata_size_get(edata) > (2 << LG_PAGE)); - rtree_write_range(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_base_get(edata) + PAGE, - (uintptr_t)edata_last_get(edata) - PAGE, contents); -} - -void -emap_deregister_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata) { - /* - * The edata must be either in an acquired state, or protected by state - * based locks. - */ - if (!emap_edata_is_acquired(tsdn, emap, edata)) { - witness_assert_positive_depth_to_rank( - tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE); - } - - EMAP_DECLARE_RTREE_CTX; - rtree_leaf_elm_t *elm_a, *elm_b; - - emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, edata, - true, false, &elm_a, &elm_b); - emap_rtree_write_acquired(tsdn, emap, elm_a, elm_b, NULL, SC_NSIZES, - false); -} - -void -emap_deregister_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata) { - EMAP_DECLARE_RTREE_CTX; - - assert(edata_slab_get(edata)); - if (edata_size_get(edata) > (2 << LG_PAGE)) { - rtree_clear_range(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_base_get(edata) + PAGE, - (uintptr_t)edata_last_get(edata) - PAGE); - } -} - -void -emap_remap(tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind, - bool slab) { - EMAP_DECLARE_RTREE_CTX; - - if (szind != SC_NSIZES) { - rtree_contents_t contents; - contents.edata = edata; - contents.metadata.szind = szind; - contents.metadata.slab = slab; - contents.metadata.is_head = edata_is_head_get(edata); - contents.metadata.state = edata_state_get(edata); - - rtree_write(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_addr_get(edata), contents); - /* - * Recall that this is called only for active->inactive and - * inactive->active transitions (since only active extents have - * meaningful values for szind and slab). Active, non-slab - * extents only need to handle lookups at their head (on - * deallocation), so we don't bother filling in the end - * boundary. - * - * For slab extents, we do the end-mapping change. This still - * leaves the interior unmodified; an emap_register_interior - * call is coming in those cases, though. - */ - if (slab && edata_size_get(edata) > PAGE) { - uintptr_t key = (uintptr_t)edata_past_get(edata) - - (uintptr_t)PAGE; - rtree_write(tsdn, &emap->rtree, rtree_ctx, key, - contents); - } - } -} - -bool -emap_split_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare, - edata_t *edata, size_t size_a, edata_t *trail, size_t size_b) { - EMAP_DECLARE_RTREE_CTX; - - /* - * We use incorrect constants for things like arena ind, zero, ranged, - * and commit state, and head status. This is a fake edata_t, used to - * facilitate a lookup. - */ - edata_t lead = {0}; - edata_init(&lead, 0U, edata_addr_get(edata), size_a, false, 0, 0, - extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD); - - emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, &lead, false, true, - &prepare->lead_elm_a, &prepare->lead_elm_b); - emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, trail, false, true, - &prepare->trail_elm_a, &prepare->trail_elm_b); - - if (prepare->lead_elm_a == NULL || prepare->lead_elm_b == NULL - || prepare->trail_elm_a == NULL || prepare->trail_elm_b == NULL) { - return true; - } - return false; -} - -void -emap_split_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare, - edata_t *lead, size_t size_a, edata_t *trail, size_t size_b) { - /* - * We should think about not writing to the lead leaf element. We can - * get into situations where a racing realloc-like call can disagree - * with a size lookup request. I think it's fine to declare that these - * situations are race bugs, but there's an argument to be made that for - * things like xallocx, a size lookup call should return either the old - * size or the new size, but not anything else. - */ - emap_rtree_write_acquired(tsdn, emap, prepare->lead_elm_a, - prepare->lead_elm_b, lead, SC_NSIZES, /* slab */ false); - emap_rtree_write_acquired(tsdn, emap, prepare->trail_elm_a, - prepare->trail_elm_b, trail, SC_NSIZES, /* slab */ false); -} - -void -emap_merge_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare, - edata_t *lead, edata_t *trail) { - EMAP_DECLARE_RTREE_CTX; - emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, lead, true, false, - &prepare->lead_elm_a, &prepare->lead_elm_b); - emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, trail, true, false, - &prepare->trail_elm_a, &prepare->trail_elm_b); -} - -void -emap_merge_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare, - edata_t *lead, edata_t *trail) { - rtree_contents_t clear_contents; - clear_contents.edata = NULL; - clear_contents.metadata.szind = SC_NSIZES; - clear_contents.metadata.slab = false; - clear_contents.metadata.is_head = false; - clear_contents.metadata.state = (extent_state_t)0; - - if (prepare->lead_elm_b != NULL) { - rtree_leaf_elm_write(tsdn, &emap->rtree, - prepare->lead_elm_b, clear_contents); - } - - rtree_leaf_elm_t *merged_b; - if (prepare->trail_elm_b != NULL) { - rtree_leaf_elm_write(tsdn, &emap->rtree, - prepare->trail_elm_a, clear_contents); - merged_b = prepare->trail_elm_b; - } else { - merged_b = prepare->trail_elm_a; - } - - emap_rtree_write_acquired(tsdn, emap, prepare->lead_elm_a, merged_b, - lead, SC_NSIZES, false); -} - -void -emap_do_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) { - EMAP_DECLARE_RTREE_CTX; - - rtree_contents_t contents = rtree_read(tsdn, &emap->rtree, rtree_ctx, - (uintptr_t)edata_base_get(edata)); - assert(contents.edata == edata); - assert(contents.metadata.is_head == edata_is_head_get(edata)); - assert(contents.metadata.state == edata_state_get(edata)); -} - -void -emap_do_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) { - emap_full_alloc_ctx_t context1 = {0}; - emap_full_alloc_ctx_try_lookup(tsdn, emap, edata_base_get(edata), - &context1); - assert(context1.edata == NULL); - - emap_full_alloc_ctx_t context2 = {0}; - emap_full_alloc_ctx_try_lookup(tsdn, emap, edata_last_get(edata), - &context2); - assert(context2.edata == NULL); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/eset.c b/fluent-bit/lib/jemalloc-5.3.0/src/eset.c deleted file mode 100644 index 6f8f335e..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/eset.c +++ /dev/null @@ -1,282 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/eset.h" - -#define ESET_NPSIZES (SC_NPSIZES + 1) - -static void -eset_bin_init(eset_bin_t *bin) { - edata_heap_new(&bin->heap); - /* - * heap_min doesn't need initialization; it gets filled in when the bin - * goes from non-empty to empty. - */ -} - -static void -eset_bin_stats_init(eset_bin_stats_t *bin_stats) { - atomic_store_zu(&bin_stats->nextents, 0, ATOMIC_RELAXED); - atomic_store_zu(&bin_stats->nbytes, 0, ATOMIC_RELAXED); -} - -void -eset_init(eset_t *eset, extent_state_t state) { - for (unsigned i = 0; i < ESET_NPSIZES; i++) { - eset_bin_init(&eset->bins[i]); - eset_bin_stats_init(&eset->bin_stats[i]); - } - fb_init(eset->bitmap, ESET_NPSIZES); - edata_list_inactive_init(&eset->lru); - eset->state = state; -} - -size_t -eset_npages_get(eset_t *eset) { - return atomic_load_zu(&eset->npages, ATOMIC_RELAXED); -} - -size_t -eset_nextents_get(eset_t *eset, pszind_t pind) { - return atomic_load_zu(&eset->bin_stats[pind].nextents, ATOMIC_RELAXED); -} - -size_t -eset_nbytes_get(eset_t *eset, pszind_t pind) { - return atomic_load_zu(&eset->bin_stats[pind].nbytes, ATOMIC_RELAXED); -} - -static void -eset_stats_add(eset_t *eset, pszind_t pind, size_t sz) { - size_t cur = atomic_load_zu(&eset->bin_stats[pind].nextents, - ATOMIC_RELAXED); - atomic_store_zu(&eset->bin_stats[pind].nextents, cur + 1, - ATOMIC_RELAXED); - cur = atomic_load_zu(&eset->bin_stats[pind].nbytes, ATOMIC_RELAXED); - atomic_store_zu(&eset->bin_stats[pind].nbytes, cur + sz, - ATOMIC_RELAXED); -} - -static void -eset_stats_sub(eset_t *eset, pszind_t pind, size_t sz) { - size_t cur = atomic_load_zu(&eset->bin_stats[pind].nextents, - ATOMIC_RELAXED); - atomic_store_zu(&eset->bin_stats[pind].nextents, cur - 1, - ATOMIC_RELAXED); - cur = atomic_load_zu(&eset->bin_stats[pind].nbytes, ATOMIC_RELAXED); - atomic_store_zu(&eset->bin_stats[pind].nbytes, cur - sz, - ATOMIC_RELAXED); -} - -void -eset_insert(eset_t *eset, edata_t *edata) { - assert(edata_state_get(edata) == eset->state); - - size_t size = edata_size_get(edata); - size_t psz = sz_psz_quantize_floor(size); - pszind_t pind = sz_psz2ind(psz); - - edata_cmp_summary_t edata_cmp_summary = edata_cmp_summary_get(edata); - if (edata_heap_empty(&eset->bins[pind].heap)) { - fb_set(eset->bitmap, ESET_NPSIZES, (size_t)pind); - /* Only element is automatically the min element. */ - eset->bins[pind].heap_min = edata_cmp_summary; - } else { - /* - * There's already a min element; update the summary if we're - * about to insert a lower one. - */ - if (edata_cmp_summary_comp(edata_cmp_summary, - eset->bins[pind].heap_min) < 0) { - eset->bins[pind].heap_min = edata_cmp_summary; - } - } - edata_heap_insert(&eset->bins[pind].heap, edata); - - if (config_stats) { - eset_stats_add(eset, pind, size); - } - - edata_list_inactive_append(&eset->lru, edata); - size_t npages = size >> LG_PAGE; - /* - * All modifications to npages hold the mutex (as asserted above), so we - * don't need an atomic fetch-add; we can get by with a load followed by - * a store. - */ - size_t cur_eset_npages = - atomic_load_zu(&eset->npages, ATOMIC_RELAXED); - atomic_store_zu(&eset->npages, cur_eset_npages + npages, - ATOMIC_RELAXED); -} - -void -eset_remove(eset_t *eset, edata_t *edata) { - assert(edata_state_get(edata) == eset->state || - edata_state_in_transition(edata_state_get(edata))); - - size_t size = edata_size_get(edata); - size_t psz = sz_psz_quantize_floor(size); - pszind_t pind = sz_psz2ind(psz); - if (config_stats) { - eset_stats_sub(eset, pind, size); - } - - edata_cmp_summary_t edata_cmp_summary = edata_cmp_summary_get(edata); - edata_heap_remove(&eset->bins[pind].heap, edata); - if (edata_heap_empty(&eset->bins[pind].heap)) { - fb_unset(eset->bitmap, ESET_NPSIZES, (size_t)pind); - } else { - /* - * This is a little weird; we compare if the summaries are - * equal, rather than if the edata we removed was the heap - * minimum. The reason why is that getting the heap minimum - * can cause a pairing heap merge operation. We can avoid this - * if we only update the min if it's changed, in which case the - * summaries of the removed element and the min element should - * compare equal. - */ - if (edata_cmp_summary_comp(edata_cmp_summary, - eset->bins[pind].heap_min) == 0) { - eset->bins[pind].heap_min = edata_cmp_summary_get( - edata_heap_first(&eset->bins[pind].heap)); - } - } - edata_list_inactive_remove(&eset->lru, edata); - size_t npages = size >> LG_PAGE; - /* - * As in eset_insert, we hold eset->mtx and so don't need atomic - * operations for updating eset->npages. - */ - size_t cur_extents_npages = - atomic_load_zu(&eset->npages, ATOMIC_RELAXED); - assert(cur_extents_npages >= npages); - atomic_store_zu(&eset->npages, - cur_extents_npages - (size >> LG_PAGE), ATOMIC_RELAXED); -} - -/* - * Find an extent with size [min_size, max_size) to satisfy the alignment - * requirement. For each size, try only the first extent in the heap. - */ -static edata_t * -eset_fit_alignment(eset_t *eset, size_t min_size, size_t max_size, - size_t alignment) { - pszind_t pind = sz_psz2ind(sz_psz_quantize_ceil(min_size)); - pszind_t pind_max = sz_psz2ind(sz_psz_quantize_ceil(max_size)); - - for (pszind_t i = - (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)pind); - i < pind_max; - i = (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)i + 1)) { - assert(i < SC_NPSIZES); - assert(!edata_heap_empty(&eset->bins[i].heap)); - edata_t *edata = edata_heap_first(&eset->bins[i].heap); - uintptr_t base = (uintptr_t)edata_base_get(edata); - size_t candidate_size = edata_size_get(edata); - assert(candidate_size >= min_size); - - uintptr_t next_align = ALIGNMENT_CEILING((uintptr_t)base, - PAGE_CEILING(alignment)); - if (base > next_align || base + candidate_size <= next_align) { - /* Overflow or not crossing the next alignment. */ - continue; - } - - size_t leadsize = next_align - base; - if (candidate_size - leadsize >= min_size) { - return edata; - } - } - - return NULL; -} - -/* - * Do first-fit extent selection, i.e. select the oldest/lowest extent that is - * large enough. - * - * lg_max_fit is the (log of the) maximum ratio between the requested size and - * the returned size that we'll allow. This can reduce fragmentation by - * avoiding reusing and splitting large extents for smaller sizes. In practice, - * it's set to opt_lg_extent_max_active_fit for the dirty eset and SC_PTR_BITS - * for others. - */ -static edata_t * -eset_first_fit(eset_t *eset, size_t size, bool exact_only, - unsigned lg_max_fit) { - edata_t *ret = NULL; - edata_cmp_summary_t ret_summ JEMALLOC_CC_SILENCE_INIT({0}); - - pszind_t pind = sz_psz2ind(sz_psz_quantize_ceil(size)); - - if (exact_only) { - return edata_heap_empty(&eset->bins[pind].heap) ? NULL : - edata_heap_first(&eset->bins[pind].heap); - } - - for (pszind_t i = - (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)pind); - i < ESET_NPSIZES; - i = (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)i + 1)) { - assert(!edata_heap_empty(&eset->bins[i].heap)); - if (lg_max_fit == SC_PTR_BITS) { - /* - * We'll shift by this below, and shifting out all the - * bits is undefined. Decreasing is safe, since the - * page size is larger than 1 byte. - */ - lg_max_fit = SC_PTR_BITS - 1; - } - if ((sz_pind2sz(i) >> lg_max_fit) > size) { - break; - } - if (ret == NULL || edata_cmp_summary_comp( - eset->bins[i].heap_min, ret_summ) < 0) { - /* - * We grab the edata as early as possible, even though - * we might change it later. Practically, a large - * portion of eset_fit calls succeed at the first valid - * index, so this doesn't cost much, and we get the - * effect of prefetching the edata as early as possible. - */ - edata_t *edata = edata_heap_first(&eset->bins[i].heap); - assert(edata_size_get(edata) >= size); - assert(ret == NULL || edata_snad_comp(edata, ret) < 0); - assert(ret == NULL || edata_cmp_summary_comp( - eset->bins[i].heap_min, - edata_cmp_summary_get(edata)) == 0); - ret = edata; - ret_summ = eset->bins[i].heap_min; - } - if (i == SC_NPSIZES) { - break; - } - assert(i < SC_NPSIZES); - } - - return ret; -} - -edata_t * -eset_fit(eset_t *eset, size_t esize, size_t alignment, bool exact_only, - unsigned lg_max_fit) { - size_t max_size = esize + PAGE_CEILING(alignment) - PAGE; - /* Beware size_t wrap-around. */ - if (max_size < esize) { - return NULL; - } - - edata_t *edata = eset_first_fit(eset, max_size, exact_only, lg_max_fit); - - if (alignment > PAGE && edata == NULL) { - /* - * max_size guarantees the alignment requirement but is rather - * pessimistic. Next we try to satisfy the aligned allocation - * with sizes in [esize, max_size). - */ - edata = eset_fit_alignment(eset, esize, max_size, alignment); - } - - return edata; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/exp_grow.c b/fluent-bit/lib/jemalloc-5.3.0/src/exp_grow.c deleted file mode 100644 index 386471f4..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/exp_grow.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -void -exp_grow_init(exp_grow_t *exp_grow) { - exp_grow->next = sz_psz2ind(HUGEPAGE); - exp_grow->limit = sz_psz2ind(SC_LARGE_MAXCLASS); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/extent.c b/fluent-bit/lib/jemalloc-5.3.0/src/extent.c deleted file mode 100644 index cf3d1f31..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/extent.c +++ /dev/null @@ -1,1326 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/emap.h" -#include "jemalloc/internal/extent_dss.h" -#include "jemalloc/internal/extent_mmap.h" -#include "jemalloc/internal/ph.h" -#include "jemalloc/internal/mutex.h" - -/******************************************************************************/ -/* Data. */ - -size_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT; - -static bool extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length, bool growing_retained); -static bool extent_purge_lazy_impl(tsdn_t *tsdn, ehooks_t *ehooks, - edata_t *edata, size_t offset, size_t length, bool growing_retained); -static bool extent_purge_forced_impl(tsdn_t *tsdn, ehooks_t *ehooks, - edata_t *edata, size_t offset, size_t length, bool growing_retained); -static edata_t *extent_split_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks); -static bool extent_merge_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *a, edata_t *b, bool holding_core_locks); - -/* Used exclusively for gdump triggering. */ -static atomic_zu_t curpages; -static atomic_zu_t highpages; - -/******************************************************************************/ -/* - * Function prototypes for static functions that are referenced prior to - * definition. - */ - -static void extent_deregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata); -static edata_t *extent_recycle(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *expand_edata, size_t usize, size_t alignment, - bool zero, bool *commit, bool growing_retained, bool guarded); -static edata_t *extent_try_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *edata, bool *coalesced); -static edata_t *extent_alloc_retained(tsdn_t *tsdn, pac_t *pac, - ehooks_t *ehooks, edata_t *expand_edata, size_t size, size_t alignment, - bool zero, bool *commit, bool guarded); - -/******************************************************************************/ - -size_t -extent_sn_next(pac_t *pac) { - return atomic_fetch_add_zu(&pac->extent_sn_next, 1, ATOMIC_RELAXED); -} - -static inline bool -extent_may_force_decay(pac_t *pac) { - return !(pac_decay_ms_get(pac, extent_state_dirty) == -1 - || pac_decay_ms_get(pac, extent_state_muzzy) == -1); -} - -static bool -extent_try_delayed_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *edata) { - emap_update_edata_state(tsdn, pac->emap, edata, extent_state_active); - - bool coalesced; - edata = extent_try_coalesce(tsdn, pac, ehooks, ecache, - edata, &coalesced); - emap_update_edata_state(tsdn, pac->emap, edata, ecache->state); - - if (!coalesced) { - return true; - } - eset_insert(&ecache->eset, edata); - return false; -} - -edata_t * -ecache_alloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *expand_edata, size_t size, size_t alignment, bool zero, - bool guarded) { - assert(size != 0); - assert(alignment != 0); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - bool commit = true; - edata_t *edata = extent_recycle(tsdn, pac, ehooks, ecache, expand_edata, - size, alignment, zero, &commit, false, guarded); - assert(edata == NULL || edata_pai_get(edata) == EXTENT_PAI_PAC); - assert(edata == NULL || edata_guarded_get(edata) == guarded); - return edata; -} - -edata_t * -ecache_alloc_grow(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *expand_edata, size_t size, size_t alignment, bool zero, - bool guarded) { - assert(size != 0); - assert(alignment != 0); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - bool commit = true; - edata_t *edata = extent_alloc_retained(tsdn, pac, ehooks, expand_edata, - size, alignment, zero, &commit, guarded); - if (edata == NULL) { - if (opt_retain && expand_edata != NULL) { - /* - * When retain is enabled and trying to expand, we do - * not attempt extent_alloc_wrapper which does mmap that - * is very unlikely to succeed (unless it happens to be - * at the end). - */ - return NULL; - } - if (guarded) { - /* - * Means no cached guarded extents available (and no - * grow_retained was attempted). The pac_alloc flow - * will alloc regular extents to make new guarded ones. - */ - return NULL; - } - void *new_addr = (expand_edata == NULL) ? NULL : - edata_past_get(expand_edata); - edata = extent_alloc_wrapper(tsdn, pac, ehooks, new_addr, - size, alignment, zero, &commit, - /* growing_retained */ false); - } - - assert(edata == NULL || edata_pai_get(edata) == EXTENT_PAI_PAC); - return edata; -} - -void -ecache_dalloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *edata) { - assert(edata_base_get(edata) != NULL); - assert(edata_size_get(edata) != 0); - assert(edata_pai_get(edata) == EXTENT_PAI_PAC); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - edata_addr_set(edata, edata_base_get(edata)); - edata_zeroed_set(edata, false); - - extent_record(tsdn, pac, ehooks, ecache, edata); -} - -edata_t * -ecache_evict(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, size_t npages_min) { - malloc_mutex_lock(tsdn, &ecache->mtx); - - /* - * Get the LRU coalesced extent, if any. If coalescing was delayed, - * the loop will iterate until the LRU extent is fully coalesced. - */ - edata_t *edata; - while (true) { - /* Get the LRU extent, if any. */ - eset_t *eset = &ecache->eset; - edata = edata_list_inactive_first(&eset->lru); - if (edata == NULL) { - /* - * Next check if there are guarded extents. They are - * more expensive to purge (since they are not - * mergeable), thus in favor of caching them longer. - */ - eset = &ecache->guarded_eset; - edata = edata_list_inactive_first(&eset->lru); - if (edata == NULL) { - goto label_return; - } - } - /* Check the eviction limit. */ - size_t extents_npages = ecache_npages_get(ecache); - if (extents_npages <= npages_min) { - edata = NULL; - goto label_return; - } - eset_remove(eset, edata); - if (!ecache->delay_coalesce || edata_guarded_get(edata)) { - break; - } - /* Try to coalesce. */ - if (extent_try_delayed_coalesce(tsdn, pac, ehooks, ecache, - edata)) { - break; - } - /* - * The LRU extent was just coalesced and the result placed in - * the LRU at its neighbor's position. Start over. - */ - } - - /* - * Either mark the extent active or deregister it to protect against - * concurrent operations. - */ - switch (ecache->state) { - case extent_state_active: - not_reached(); - case extent_state_dirty: - case extent_state_muzzy: - emap_update_edata_state(tsdn, pac->emap, edata, - extent_state_active); - break; - case extent_state_retained: - extent_deregister(tsdn, pac, edata); - break; - default: - not_reached(); - } - -label_return: - malloc_mutex_unlock(tsdn, &ecache->mtx); - return edata; -} - -/* - * This can only happen when we fail to allocate a new extent struct (which - * indicates OOM), e.g. when trying to split an existing extent. - */ -static void -extents_abandon_vm(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *edata, bool growing_retained) { - size_t sz = edata_size_get(edata); - if (config_stats) { - atomic_fetch_add_zu(&pac->stats->abandoned_vm, sz, - ATOMIC_RELAXED); - } - /* - * Leak extent after making sure its pages have already been purged, so - * that this is only a virtual memory leak. - */ - if (ecache->state == extent_state_dirty) { - if (extent_purge_lazy_impl(tsdn, ehooks, edata, 0, sz, - growing_retained)) { - extent_purge_forced_impl(tsdn, ehooks, edata, 0, - edata_size_get(edata), growing_retained); - } - } - edata_cache_put(tsdn, pac->edata_cache, edata); -} - -static void -extent_deactivate_locked_impl(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, - edata_t *edata) { - malloc_mutex_assert_owner(tsdn, &ecache->mtx); - assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache)); - - emap_update_edata_state(tsdn, pac->emap, edata, ecache->state); - eset_t *eset = edata_guarded_get(edata) ? &ecache->guarded_eset : - &ecache->eset; - eset_insert(eset, edata); -} - -static void -extent_deactivate_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, - edata_t *edata) { - assert(edata_state_get(edata) == extent_state_active); - extent_deactivate_locked_impl(tsdn, pac, ecache, edata); -} - -static void -extent_deactivate_check_state_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, - edata_t *edata, extent_state_t expected_state) { - assert(edata_state_get(edata) == expected_state); - extent_deactivate_locked_impl(tsdn, pac, ecache, edata); -} - -static void -extent_activate_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, eset_t *eset, - edata_t *edata) { - assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache)); - assert(edata_state_get(edata) == ecache->state || - edata_state_get(edata) == extent_state_merging); - - eset_remove(eset, edata); - emap_update_edata_state(tsdn, pac->emap, edata, extent_state_active); -} - -void -extent_gdump_add(tsdn_t *tsdn, const edata_t *edata) { - cassert(config_prof); - /* prof_gdump() requirement. */ - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - if (opt_prof && edata_state_get(edata) == extent_state_active) { - size_t nadd = edata_size_get(edata) >> LG_PAGE; - size_t cur = atomic_fetch_add_zu(&curpages, nadd, - ATOMIC_RELAXED) + nadd; - size_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED); - while (cur > high && !atomic_compare_exchange_weak_zu( - &highpages, &high, cur, ATOMIC_RELAXED, ATOMIC_RELAXED)) { - /* - * Don't refresh cur, because it may have decreased - * since this thread lost the highpages update race. - * Note that high is updated in case of CAS failure. - */ - } - if (cur > high && prof_gdump_get_unlocked()) { - prof_gdump(tsdn); - } - } -} - -static void -extent_gdump_sub(tsdn_t *tsdn, const edata_t *edata) { - cassert(config_prof); - - if (opt_prof && edata_state_get(edata) == extent_state_active) { - size_t nsub = edata_size_get(edata) >> LG_PAGE; - assert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub); - atomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED); - } -} - -static bool -extent_register_impl(tsdn_t *tsdn, pac_t *pac, edata_t *edata, bool gdump_add) { - assert(edata_state_get(edata) == extent_state_active); - /* - * No locking needed, as the edata must be in active state, which - * prevents other threads from accessing the edata. - */ - if (emap_register_boundary(tsdn, pac->emap, edata, SC_NSIZES, - /* slab */ false)) { - return true; - } - - if (config_prof && gdump_add) { - extent_gdump_add(tsdn, edata); - } - - return false; -} - -static bool -extent_register(tsdn_t *tsdn, pac_t *pac, edata_t *edata) { - return extent_register_impl(tsdn, pac, edata, true); -} - -static bool -extent_register_no_gdump_add(tsdn_t *tsdn, pac_t *pac, edata_t *edata) { - return extent_register_impl(tsdn, pac, edata, false); -} - -static void -extent_reregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata) { - bool err = extent_register(tsdn, pac, edata); - assert(!err); -} - -/* - * Removes all pointers to the given extent from the global rtree. - */ -static void -extent_deregister_impl(tsdn_t *tsdn, pac_t *pac, edata_t *edata, - bool gdump) { - emap_deregister_boundary(tsdn, pac->emap, edata); - - if (config_prof && gdump) { - extent_gdump_sub(tsdn, edata); - } -} - -static void -extent_deregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata) { - extent_deregister_impl(tsdn, pac, edata, true); -} - -static void -extent_deregister_no_gdump_sub(tsdn_t *tsdn, pac_t *pac, - edata_t *edata) { - extent_deregister_impl(tsdn, pac, edata, false); -} - -/* - * Tries to find and remove an extent from ecache that can be used for the - * given allocation request. - */ -static edata_t * -extent_recycle_extract(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment, - bool guarded) { - malloc_mutex_assert_owner(tsdn, &ecache->mtx); - assert(alignment > 0); - if (config_debug && expand_edata != NULL) { - /* - * Non-NULL expand_edata indicates in-place expanding realloc. - * new_addr must either refer to a non-existing extent, or to - * the base of an extant extent, since only active slabs support - * interior lookups (which of course cannot be recycled). - */ - void *new_addr = edata_past_get(expand_edata); - assert(PAGE_ADDR2BASE(new_addr) == new_addr); - assert(alignment <= PAGE); - } - - edata_t *edata; - eset_t *eset = guarded ? &ecache->guarded_eset : &ecache->eset; - if (expand_edata != NULL) { - edata = emap_try_acquire_edata_neighbor_expand(tsdn, pac->emap, - expand_edata, EXTENT_PAI_PAC, ecache->state); - if (edata != NULL) { - extent_assert_can_expand(expand_edata, edata); - if (edata_size_get(edata) < size) { - emap_release_edata(tsdn, pac->emap, edata, - ecache->state); - edata = NULL; - } - } - } else { - /* - * A large extent might be broken up from its original size to - * some small size to satisfy a small request. When that small - * request is freed, though, it won't merge back with the larger - * extent if delayed coalescing is on. The large extent can - * then no longer satify a request for its original size. To - * limit this effect, when delayed coalescing is enabled, we - * put a cap on how big an extent we can split for a request. - */ - unsigned lg_max_fit = ecache->delay_coalesce - ? (unsigned)opt_lg_extent_max_active_fit : SC_PTR_BITS; - - /* - * If split and merge are not allowed (Windows w/o retain), try - * exact fit only. - * - * For simplicity purposes, splitting guarded extents is not - * supported. Hence, we do only exact fit for guarded - * allocations. - */ - bool exact_only = (!maps_coalesce && !opt_retain) || guarded; - edata = eset_fit(eset, size, alignment, exact_only, - lg_max_fit); - } - if (edata == NULL) { - return NULL; - } - assert(!guarded || edata_guarded_get(edata)); - extent_activate_locked(tsdn, pac, ecache, eset, edata); - - return edata; -} - -/* - * Given an allocation request and an extent guaranteed to be able to satisfy - * it, this splits off lead and trail extents, leaving edata pointing to an - * extent satisfying the allocation. - * This function doesn't put lead or trail into any ecache; it's the caller's - * job to ensure that they can be reused. - */ -typedef enum { - /* - * Split successfully. lead, edata, and trail, are modified to extents - * describing the ranges before, in, and after the given allocation. - */ - extent_split_interior_ok, - /* - * The extent can't satisfy the given allocation request. None of the - * input edata_t *s are touched. - */ - extent_split_interior_cant_alloc, - /* - * In a potentially invalid state. Must leak (if *to_leak is non-NULL), - * and salvage what's still salvageable (if *to_salvage is non-NULL). - * None of lead, edata, or trail are valid. - */ - extent_split_interior_error -} extent_split_interior_result_t; - -static extent_split_interior_result_t -extent_split_interior(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - /* The result of splitting, in case of success. */ - edata_t **edata, edata_t **lead, edata_t **trail, - /* The mess to clean up, in case of error. */ - edata_t **to_leak, edata_t **to_salvage, - edata_t *expand_edata, size_t size, size_t alignment) { - size_t leadsize = ALIGNMENT_CEILING((uintptr_t)edata_base_get(*edata), - PAGE_CEILING(alignment)) - (uintptr_t)edata_base_get(*edata); - assert(expand_edata == NULL || leadsize == 0); - if (edata_size_get(*edata) < leadsize + size) { - return extent_split_interior_cant_alloc; - } - size_t trailsize = edata_size_get(*edata) - leadsize - size; - - *lead = NULL; - *trail = NULL; - *to_leak = NULL; - *to_salvage = NULL; - - /* Split the lead. */ - if (leadsize != 0) { - assert(!edata_guarded_get(*edata)); - *lead = *edata; - *edata = extent_split_impl(tsdn, pac, ehooks, *lead, leadsize, - size + trailsize, /* holding_core_locks*/ true); - if (*edata == NULL) { - *to_leak = *lead; - *lead = NULL; - return extent_split_interior_error; - } - } - - /* Split the trail. */ - if (trailsize != 0) { - assert(!edata_guarded_get(*edata)); - *trail = extent_split_impl(tsdn, pac, ehooks, *edata, size, - trailsize, /* holding_core_locks */ true); - if (*trail == NULL) { - *to_leak = *edata; - *to_salvage = *lead; - *lead = NULL; - *edata = NULL; - return extent_split_interior_error; - } - } - - return extent_split_interior_ok; -} - -/* - * This fulfills the indicated allocation request out of the given extent (which - * the caller should have ensured was big enough). If there's any unused space - * before or after the resulting allocation, that space is given its own extent - * and put back into ecache. - */ -static edata_t * -extent_recycle_split(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment, - edata_t *edata, bool growing_retained) { - assert(!edata_guarded_get(edata) || size == edata_size_get(edata)); - malloc_mutex_assert_owner(tsdn, &ecache->mtx); - - edata_t *lead; - edata_t *trail; - edata_t *to_leak JEMALLOC_CC_SILENCE_INIT(NULL); - edata_t *to_salvage JEMALLOC_CC_SILENCE_INIT(NULL); - - extent_split_interior_result_t result = extent_split_interior( - tsdn, pac, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage, - expand_edata, size, alignment); - - if (!maps_coalesce && result != extent_split_interior_ok - && !opt_retain) { - /* - * Split isn't supported (implies Windows w/o retain). Avoid - * leaking the extent. - */ - assert(to_leak != NULL && lead == NULL && trail == NULL); - extent_deactivate_locked(tsdn, pac, ecache, to_leak); - return NULL; - } - - if (result == extent_split_interior_ok) { - if (lead != NULL) { - extent_deactivate_locked(tsdn, pac, ecache, lead); - } - if (trail != NULL) { - extent_deactivate_locked(tsdn, pac, ecache, trail); - } - return edata; - } else { - /* - * We should have picked an extent that was large enough to - * fulfill our allocation request. - */ - assert(result == extent_split_interior_error); - if (to_salvage != NULL) { - extent_deregister(tsdn, pac, to_salvage); - } - if (to_leak != NULL) { - extent_deregister_no_gdump_sub(tsdn, pac, to_leak); - /* - * May go down the purge path (which assume no ecache - * locks). Only happens with OOM caused split failures. - */ - malloc_mutex_unlock(tsdn, &ecache->mtx); - extents_abandon_vm(tsdn, pac, ehooks, ecache, to_leak, - growing_retained); - malloc_mutex_lock(tsdn, &ecache->mtx); - } - return NULL; - } - unreachable(); -} - -/* - * Tries to satisfy the given allocation request by reusing one of the extents - * in the given ecache_t. - */ -static edata_t * -extent_recycle(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *expand_edata, size_t size, size_t alignment, bool zero, - bool *commit, bool growing_retained, bool guarded) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, growing_retained ? 1 : 0); - assert(!guarded || expand_edata == NULL); - assert(!guarded || alignment <= PAGE); - - malloc_mutex_lock(tsdn, &ecache->mtx); - - edata_t *edata = extent_recycle_extract(tsdn, pac, ehooks, ecache, - expand_edata, size, alignment, guarded); - if (edata == NULL) { - malloc_mutex_unlock(tsdn, &ecache->mtx); - return NULL; - } - - edata = extent_recycle_split(tsdn, pac, ehooks, ecache, expand_edata, - size, alignment, edata, growing_retained); - malloc_mutex_unlock(tsdn, &ecache->mtx); - if (edata == NULL) { - return NULL; - } - - assert(edata_state_get(edata) == extent_state_active); - if (extent_commit_zero(tsdn, ehooks, edata, *commit, zero, - growing_retained)) { - extent_record(tsdn, pac, ehooks, ecache, edata); - return NULL; - } - if (edata_committed_get(edata)) { - /* - * This reverses the purpose of this variable - previously it - * was treated as an input parameter, now it turns into an - * output parameter, reporting if the edata has actually been - * committed. - */ - *commit = true; - } - return edata; -} - -/* - * If virtual memory is retained, create increasingly larger extents from which - * to split requested extents in order to limit the total number of disjoint - * virtual memory ranges retained by each shard. - */ -static edata_t * -extent_grow_retained(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - size_t size, size_t alignment, bool zero, bool *commit) { - malloc_mutex_assert_owner(tsdn, &pac->grow_mtx); - - size_t alloc_size_min = size + PAGE_CEILING(alignment) - PAGE; - /* Beware size_t wrap-around. */ - if (alloc_size_min < size) { - goto label_err; - } - /* - * Find the next extent size in the series that would be large enough to - * satisfy this request. - */ - size_t alloc_size; - pszind_t exp_grow_skip; - bool err = exp_grow_size_prepare(&pac->exp_grow, alloc_size_min, - &alloc_size, &exp_grow_skip); - if (err) { - goto label_err; - } - - edata_t *edata = edata_cache_get(tsdn, pac->edata_cache); - if (edata == NULL) { - goto label_err; - } - bool zeroed = false; - bool committed = false; - - void *ptr = ehooks_alloc(tsdn, ehooks, NULL, alloc_size, PAGE, &zeroed, - &committed); - - if (ptr == NULL) { - edata_cache_put(tsdn, pac->edata_cache, edata); - goto label_err; - } - - edata_init(edata, ecache_ind_get(&pac->ecache_retained), ptr, - alloc_size, false, SC_NSIZES, extent_sn_next(pac), - extent_state_active, zeroed, committed, EXTENT_PAI_PAC, - EXTENT_IS_HEAD); - - if (extent_register_no_gdump_add(tsdn, pac, edata)) { - edata_cache_put(tsdn, pac->edata_cache, edata); - goto label_err; - } - - if (edata_committed_get(edata)) { - *commit = true; - } - - edata_t *lead; - edata_t *trail; - edata_t *to_leak JEMALLOC_CC_SILENCE_INIT(NULL); - edata_t *to_salvage JEMALLOC_CC_SILENCE_INIT(NULL); - - extent_split_interior_result_t result = extent_split_interior(tsdn, - pac, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage, NULL, - size, alignment); - - if (result == extent_split_interior_ok) { - if (lead != NULL) { - extent_record(tsdn, pac, ehooks, &pac->ecache_retained, - lead); - } - if (trail != NULL) { - extent_record(tsdn, pac, ehooks, &pac->ecache_retained, - trail); - } - } else { - /* - * We should have allocated a sufficiently large extent; the - * cant_alloc case should not occur. - */ - assert(result == extent_split_interior_error); - if (to_salvage != NULL) { - if (config_prof) { - extent_gdump_add(tsdn, to_salvage); - } - extent_record(tsdn, pac, ehooks, &pac->ecache_retained, - to_salvage); - } - if (to_leak != NULL) { - extent_deregister_no_gdump_sub(tsdn, pac, to_leak); - extents_abandon_vm(tsdn, pac, ehooks, - &pac->ecache_retained, to_leak, true); - } - goto label_err; - } - - if (*commit && !edata_committed_get(edata)) { - if (extent_commit_impl(tsdn, ehooks, edata, 0, - edata_size_get(edata), true)) { - extent_record(tsdn, pac, ehooks, - &pac->ecache_retained, edata); - goto label_err; - } - /* A successful commit should return zeroed memory. */ - if (config_debug) { - void *addr = edata_addr_get(edata); - size_t *p = (size_t *)(uintptr_t)addr; - /* Check the first page only. */ - for (size_t i = 0; i < PAGE / sizeof(size_t); i++) { - assert(p[i] == 0); - } - } - } - - /* - * Increment extent_grow_next if doing so wouldn't exceed the allowed - * range. - */ - /* All opportunities for failure are past. */ - exp_grow_size_commit(&pac->exp_grow, exp_grow_skip); - malloc_mutex_unlock(tsdn, &pac->grow_mtx); - - if (config_prof) { - /* Adjust gdump stats now that extent is final size. */ - extent_gdump_add(tsdn, edata); - } - if (zero && !edata_zeroed_get(edata)) { - ehooks_zero(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata)); - } - return edata; -label_err: - malloc_mutex_unlock(tsdn, &pac->grow_mtx); - return NULL; -} - -static edata_t * -extent_alloc_retained(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *expand_edata, size_t size, size_t alignment, bool zero, - bool *commit, bool guarded) { - assert(size != 0); - assert(alignment != 0); - - malloc_mutex_lock(tsdn, &pac->grow_mtx); - - edata_t *edata = extent_recycle(tsdn, pac, ehooks, - &pac->ecache_retained, expand_edata, size, alignment, zero, commit, - /* growing_retained */ true, guarded); - if (edata != NULL) { - malloc_mutex_unlock(tsdn, &pac->grow_mtx); - if (config_prof) { - extent_gdump_add(tsdn, edata); - } - } else if (opt_retain && expand_edata == NULL && !guarded) { - edata = extent_grow_retained(tsdn, pac, ehooks, size, - alignment, zero, commit); - /* extent_grow_retained() always releases pac->grow_mtx. */ - } else { - malloc_mutex_unlock(tsdn, &pac->grow_mtx); - } - malloc_mutex_assert_not_owner(tsdn, &pac->grow_mtx); - - return edata; -} - -static bool -extent_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *inner, edata_t *outer, bool forward) { - extent_assert_can_coalesce(inner, outer); - eset_remove(&ecache->eset, outer); - - bool err = extent_merge_impl(tsdn, pac, ehooks, - forward ? inner : outer, forward ? outer : inner, - /* holding_core_locks */ true); - if (err) { - extent_deactivate_check_state_locked(tsdn, pac, ecache, outer, - extent_state_merging); - } - - return err; -} - -static edata_t * -extent_try_coalesce_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *edata, bool *coalesced) { - assert(!edata_guarded_get(edata)); - /* - * We avoid checking / locking inactive neighbors for large size - * classes, since they are eagerly coalesced on deallocation which can - * cause lock contention. - */ - /* - * Continue attempting to coalesce until failure, to protect against - * races with other threads that are thwarted by this one. - */ - bool again; - do { - again = false; - - /* Try to coalesce forward. */ - edata_t *next = emap_try_acquire_edata_neighbor(tsdn, pac->emap, - edata, EXTENT_PAI_PAC, ecache->state, /* forward */ true); - if (next != NULL) { - if (!extent_coalesce(tsdn, pac, ehooks, ecache, edata, - next, true)) { - if (ecache->delay_coalesce) { - /* Do minimal coalescing. */ - *coalesced = true; - return edata; - } - again = true; - } - } - - /* Try to coalesce backward. */ - edata_t *prev = emap_try_acquire_edata_neighbor(tsdn, pac->emap, - edata, EXTENT_PAI_PAC, ecache->state, /* forward */ false); - if (prev != NULL) { - if (!extent_coalesce(tsdn, pac, ehooks, ecache, edata, - prev, false)) { - edata = prev; - if (ecache->delay_coalesce) { - /* Do minimal coalescing. */ - *coalesced = true; - return edata; - } - again = true; - } - } - } while (again); - - if (ecache->delay_coalesce) { - *coalesced = false; - } - return edata; -} - -static edata_t * -extent_try_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *edata, bool *coalesced) { - return extent_try_coalesce_impl(tsdn, pac, ehooks, ecache, edata, - coalesced); -} - -static edata_t * -extent_try_coalesce_large(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - ecache_t *ecache, edata_t *edata, bool *coalesced) { - return extent_try_coalesce_impl(tsdn, pac, ehooks, ecache, edata, - coalesced); -} - -/* Purge a single extent to retained / unmapped directly. */ -static void -extent_maximally_purge(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata) { - size_t extent_size = edata_size_get(edata); - extent_dalloc_wrapper(tsdn, pac, ehooks, edata); - if (config_stats) { - /* Update stats accordingly. */ - LOCKEDINT_MTX_LOCK(tsdn, *pac->stats_mtx); - locked_inc_u64(tsdn, - LOCKEDINT_MTX(*pac->stats_mtx), - &pac->stats->decay_dirty.nmadvise, 1); - locked_inc_u64(tsdn, - LOCKEDINT_MTX(*pac->stats_mtx), - &pac->stats->decay_dirty.purged, - extent_size >> LG_PAGE); - LOCKEDINT_MTX_UNLOCK(tsdn, *pac->stats_mtx); - atomic_fetch_sub_zu(&pac->stats->pac_mapped, extent_size, - ATOMIC_RELAXED); - } -} - -/* - * Does the metadata management portions of putting an unused extent into the - * given ecache_t (coalesces and inserts into the eset). - */ -void -extent_record(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache, - edata_t *edata) { - assert((ecache->state != extent_state_dirty && - ecache->state != extent_state_muzzy) || - !edata_zeroed_get(edata)); - - malloc_mutex_lock(tsdn, &ecache->mtx); - - emap_assert_mapped(tsdn, pac->emap, edata); - - if (edata_guarded_get(edata)) { - goto label_skip_coalesce; - } - if (!ecache->delay_coalesce) { - edata = extent_try_coalesce(tsdn, pac, ehooks, ecache, edata, - NULL); - } else if (edata_size_get(edata) >= SC_LARGE_MINCLASS) { - assert(ecache == &pac->ecache_dirty); - /* Always coalesce large extents eagerly. */ - bool coalesced; - do { - assert(edata_state_get(edata) == extent_state_active); - edata = extent_try_coalesce_large(tsdn, pac, ehooks, - ecache, edata, &coalesced); - } while (coalesced); - if (edata_size_get(edata) >= - atomic_load_zu(&pac->oversize_threshold, ATOMIC_RELAXED) - && extent_may_force_decay(pac)) { - /* Shortcut to purge the oversize extent eagerly. */ - malloc_mutex_unlock(tsdn, &ecache->mtx); - extent_maximally_purge(tsdn, pac, ehooks, edata); - return; - } - } -label_skip_coalesce: - extent_deactivate_locked(tsdn, pac, ecache, edata); - - malloc_mutex_unlock(tsdn, &ecache->mtx); -} - -void -extent_dalloc_gap(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - if (extent_register(tsdn, pac, edata)) { - edata_cache_put(tsdn, pac->edata_cache, edata); - return; - } - extent_dalloc_wrapper(tsdn, pac, ehooks, edata); -} - -static bool -extent_dalloc_wrapper_try(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata) { - bool err; - - assert(edata_base_get(edata) != NULL); - assert(edata_size_get(edata) != 0); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - edata_addr_set(edata, edata_base_get(edata)); - - /* Try to deallocate. */ - err = ehooks_dalloc(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), edata_committed_get(edata)); - - if (!err) { - edata_cache_put(tsdn, pac->edata_cache, edata); - } - - return err; -} - -edata_t * -extent_alloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - void *new_addr, size_t size, size_t alignment, bool zero, bool *commit, - bool growing_retained) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, growing_retained ? 1 : 0); - - edata_t *edata = edata_cache_get(tsdn, pac->edata_cache); - if (edata == NULL) { - return NULL; - } - size_t palignment = ALIGNMENT_CEILING(alignment, PAGE); - void *addr = ehooks_alloc(tsdn, ehooks, new_addr, size, palignment, - &zero, commit); - if (addr == NULL) { - edata_cache_put(tsdn, pac->edata_cache, edata); - return NULL; - } - edata_init(edata, ecache_ind_get(&pac->ecache_dirty), addr, - size, /* slab */ false, SC_NSIZES, extent_sn_next(pac), - extent_state_active, zero, *commit, EXTENT_PAI_PAC, - opt_retain ? EXTENT_IS_HEAD : EXTENT_NOT_HEAD); - /* - * Retained memory is not counted towards gdump. Only if an extent is - * allocated as a separate mapping, i.e. growing_retained is false, then - * gdump should be updated. - */ - bool gdump_add = !growing_retained; - if (extent_register_impl(tsdn, pac, edata, gdump_add)) { - edata_cache_put(tsdn, pac->edata_cache, edata); - return NULL; - } - - return edata; -} - -void -extent_dalloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata) { - assert(edata_pai_get(edata) == EXTENT_PAI_PAC); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - /* Avoid calling the default extent_dalloc unless have to. */ - if (!ehooks_dalloc_will_fail(ehooks)) { - /* Remove guard pages for dalloc / unmap. */ - if (edata_guarded_get(edata)) { - assert(ehooks_are_default(ehooks)); - san_unguard_pages_two_sided(tsdn, ehooks, edata, - pac->emap); - } - /* - * Deregister first to avoid a race with other allocating - * threads, and reregister if deallocation fails. - */ - extent_deregister(tsdn, pac, edata); - if (!extent_dalloc_wrapper_try(tsdn, pac, ehooks, edata)) { - return; - } - extent_reregister(tsdn, pac, edata); - } - - /* Try to decommit; purge if that fails. */ - bool zeroed; - if (!edata_committed_get(edata)) { - zeroed = true; - } else if (!extent_decommit_wrapper(tsdn, ehooks, edata, 0, - edata_size_get(edata))) { - zeroed = true; - } else if (!ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), 0, edata_size_get(edata))) { - zeroed = true; - } else if (edata_state_get(edata) == extent_state_muzzy || - !ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), 0, edata_size_get(edata))) { - zeroed = false; - } else { - zeroed = false; - } - edata_zeroed_set(edata, zeroed); - - if (config_prof) { - extent_gdump_sub(tsdn, edata); - } - - extent_record(tsdn, pac, ehooks, &pac->ecache_retained, edata); -} - -void -extent_destroy_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata) { - assert(edata_base_get(edata) != NULL); - assert(edata_size_get(edata) != 0); - extent_state_t state = edata_state_get(edata); - assert(state == extent_state_retained || state == extent_state_active); - assert(emap_edata_is_acquired(tsdn, pac->emap, edata)); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - if (edata_guarded_get(edata)) { - assert(opt_retain); - san_unguard_pages_pre_destroy(tsdn, ehooks, edata, pac->emap); - } - edata_addr_set(edata, edata_base_get(edata)); - - /* Try to destroy; silently fail otherwise. */ - ehooks_destroy(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), edata_committed_get(edata)); - - edata_cache_put(tsdn, pac->edata_cache, edata); -} - -static bool -extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length, bool growing_retained) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, growing_retained ? 1 : 0); - bool err = ehooks_commit(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), offset, length); - edata_committed_set(edata, edata_committed_get(edata) || !err); - return err; -} - -bool -extent_commit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length) { - return extent_commit_impl(tsdn, ehooks, edata, offset, length, - /* growing_retained */ false); -} - -bool -extent_decommit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - bool err = ehooks_decommit(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), offset, length); - edata_committed_set(edata, edata_committed_get(edata) && err); - return err; -} - -static bool -extent_purge_lazy_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length, bool growing_retained) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, growing_retained ? 1 : 0); - bool err = ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), offset, length); - return err; -} - -bool -extent_purge_lazy_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length) { - return extent_purge_lazy_impl(tsdn, ehooks, edata, offset, - length, false); -} - -static bool -extent_purge_forced_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length, bool growing_retained) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, growing_retained ? 1 : 0); - bool err = ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata), - edata_size_get(edata), offset, length); - return err; -} - -bool -extent_purge_forced_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - size_t offset, size_t length) { - return extent_purge_forced_impl(tsdn, ehooks, edata, offset, length, - false); -} - -/* - * Accepts the extent to split, and the characteristics of each side of the - * split. The 'a' parameters go with the 'lead' of the resulting pair of - * extents (the lower addressed portion of the split), and the 'b' parameters go - * with the trail (the higher addressed portion). This makes 'extent' the lead, - * and returns the trail (except in case of error). - */ -static edata_t * -extent_split_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks) { - assert(edata_size_get(edata) == size_a + size_b); - /* Only the shrink path may split w/o holding core locks. */ - if (holding_core_locks) { - witness_assert_positive_depth_to_rank( - tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE); - } else { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - } - - if (ehooks_split_will_fail(ehooks)) { - return NULL; - } - - edata_t *trail = edata_cache_get(tsdn, pac->edata_cache); - if (trail == NULL) { - goto label_error_a; - } - - edata_init(trail, edata_arena_ind_get(edata), - (void *)((uintptr_t)edata_base_get(edata) + size_a), size_b, - /* slab */ false, SC_NSIZES, edata_sn_get(edata), - edata_state_get(edata), edata_zeroed_get(edata), - edata_committed_get(edata), EXTENT_PAI_PAC, EXTENT_NOT_HEAD); - emap_prepare_t prepare; - bool err = emap_split_prepare(tsdn, pac->emap, &prepare, edata, - size_a, trail, size_b); - if (err) { - goto label_error_b; - } - - /* - * No need to acquire trail or edata, because: 1) trail was new (just - * allocated); and 2) edata is either an active allocation (the shrink - * path), or in an acquired state (extracted from the ecache on the - * extent_recycle_split path). - */ - assert(emap_edata_is_acquired(tsdn, pac->emap, edata)); - assert(emap_edata_is_acquired(tsdn, pac->emap, trail)); - - err = ehooks_split(tsdn, ehooks, edata_base_get(edata), size_a + size_b, - size_a, size_b, edata_committed_get(edata)); - - if (err) { - goto label_error_b; - } - - edata_size_set(edata, size_a); - emap_split_commit(tsdn, pac->emap, &prepare, edata, size_a, trail, - size_b); - - return trail; -label_error_b: - edata_cache_put(tsdn, pac->edata_cache, trail); -label_error_a: - return NULL; -} - -edata_t * -extent_split_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata, - size_t size_a, size_t size_b, bool holding_core_locks) { - return extent_split_impl(tsdn, pac, ehooks, edata, size_a, size_b, - holding_core_locks); -} - -static bool -extent_merge_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *a, - edata_t *b, bool holding_core_locks) { - /* Only the expanding path may merge w/o holding ecache locks. */ - if (holding_core_locks) { - witness_assert_positive_depth_to_rank( - tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE); - } else { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - } - - assert(edata_base_get(a) < edata_base_get(b)); - assert(edata_arena_ind_get(a) == edata_arena_ind_get(b)); - assert(edata_arena_ind_get(a) == ehooks_ind_get(ehooks)); - emap_assert_mapped(tsdn, pac->emap, a); - emap_assert_mapped(tsdn, pac->emap, b); - - bool err = ehooks_merge(tsdn, ehooks, edata_base_get(a), - edata_size_get(a), edata_base_get(b), edata_size_get(b), - edata_committed_get(a)); - - if (err) { - return true; - } - - /* - * The rtree writes must happen while all the relevant elements are - * owned, so the following code uses decomposed helper functions rather - * than extent_{,de}register() to do things in the right order. - */ - emap_prepare_t prepare; - emap_merge_prepare(tsdn, pac->emap, &prepare, a, b); - - assert(edata_state_get(a) == extent_state_active || - edata_state_get(a) == extent_state_merging); - edata_state_set(a, extent_state_active); - edata_size_set(a, edata_size_get(a) + edata_size_get(b)); - edata_sn_set(a, (edata_sn_get(a) < edata_sn_get(b)) ? - edata_sn_get(a) : edata_sn_get(b)); - edata_zeroed_set(a, edata_zeroed_get(a) && edata_zeroed_get(b)); - - emap_merge_commit(tsdn, pac->emap, &prepare, a, b); - - edata_cache_put(tsdn, pac->edata_cache, b); - - return false; -} - -bool -extent_merge_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, - edata_t *a, edata_t *b) { - return extent_merge_impl(tsdn, pac, ehooks, a, b, - /* holding_core_locks */ false); -} - -bool -extent_commit_zero(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - bool commit, bool zero, bool growing_retained) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, growing_retained ? 1 : 0); - - if (commit && !edata_committed_get(edata)) { - if (extent_commit_impl(tsdn, ehooks, edata, 0, - edata_size_get(edata), growing_retained)) { - return true; - } - } - if (zero && !edata_zeroed_get(edata)) { - void *addr = edata_base_get(edata); - size_t size = edata_size_get(edata); - ehooks_zero(tsdn, ehooks, addr, size); - } - return false; -} - -bool -extent_boot(void) { - assert(sizeof(slab_data_t) >= sizeof(e_prof_info_t)); - - if (have_dss) { - extent_dss_boot(); - } - - return false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/extent_dss.c b/fluent-bit/lib/jemalloc-5.3.0/src/extent_dss.c deleted file mode 100644 index 9a35bacf..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/extent_dss.c +++ /dev/null @@ -1,277 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/extent_dss.h" -#include "jemalloc/internal/spin.h" - -/******************************************************************************/ -/* Data. */ - -const char *opt_dss = DSS_DEFAULT; - -const char *dss_prec_names[] = { - "disabled", - "primary", - "secondary", - "N/A" -}; - -/* - * Current dss precedence default, used when creating new arenas. NB: This is - * stored as unsigned rather than dss_prec_t because in principle there's no - * guarantee that sizeof(dss_prec_t) is the same as sizeof(unsigned), and we use - * atomic operations to synchronize the setting. - */ -static atomic_u_t dss_prec_default = ATOMIC_INIT( - (unsigned)DSS_PREC_DEFAULT); - -/* Base address of the DSS. */ -static void *dss_base; -/* Atomic boolean indicating whether a thread is currently extending DSS. */ -static atomic_b_t dss_extending; -/* Atomic boolean indicating whether the DSS is exhausted. */ -static atomic_b_t dss_exhausted; -/* Atomic current upper limit on DSS addresses. */ -static atomic_p_t dss_max; - -/******************************************************************************/ - -static void * -extent_dss_sbrk(intptr_t increment) { -#ifdef JEMALLOC_DSS - return sbrk(increment); -#else - not_implemented(); - return NULL; -#endif -} - -dss_prec_t -extent_dss_prec_get(void) { - dss_prec_t ret; - - if (!have_dss) { - return dss_prec_disabled; - } - ret = (dss_prec_t)atomic_load_u(&dss_prec_default, ATOMIC_ACQUIRE); - return ret; -} - -bool -extent_dss_prec_set(dss_prec_t dss_prec) { - if (!have_dss) { - return (dss_prec != dss_prec_disabled); - } - atomic_store_u(&dss_prec_default, (unsigned)dss_prec, ATOMIC_RELEASE); - return false; -} - -static void -extent_dss_extending_start(void) { - spin_t spinner = SPIN_INITIALIZER; - while (true) { - bool expected = false; - if (atomic_compare_exchange_weak_b(&dss_extending, &expected, - true, ATOMIC_ACQ_REL, ATOMIC_RELAXED)) { - break; - } - spin_adaptive(&spinner); - } -} - -static void -extent_dss_extending_finish(void) { - assert(atomic_load_b(&dss_extending, ATOMIC_RELAXED)); - - atomic_store_b(&dss_extending, false, ATOMIC_RELEASE); -} - -static void * -extent_dss_max_update(void *new_addr) { - /* - * Get the current end of the DSS as max_cur and assure that dss_max is - * up to date. - */ - void *max_cur = extent_dss_sbrk(0); - if (max_cur == (void *)-1) { - return NULL; - } - atomic_store_p(&dss_max, max_cur, ATOMIC_RELEASE); - /* Fixed new_addr can only be supported if it is at the edge of DSS. */ - if (new_addr != NULL && max_cur != new_addr) { - return NULL; - } - return max_cur; -} - -void * -extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size, - size_t alignment, bool *zero, bool *commit) { - edata_t *gap; - - cassert(have_dss); - assert(size > 0); - assert(alignment == ALIGNMENT_CEILING(alignment, PAGE)); - - /* - * sbrk() uses a signed increment argument, so take care not to - * interpret a large allocation request as a negative increment. - */ - if ((intptr_t)size < 0) { - return NULL; - } - - gap = edata_cache_get(tsdn, &arena->pa_shard.edata_cache); - if (gap == NULL) { - return NULL; - } - - extent_dss_extending_start(); - if (!atomic_load_b(&dss_exhausted, ATOMIC_ACQUIRE)) { - /* - * The loop is necessary to recover from races with other - * threads that are using the DSS for something other than - * malloc. - */ - while (true) { - void *max_cur = extent_dss_max_update(new_addr); - if (max_cur == NULL) { - goto label_oom; - } - - bool head_state = opt_retain ? EXTENT_IS_HEAD : - EXTENT_NOT_HEAD; - /* - * Compute how much page-aligned gap space (if any) is - * necessary to satisfy alignment. This space can be - * recycled for later use. - */ - void *gap_addr_page = (void *)(PAGE_CEILING( - (uintptr_t)max_cur)); - void *ret = (void *)ALIGNMENT_CEILING( - (uintptr_t)gap_addr_page, alignment); - size_t gap_size_page = (uintptr_t)ret - - (uintptr_t)gap_addr_page; - if (gap_size_page != 0) { - edata_init(gap, arena_ind_get(arena), - gap_addr_page, gap_size_page, false, - SC_NSIZES, extent_sn_next( - &arena->pa_shard.pac), - extent_state_active, false, true, - EXTENT_PAI_PAC, head_state); - } - /* - * Compute the address just past the end of the desired - * allocation space. - */ - void *dss_next = (void *)((uintptr_t)ret + size); - if ((uintptr_t)ret < (uintptr_t)max_cur || - (uintptr_t)dss_next < (uintptr_t)max_cur) { - goto label_oom; /* Wrap-around. */ - } - /* Compute the increment, including subpage bytes. */ - void *gap_addr_subpage = max_cur; - size_t gap_size_subpage = (uintptr_t)ret - - (uintptr_t)gap_addr_subpage; - intptr_t incr = gap_size_subpage + size; - - assert((uintptr_t)max_cur + incr == (uintptr_t)ret + - size); - - /* Try to allocate. */ - void *dss_prev = extent_dss_sbrk(incr); - if (dss_prev == max_cur) { - /* Success. */ - atomic_store_p(&dss_max, dss_next, - ATOMIC_RELEASE); - extent_dss_extending_finish(); - - if (gap_size_page != 0) { - ehooks_t *ehooks = arena_get_ehooks( - arena); - extent_dalloc_gap(tsdn, - &arena->pa_shard.pac, ehooks, gap); - } else { - edata_cache_put(tsdn, - &arena->pa_shard.edata_cache, gap); - } - if (!*commit) { - *commit = pages_decommit(ret, size); - } - if (*zero && *commit) { - edata_t edata = {0}; - ehooks_t *ehooks = arena_get_ehooks( - arena); - - edata_init(&edata, - arena_ind_get(arena), ret, size, - size, false, SC_NSIZES, - extent_state_active, false, true, - EXTENT_PAI_PAC, head_state); - if (extent_purge_forced_wrapper(tsdn, - ehooks, &edata, 0, size)) { - memset(ret, 0, size); - } - } - return ret; - } - /* - * Failure, whether due to OOM or a race with a raw - * sbrk() call from outside the allocator. - */ - if (dss_prev == (void *)-1) { - /* OOM. */ - atomic_store_b(&dss_exhausted, true, - ATOMIC_RELEASE); - goto label_oom; - } - } - } -label_oom: - extent_dss_extending_finish(); - edata_cache_put(tsdn, &arena->pa_shard.edata_cache, gap); - return NULL; -} - -static bool -extent_in_dss_helper(void *addr, void *max) { - return ((uintptr_t)addr >= (uintptr_t)dss_base && (uintptr_t)addr < - (uintptr_t)max); -} - -bool -extent_in_dss(void *addr) { - cassert(have_dss); - - return extent_in_dss_helper(addr, atomic_load_p(&dss_max, - ATOMIC_ACQUIRE)); -} - -bool -extent_dss_mergeable(void *addr_a, void *addr_b) { - void *max; - - cassert(have_dss); - - if ((uintptr_t)addr_a < (uintptr_t)dss_base && (uintptr_t)addr_b < - (uintptr_t)dss_base) { - return true; - } - - max = atomic_load_p(&dss_max, ATOMIC_ACQUIRE); - return (extent_in_dss_helper(addr_a, max) == - extent_in_dss_helper(addr_b, max)); -} - -void -extent_dss_boot(void) { - cassert(have_dss); - - dss_base = extent_dss_sbrk(0); - atomic_store_b(&dss_extending, false, ATOMIC_RELAXED); - atomic_store_b(&dss_exhausted, dss_base == (void *)-1, ATOMIC_RELAXED); - atomic_store_p(&dss_max, dss_base, ATOMIC_RELAXED); -} - -/******************************************************************************/ diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/extent_mmap.c b/fluent-bit/lib/jemalloc-5.3.0/src/extent_mmap.c deleted file mode 100644 index 5f0ee2d2..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/extent_mmap.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/extent_mmap.h" - -/******************************************************************************/ -/* Data. */ - -bool opt_retain = -#ifdef JEMALLOC_RETAIN - true -#else - false -#endif - ; - -/******************************************************************************/ - -void * -extent_alloc_mmap(void *new_addr, size_t size, size_t alignment, bool *zero, - bool *commit) { - assert(alignment == ALIGNMENT_CEILING(alignment, PAGE)); - void *ret = pages_map(new_addr, size, alignment, commit); - if (ret == NULL) { - return NULL; - } - assert(ret != NULL); - if (*commit) { - *zero = true; - } - return ret; -} - -bool -extent_dalloc_mmap(void *addr, size_t size) { - if (!opt_retain) { - pages_unmap(addr, size); - } - return opt_retain; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/fxp.c b/fluent-bit/lib/jemalloc-5.3.0/src/fxp.c deleted file mode 100644 index 96585f0a..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/fxp.c +++ /dev/null @@ -1,124 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/fxp.h" - -static bool -fxp_isdigit(char c) { - return '0' <= c && c <= '9'; -} - -bool -fxp_parse(fxp_t *result, const char *str, char **end) { - /* - * Using malloc_strtoumax in this method isn't as handy as you might - * expect (I tried). In the fractional part, significant leading zeros - * mean that you still need to do your own parsing, now with trickier - * math. In the integer part, the casting (uintmax_t to uint32_t) - * forces more reasoning about bounds than just checking for overflow as - * we parse. - */ - uint32_t integer_part = 0; - - const char *cur = str; - - /* The string must start with a digit or a decimal point. */ - if (*cur != '.' && !fxp_isdigit(*cur)) { - return true; - } - - while ('0' <= *cur && *cur <= '9') { - integer_part *= 10; - integer_part += *cur - '0'; - if (integer_part >= (1U << 16)) { - return true; - } - cur++; - } - - /* - * We've parsed all digits at the beginning of the string, without - * overflow. Either we're done, or there's a fractional part. - */ - if (*cur != '.') { - *result = (integer_part << 16); - if (end != NULL) { - *end = (char *)cur; - } - return false; - } - - /* There's a fractional part. */ - cur++; - if (!fxp_isdigit(*cur)) { - /* Shouldn't end on the decimal point. */ - return true; - } - - /* - * We use a lot of precision for the fractional part, even though we'll - * discard most of it; this lets us get exact values for the important - * special case where the denominator is a small power of 2 (for - * instance, 1/512 == 0.001953125 is exactly representable even with - * only 16 bits of fractional precision). We need to left-shift by 16 - * before dividing so we pick the number of digits to be - * floor(log(2**48)) = 14. - */ - uint64_t fractional_part = 0; - uint64_t frac_div = 1; - for (int i = 0; i < FXP_FRACTIONAL_PART_DIGITS; i++) { - fractional_part *= 10; - frac_div *= 10; - if (fxp_isdigit(*cur)) { - fractional_part += *cur - '0'; - cur++; - } - } - /* - * We only parse the first maxdigits characters, but we can still ignore - * any digits after that. - */ - while (fxp_isdigit(*cur)) { - cur++; - } - - assert(fractional_part < frac_div); - uint32_t fractional_repr = (uint32_t)( - (fractional_part << 16) / frac_div); - - /* Success! */ - *result = (integer_part << 16) + fractional_repr; - if (end != NULL) { - *end = (char *)cur; - } - return false; -} - -void -fxp_print(fxp_t a, char buf[FXP_BUF_SIZE]) { - uint32_t integer_part = fxp_round_down(a); - uint32_t fractional_part = (a & ((1U << 16) - 1)); - - int leading_fraction_zeros = 0; - uint64_t fraction_digits = fractional_part; - for (int i = 0; i < FXP_FRACTIONAL_PART_DIGITS; i++) { - if (fraction_digits < (1U << 16) - && fraction_digits * 10 >= (1U << 16)) { - leading_fraction_zeros = i; - } - fraction_digits *= 10; - } - fraction_digits >>= 16; - while (fraction_digits > 0 && fraction_digits % 10 == 0) { - fraction_digits /= 10; - } - - size_t printed = malloc_snprintf(buf, FXP_BUF_SIZE, "%"FMTu32".", - integer_part); - for (int i = 0; i < leading_fraction_zeros; i++) { - buf[printed] = '0'; - printed++; - } - malloc_snprintf(&buf[printed], FXP_BUF_SIZE - printed, "%"FMTu64, - fraction_digits); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/hook.c b/fluent-bit/lib/jemalloc-5.3.0/src/hook.c deleted file mode 100644 index 493edbbe..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/hook.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" - -#include "jemalloc/internal/hook.h" - -#include "jemalloc/internal/atomic.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/seq.h" - -typedef struct hooks_internal_s hooks_internal_t; -struct hooks_internal_s { - hooks_t hooks; - bool in_use; -}; - -seq_define(hooks_internal_t, hooks) - -static atomic_u_t nhooks = ATOMIC_INIT(0); -static seq_hooks_t hooks[HOOK_MAX]; -static malloc_mutex_t hooks_mu; - -bool -hook_boot() { - return malloc_mutex_init(&hooks_mu, "hooks", WITNESS_RANK_HOOK, - malloc_mutex_rank_exclusive); -} - -static void * -hook_install_locked(hooks_t *to_install) { - hooks_internal_t hooks_internal; - for (int i = 0; i < HOOK_MAX; i++) { - bool success = seq_try_load_hooks(&hooks_internal, &hooks[i]); - /* We hold mu; no concurrent access. */ - assert(success); - if (!hooks_internal.in_use) { - hooks_internal.hooks = *to_install; - hooks_internal.in_use = true; - seq_store_hooks(&hooks[i], &hooks_internal); - atomic_store_u(&nhooks, - atomic_load_u(&nhooks, ATOMIC_RELAXED) + 1, - ATOMIC_RELAXED); - return &hooks[i]; - } - } - return NULL; -} - -void * -hook_install(tsdn_t *tsdn, hooks_t *to_install) { - malloc_mutex_lock(tsdn, &hooks_mu); - void *ret = hook_install_locked(to_install); - if (ret != NULL) { - tsd_global_slow_inc(tsdn); - } - malloc_mutex_unlock(tsdn, &hooks_mu); - return ret; -} - -static void -hook_remove_locked(seq_hooks_t *to_remove) { - hooks_internal_t hooks_internal; - bool success = seq_try_load_hooks(&hooks_internal, to_remove); - /* We hold mu; no concurrent access. */ - assert(success); - /* Should only remove hooks that were added. */ - assert(hooks_internal.in_use); - hooks_internal.in_use = false; - seq_store_hooks(to_remove, &hooks_internal); - atomic_store_u(&nhooks, atomic_load_u(&nhooks, ATOMIC_RELAXED) - 1, - ATOMIC_RELAXED); -} - -void -hook_remove(tsdn_t *tsdn, void *opaque) { - if (config_debug) { - char *hooks_begin = (char *)&hooks[0]; - char *hooks_end = (char *)&hooks[HOOK_MAX]; - char *hook = (char *)opaque; - assert(hooks_begin <= hook && hook < hooks_end - && (hook - hooks_begin) % sizeof(seq_hooks_t) == 0); - } - malloc_mutex_lock(tsdn, &hooks_mu); - hook_remove_locked((seq_hooks_t *)opaque); - tsd_global_slow_dec(tsdn); - malloc_mutex_unlock(tsdn, &hooks_mu); -} - -#define FOR_EACH_HOOK_BEGIN(hooks_internal_ptr) \ -for (int for_each_hook_counter = 0; \ - for_each_hook_counter < HOOK_MAX; \ - for_each_hook_counter++) { \ - bool for_each_hook_success = seq_try_load_hooks( \ - (hooks_internal_ptr), &hooks[for_each_hook_counter]); \ - if (!for_each_hook_success) { \ - continue; \ - } \ - if (!(hooks_internal_ptr)->in_use) { \ - continue; \ - } -#define FOR_EACH_HOOK_END \ -} - -static bool * -hook_reentrantp() { - /* - * We prevent user reentrancy within hooks. This is basically just a - * thread-local bool that triggers an early-exit. - * - * We don't fold in_hook into reentrancy. There are two reasons for - * this: - * - Right now, we turn on reentrancy during things like extent hook - * execution. Allocating during extent hooks is not officially - * supported, but we don't want to break it for the time being. These - * sorts of allocations should probably still be hooked, though. - * - If a hook allocates, we may want it to be relatively fast (after - * all, it executes on every allocator operation). Turning on - * reentrancy is a fairly heavyweight mode (disabling tcache, - * redirecting to arena 0, etc.). It's possible we may one day want - * to turn on reentrant mode here, if it proves too difficult to keep - * this working. But that's fairly easy for us to see; OTOH, people - * not using hooks because they're too slow is easy for us to miss. - * - * The tricky part is - * that this code might get invoked even if we don't have access to tsd. - * This function mimics getting a pointer to thread-local data, except - * that it might secretly return a pointer to some global data if we - * know that the caller will take the early-exit path. - * If we return a bool that indicates that we are reentrant, then the - * caller will go down the early exit path, leaving the global - * untouched. - */ - static bool in_hook_global = true; - tsdn_t *tsdn = tsdn_fetch(); - bool *in_hook = tsdn_in_hookp_get(tsdn); - if (in_hook!= NULL) { - return in_hook; - } - return &in_hook_global; -} - -#define HOOK_PROLOGUE \ - if (likely(atomic_load_u(&nhooks, ATOMIC_RELAXED) == 0)) { \ - return; \ - } \ - bool *in_hook = hook_reentrantp(); \ - if (*in_hook) { \ - return; \ - } \ - *in_hook = true; - -#define HOOK_EPILOGUE \ - *in_hook = false; - -void -hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw, - uintptr_t args_raw[3]) { - HOOK_PROLOGUE - - hooks_internal_t hook; - FOR_EACH_HOOK_BEGIN(&hook) - hook_alloc h = hook.hooks.alloc_hook; - if (h != NULL) { - h(hook.hooks.extra, type, result, result_raw, args_raw); - } - FOR_EACH_HOOK_END - - HOOK_EPILOGUE -} - -void -hook_invoke_dalloc(hook_dalloc_t type, void *address, uintptr_t args_raw[3]) { - HOOK_PROLOGUE - hooks_internal_t hook; - FOR_EACH_HOOK_BEGIN(&hook) - hook_dalloc h = hook.hooks.dalloc_hook; - if (h != NULL) { - h(hook.hooks.extra, type, address, args_raw); - } - FOR_EACH_HOOK_END - HOOK_EPILOGUE -} - -void -hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize, - size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]) { - HOOK_PROLOGUE - hooks_internal_t hook; - FOR_EACH_HOOK_BEGIN(&hook) - hook_expand h = hook.hooks.expand_hook; - if (h != NULL) { - h(hook.hooks.extra, type, address, old_usize, new_usize, - result_raw, args_raw); - } - FOR_EACH_HOOK_END - HOOK_EPILOGUE -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/hpa.c b/fluent-bit/lib/jemalloc-5.3.0/src/hpa.c deleted file mode 100644 index 7e2aeba0..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/hpa.c +++ /dev/null @@ -1,1044 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/hpa.h" - -#include "jemalloc/internal/fb.h" -#include "jemalloc/internal/witness.h" - -#define HPA_EDEN_SIZE (128 * HUGEPAGE) - -static edata_t *hpa_alloc(tsdn_t *tsdn, pai_t *self, size_t size, - size_t alignment, bool zero, bool guarded, bool frequent_reuse, - bool *deferred_work_generated); -static size_t hpa_alloc_batch(tsdn_t *tsdn, pai_t *self, size_t size, - size_t nallocs, edata_list_active_t *results, bool *deferred_work_generated); -static bool hpa_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool zero, bool *deferred_work_generated); -static bool hpa_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool *deferred_work_generated); -static void hpa_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata, - bool *deferred_work_generated); -static void hpa_dalloc_batch(tsdn_t *tsdn, pai_t *self, - edata_list_active_t *list, bool *deferred_work_generated); -static uint64_t hpa_time_until_deferred_work(tsdn_t *tsdn, pai_t *self); - -bool -hpa_supported() { -#ifdef _WIN32 - /* - * At least until the API and implementation is somewhat settled, we - * don't want to try to debug the VM subsystem on the hardest-to-test - * platform. - */ - return false; -#endif - if (!pages_can_hugify) { - return false; - } - /* - * We fundamentally rely on a address-space-hungry growth strategy for - * hugepages. - */ - if (LG_SIZEOF_PTR != 3) { - return false; - } - /* - * If we couldn't detect the value of HUGEPAGE, HUGEPAGE_PAGES becomes - * this sentinel value -- see the comment in pages.h. - */ - if (HUGEPAGE_PAGES == 1) { - return false; - } - return true; -} - -static void -hpa_do_consistency_checks(hpa_shard_t *shard) { - assert(shard->base != NULL); -} - -bool -hpa_central_init(hpa_central_t *central, base_t *base, const hpa_hooks_t *hooks) { - /* malloc_conf processing should have filtered out these cases. */ - assert(hpa_supported()); - bool err; - err = malloc_mutex_init(¢ral->grow_mtx, "hpa_central_grow", - WITNESS_RANK_HPA_CENTRAL_GROW, malloc_mutex_rank_exclusive); - if (err) { - return true; - } - err = malloc_mutex_init(¢ral->mtx, "hpa_central", - WITNESS_RANK_HPA_CENTRAL, malloc_mutex_rank_exclusive); - if (err) { - return true; - } - central->base = base; - central->eden = NULL; - central->eden_len = 0; - central->age_counter = 0; - central->hooks = *hooks; - return false; -} - -static hpdata_t * -hpa_alloc_ps(tsdn_t *tsdn, hpa_central_t *central) { - return (hpdata_t *)base_alloc(tsdn, central->base, sizeof(hpdata_t), - CACHELINE); -} - -hpdata_t * -hpa_central_extract(tsdn_t *tsdn, hpa_central_t *central, size_t size, - bool *oom) { - /* Don't yet support big allocations; these should get filtered out. */ - assert(size <= HUGEPAGE); - /* - * Should only try to extract from the central allocator if the local - * shard is exhausted. We should hold the grow_mtx on that shard. - */ - witness_assert_positive_depth_to_rank( - tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_HPA_SHARD_GROW); - - malloc_mutex_lock(tsdn, ¢ral->grow_mtx); - *oom = false; - - hpdata_t *ps = NULL; - - /* Is eden a perfect fit? */ - if (central->eden != NULL && central->eden_len == HUGEPAGE) { - ps = hpa_alloc_ps(tsdn, central); - if (ps == NULL) { - *oom = true; - malloc_mutex_unlock(tsdn, ¢ral->grow_mtx); - return NULL; - } - hpdata_init(ps, central->eden, central->age_counter++); - central->eden = NULL; - central->eden_len = 0; - malloc_mutex_unlock(tsdn, ¢ral->grow_mtx); - return ps; - } - - /* - * We're about to try to allocate from eden by splitting. If eden is - * NULL, we have to allocate it too. Otherwise, we just have to - * allocate an edata_t for the new psset. - */ - if (central->eden == NULL) { - /* - * During development, we're primarily concerned with systems - * with overcommit. Eventually, we should be more careful here. - */ - bool commit = true; - /* Allocate address space, bailing if we fail. */ - void *new_eden = pages_map(NULL, HPA_EDEN_SIZE, HUGEPAGE, - &commit); - if (new_eden == NULL) { - *oom = true; - malloc_mutex_unlock(tsdn, ¢ral->grow_mtx); - return NULL; - } - ps = hpa_alloc_ps(tsdn, central); - if (ps == NULL) { - pages_unmap(new_eden, HPA_EDEN_SIZE); - *oom = true; - malloc_mutex_unlock(tsdn, ¢ral->grow_mtx); - return NULL; - } - central->eden = new_eden; - central->eden_len = HPA_EDEN_SIZE; - } else { - /* Eden is already nonempty; only need an edata for ps. */ - ps = hpa_alloc_ps(tsdn, central); - if (ps == NULL) { - *oom = true; - malloc_mutex_unlock(tsdn, ¢ral->grow_mtx); - return NULL; - } - } - assert(ps != NULL); - assert(central->eden != NULL); - assert(central->eden_len > HUGEPAGE); - assert(central->eden_len % HUGEPAGE == 0); - assert(HUGEPAGE_ADDR2BASE(central->eden) == central->eden); - - hpdata_init(ps, central->eden, central->age_counter++); - - char *eden_char = (char *)central->eden; - eden_char += HUGEPAGE; - central->eden = (void *)eden_char; - central->eden_len -= HUGEPAGE; - - malloc_mutex_unlock(tsdn, ¢ral->grow_mtx); - - return ps; -} - -bool -hpa_shard_init(hpa_shard_t *shard, hpa_central_t *central, emap_t *emap, - base_t *base, edata_cache_t *edata_cache, unsigned ind, - const hpa_shard_opts_t *opts) { - /* malloc_conf processing should have filtered out these cases. */ - assert(hpa_supported()); - bool err; - err = malloc_mutex_init(&shard->grow_mtx, "hpa_shard_grow", - WITNESS_RANK_HPA_SHARD_GROW, malloc_mutex_rank_exclusive); - if (err) { - return true; - } - err = malloc_mutex_init(&shard->mtx, "hpa_shard", - WITNESS_RANK_HPA_SHARD, malloc_mutex_rank_exclusive); - if (err) { - return true; - } - - assert(edata_cache != NULL); - shard->central = central; - shard->base = base; - edata_cache_fast_init(&shard->ecf, edata_cache); - psset_init(&shard->psset); - shard->age_counter = 0; - shard->ind = ind; - shard->emap = emap; - - shard->opts = *opts; - - shard->npending_purge = 0; - nstime_init_zero(&shard->last_purge); - - shard->stats.npurge_passes = 0; - shard->stats.npurges = 0; - shard->stats.nhugifies = 0; - shard->stats.ndehugifies = 0; - - /* - * Fill these in last, so that if an hpa_shard gets used despite - * initialization failing, we'll at least crash instead of just - * operating on corrupted data. - */ - shard->pai.alloc = &hpa_alloc; - shard->pai.alloc_batch = &hpa_alloc_batch; - shard->pai.expand = &hpa_expand; - shard->pai.shrink = &hpa_shrink; - shard->pai.dalloc = &hpa_dalloc; - shard->pai.dalloc_batch = &hpa_dalloc_batch; - shard->pai.time_until_deferred_work = &hpa_time_until_deferred_work; - - hpa_do_consistency_checks(shard); - - return false; -} - -/* - * Note that the stats functions here follow the usual stats naming conventions; - * "merge" obtains the stats from some live object of instance, while "accum" - * only combines the stats from one stats objet to another. Hence the lack of - * locking here. - */ -static void -hpa_shard_nonderived_stats_accum(hpa_shard_nonderived_stats_t *dst, - hpa_shard_nonderived_stats_t *src) { - dst->npurge_passes += src->npurge_passes; - dst->npurges += src->npurges; - dst->nhugifies += src->nhugifies; - dst->ndehugifies += src->ndehugifies; -} - -void -hpa_shard_stats_accum(hpa_shard_stats_t *dst, hpa_shard_stats_t *src) { - psset_stats_accum(&dst->psset_stats, &src->psset_stats); - hpa_shard_nonderived_stats_accum(&dst->nonderived_stats, - &src->nonderived_stats); -} - -void -hpa_shard_stats_merge(tsdn_t *tsdn, hpa_shard_t *shard, - hpa_shard_stats_t *dst) { - hpa_do_consistency_checks(shard); - - malloc_mutex_lock(tsdn, &shard->grow_mtx); - malloc_mutex_lock(tsdn, &shard->mtx); - psset_stats_accum(&dst->psset_stats, &shard->psset.stats); - hpa_shard_nonderived_stats_accum(&dst->nonderived_stats, &shard->stats); - malloc_mutex_unlock(tsdn, &shard->mtx); - malloc_mutex_unlock(tsdn, &shard->grow_mtx); -} - -static bool -hpa_good_hugification_candidate(hpa_shard_t *shard, hpdata_t *ps) { - /* - * Note that this needs to be >= rather than just >, because of the - * important special case in which the hugification threshold is exactly - * HUGEPAGE. - */ - return hpdata_nactive_get(ps) * PAGE - >= shard->opts.hugification_threshold; -} - -static size_t -hpa_adjusted_ndirty(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - return psset_ndirty(&shard->psset) - shard->npending_purge; -} - -static size_t -hpa_ndirty_max(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - if (shard->opts.dirty_mult == (fxp_t)-1) { - return (size_t)-1; - } - return fxp_mul_frac(psset_nactive(&shard->psset), - shard->opts.dirty_mult); -} - -static bool -hpa_hugify_blocked_by_ndirty(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - hpdata_t *to_hugify = psset_pick_hugify(&shard->psset); - if (to_hugify == NULL) { - return false; - } - return hpa_adjusted_ndirty(tsdn, shard) - + hpdata_nretained_get(to_hugify) > hpa_ndirty_max(tsdn, shard); -} - -static bool -hpa_should_purge(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - if (hpa_adjusted_ndirty(tsdn, shard) > hpa_ndirty_max(tsdn, shard)) { - return true; - } - if (hpa_hugify_blocked_by_ndirty(tsdn, shard)) { - return true; - } - return false; -} - -static void -hpa_update_purge_hugify_eligibility(tsdn_t *tsdn, hpa_shard_t *shard, - hpdata_t *ps) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - if (hpdata_changing_state_get(ps)) { - hpdata_purge_allowed_set(ps, false); - hpdata_disallow_hugify(ps); - return; - } - /* - * Hugepages are distinctly costly to purge, so try to avoid it unless - * they're *particularly* full of dirty pages. Eventually, we should - * use a smarter / more dynamic heuristic for situations where we have - * to manually hugify. - * - * In situations where we don't manually hugify, this problem is - * reduced. The "bad" situation we're trying to avoid is one's that's - * common in some Linux configurations (where both enabled and defrag - * are set to madvise) that can lead to long latency spikes on the first - * access after a hugification. The ideal policy in such configurations - * is probably time-based for both purging and hugifying; only hugify a - * hugepage if it's met the criteria for some extended period of time, - * and only dehugify it if it's failed to meet the criteria for an - * extended period of time. When background threads are on, we should - * try to take this hit on one of them, as well. - * - * I think the ideal setting is THP always enabled, and defrag set to - * deferred; in that case we don't need any explicit calls on the - * allocator's end at all; we just try to pack allocations in a - * hugepage-friendly manner and let the OS hugify in the background. - */ - hpdata_purge_allowed_set(ps, hpdata_ndirty_get(ps) > 0); - if (hpa_good_hugification_candidate(shard, ps) - && !hpdata_huge_get(ps)) { - nstime_t now; - shard->central->hooks.curtime(&now, /* first_reading */ true); - hpdata_allow_hugify(ps, now); - } - /* - * Once a hugepage has become eligible for hugification, we don't mark - * it as ineligible just because it stops meeting the criteria (this - * could lead to situations where a hugepage that spends most of its - * time meeting the criteria never quite getting hugified if there are - * intervening deallocations). The idea is that the hugification delay - * will allow them to get purged, reseting their "hugify-allowed" bit. - * If they don't get purged, then the hugification isn't hurting and - * might help. As an exception, we don't hugify hugepages that are now - * empty; it definitely doesn't help there until the hugepage gets - * reused, which is likely not for a while. - */ - if (hpdata_nactive_get(ps) == 0) { - hpdata_disallow_hugify(ps); - } -} - -static bool -hpa_shard_has_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - hpdata_t *to_hugify = psset_pick_hugify(&shard->psset); - return to_hugify != NULL || hpa_should_purge(tsdn, shard); -} - -/* Returns whether or not we purged anything. */ -static bool -hpa_try_purge(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - - hpdata_t *to_purge = psset_pick_purge(&shard->psset); - if (to_purge == NULL) { - return false; - } - assert(hpdata_purge_allowed_get(to_purge)); - assert(!hpdata_changing_state_get(to_purge)); - - /* - * Don't let anyone else purge or hugify this page while - * we're purging it (allocations and deallocations are - * OK). - */ - psset_update_begin(&shard->psset, to_purge); - assert(hpdata_alloc_allowed_get(to_purge)); - hpdata_mid_purge_set(to_purge, true); - hpdata_purge_allowed_set(to_purge, false); - hpdata_disallow_hugify(to_purge); - /* - * Unlike with hugification (where concurrent - * allocations are allowed), concurrent allocation out - * of a hugepage being purged is unsafe; we might hand - * out an extent for an allocation and then purge it - * (clearing out user data). - */ - hpdata_alloc_allowed_set(to_purge, false); - psset_update_end(&shard->psset, to_purge); - - /* Gather all the metadata we'll need during the purge. */ - bool dehugify = hpdata_huge_get(to_purge); - hpdata_purge_state_t purge_state; - size_t num_to_purge = hpdata_purge_begin(to_purge, &purge_state); - - shard->npending_purge += num_to_purge; - - malloc_mutex_unlock(tsdn, &shard->mtx); - - /* Actually do the purging, now that the lock is dropped. */ - if (dehugify) { - shard->central->hooks.dehugify(hpdata_addr_get(to_purge), - HUGEPAGE); - } - size_t total_purged = 0; - uint64_t purges_this_pass = 0; - void *purge_addr; - size_t purge_size; - while (hpdata_purge_next(to_purge, &purge_state, &purge_addr, - &purge_size)) { - total_purged += purge_size; - assert(total_purged <= HUGEPAGE); - purges_this_pass++; - shard->central->hooks.purge(purge_addr, purge_size); - } - - malloc_mutex_lock(tsdn, &shard->mtx); - /* The shard updates */ - shard->npending_purge -= num_to_purge; - shard->stats.npurge_passes++; - shard->stats.npurges += purges_this_pass; - shard->central->hooks.curtime(&shard->last_purge, - /* first_reading */ false); - if (dehugify) { - shard->stats.ndehugifies++; - } - - /* The hpdata updates. */ - psset_update_begin(&shard->psset, to_purge); - if (dehugify) { - hpdata_dehugify(to_purge); - } - hpdata_purge_end(to_purge, &purge_state); - hpdata_mid_purge_set(to_purge, false); - - hpdata_alloc_allowed_set(to_purge, true); - hpa_update_purge_hugify_eligibility(tsdn, shard, to_purge); - - psset_update_end(&shard->psset, to_purge); - - return true; -} - -/* Returns whether or not we hugified anything. */ -static bool -hpa_try_hugify(tsdn_t *tsdn, hpa_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - - if (hpa_hugify_blocked_by_ndirty(tsdn, shard)) { - return false; - } - - hpdata_t *to_hugify = psset_pick_hugify(&shard->psset); - if (to_hugify == NULL) { - return false; - } - assert(hpdata_hugify_allowed_get(to_hugify)); - assert(!hpdata_changing_state_get(to_hugify)); - - /* Make sure that it's been hugifiable for long enough. */ - nstime_t time_hugify_allowed = hpdata_time_hugify_allowed(to_hugify); - uint64_t millis = shard->central->hooks.ms_since(&time_hugify_allowed); - if (millis < shard->opts.hugify_delay_ms) { - return false; - } - - /* - * Don't let anyone else purge or hugify this page while - * we're hugifying it (allocations and deallocations are - * OK). - */ - psset_update_begin(&shard->psset, to_hugify); - hpdata_mid_hugify_set(to_hugify, true); - hpdata_purge_allowed_set(to_hugify, false); - hpdata_disallow_hugify(to_hugify); - assert(hpdata_alloc_allowed_get(to_hugify)); - psset_update_end(&shard->psset, to_hugify); - - malloc_mutex_unlock(tsdn, &shard->mtx); - - shard->central->hooks.hugify(hpdata_addr_get(to_hugify), HUGEPAGE); - - malloc_mutex_lock(tsdn, &shard->mtx); - shard->stats.nhugifies++; - - psset_update_begin(&shard->psset, to_hugify); - hpdata_hugify(to_hugify); - hpdata_mid_hugify_set(to_hugify, false); - hpa_update_purge_hugify_eligibility(tsdn, shard, to_hugify); - psset_update_end(&shard->psset, to_hugify); - - return true; -} - -/* - * Execution of deferred work is forced if it's triggered by an explicit - * hpa_shard_do_deferred_work() call. - */ -static void -hpa_shard_maybe_do_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard, - bool forced) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - if (!forced && shard->opts.deferral_allowed) { - return; - } - /* - * If we're on a background thread, do work so long as there's work to - * be done. Otherwise, bound latency to not be *too* bad by doing at - * most a small fixed number of operations. - */ - bool hugified = false; - bool purged = false; - size_t max_ops = (forced ? (size_t)-1 : 16); - size_t nops = 0; - do { - /* - * Always purge before hugifying, to make sure we get some - * ability to hit our quiescence targets. - */ - purged = false; - while (hpa_should_purge(tsdn, shard) && nops < max_ops) { - purged = hpa_try_purge(tsdn, shard); - if (purged) { - nops++; - } - } - hugified = hpa_try_hugify(tsdn, shard); - if (hugified) { - nops++; - } - malloc_mutex_assert_owner(tsdn, &shard->mtx); - malloc_mutex_assert_owner(tsdn, &shard->mtx); - } while ((hugified || purged) && nops < max_ops); -} - -static edata_t * -hpa_try_alloc_one_no_grow(tsdn_t *tsdn, hpa_shard_t *shard, size_t size, - bool *oom) { - bool err; - edata_t *edata = edata_cache_fast_get(tsdn, &shard->ecf); - if (edata == NULL) { - *oom = true; - return NULL; - } - - hpdata_t *ps = psset_pick_alloc(&shard->psset, size); - if (ps == NULL) { - edata_cache_fast_put(tsdn, &shard->ecf, edata); - return NULL; - } - - psset_update_begin(&shard->psset, ps); - - if (hpdata_empty(ps)) { - /* - * If the pageslab used to be empty, treat it as though it's - * brand new for fragmentation-avoidance purposes; what we're - * trying to approximate is the age of the allocations *in* that - * pageslab, and the allocations in the new pageslab are - * definitionally the youngest in this hpa shard. - */ - hpdata_age_set(ps, shard->age_counter++); - } - - void *addr = hpdata_reserve_alloc(ps, size); - edata_init(edata, shard->ind, addr, size, /* slab */ false, - SC_NSIZES, /* sn */ hpdata_age_get(ps), extent_state_active, - /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA, - EXTENT_NOT_HEAD); - edata_ps_set(edata, ps); - - /* - * This could theoretically be moved outside of the critical section, - * but that introduces the potential for a race. Without the lock, the - * (initially nonempty, since this is the reuse pathway) pageslab we - * allocated out of could become otherwise empty while the lock is - * dropped. This would force us to deal with a pageslab eviction down - * the error pathway, which is a pain. - */ - err = emap_register_boundary(tsdn, shard->emap, edata, - SC_NSIZES, /* slab */ false); - if (err) { - hpdata_unreserve(ps, edata_addr_get(edata), - edata_size_get(edata)); - /* - * We should arguably reset dirty state here, but this would - * require some sort of prepare + commit functionality that's a - * little much to deal with for now. - * - * We don't have a do_deferred_work down this pathway, on the - * principle that we didn't *really* affect shard state (we - * tweaked the stats, but our tweaks weren't really accurate). - */ - psset_update_end(&shard->psset, ps); - edata_cache_fast_put(tsdn, &shard->ecf, edata); - *oom = true; - return NULL; - } - - hpa_update_purge_hugify_eligibility(tsdn, shard, ps); - psset_update_end(&shard->psset, ps); - return edata; -} - -static size_t -hpa_try_alloc_batch_no_grow(tsdn_t *tsdn, hpa_shard_t *shard, size_t size, - bool *oom, size_t nallocs, edata_list_active_t *results, - bool *deferred_work_generated) { - malloc_mutex_lock(tsdn, &shard->mtx); - size_t nsuccess = 0; - for (; nsuccess < nallocs; nsuccess++) { - edata_t *edata = hpa_try_alloc_one_no_grow(tsdn, shard, size, - oom); - if (edata == NULL) { - break; - } - edata_list_active_append(results, edata); - } - - hpa_shard_maybe_do_deferred_work(tsdn, shard, /* forced */ false); - *deferred_work_generated = hpa_shard_has_deferred_work(tsdn, shard); - malloc_mutex_unlock(tsdn, &shard->mtx); - return nsuccess; -} - -static size_t -hpa_alloc_batch_psset(tsdn_t *tsdn, hpa_shard_t *shard, size_t size, - size_t nallocs, edata_list_active_t *results, - bool *deferred_work_generated) { - assert(size <= shard->opts.slab_max_alloc); - bool oom = false; - - size_t nsuccess = hpa_try_alloc_batch_no_grow(tsdn, shard, size, &oom, - nallocs, results, deferred_work_generated); - - if (nsuccess == nallocs || oom) { - return nsuccess; - } - - /* - * We didn't OOM, but weren't able to fill everything requested of us; - * try to grow. - */ - malloc_mutex_lock(tsdn, &shard->grow_mtx); - /* - * Check for grow races; maybe some earlier thread expanded the psset - * in between when we dropped the main mutex and grabbed the grow mutex. - */ - nsuccess += hpa_try_alloc_batch_no_grow(tsdn, shard, size, &oom, - nallocs - nsuccess, results, deferred_work_generated); - if (nsuccess == nallocs || oom) { - malloc_mutex_unlock(tsdn, &shard->grow_mtx); - return nsuccess; - } - - /* - * Note that we don't hold shard->mtx here (while growing); - * deallocations (and allocations of smaller sizes) may still succeed - * while we're doing this potentially expensive system call. - */ - hpdata_t *ps = hpa_central_extract(tsdn, shard->central, size, &oom); - if (ps == NULL) { - malloc_mutex_unlock(tsdn, &shard->grow_mtx); - return nsuccess; - } - - /* - * We got the pageslab; allocate from it. This does an unlock followed - * by a lock on the same mutex, and holds the grow mutex while doing - * deferred work, but this is an uncommon path; the simplicity is worth - * it. - */ - malloc_mutex_lock(tsdn, &shard->mtx); - psset_insert(&shard->psset, ps); - malloc_mutex_unlock(tsdn, &shard->mtx); - - nsuccess += hpa_try_alloc_batch_no_grow(tsdn, shard, size, &oom, - nallocs - nsuccess, results, deferred_work_generated); - /* - * Drop grow_mtx before doing deferred work; other threads blocked on it - * should be allowed to proceed while we're working. - */ - malloc_mutex_unlock(tsdn, &shard->grow_mtx); - - return nsuccess; -} - -static hpa_shard_t * -hpa_from_pai(pai_t *self) { - assert(self->alloc = &hpa_alloc); - assert(self->expand = &hpa_expand); - assert(self->shrink = &hpa_shrink); - assert(self->dalloc = &hpa_dalloc); - return (hpa_shard_t *)self; -} - -static size_t -hpa_alloc_batch(tsdn_t *tsdn, pai_t *self, size_t size, size_t nallocs, - edata_list_active_t *results, bool *deferred_work_generated) { - assert(nallocs > 0); - assert((size & PAGE_MASK) == 0); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - hpa_shard_t *shard = hpa_from_pai(self); - - if (size > shard->opts.slab_max_alloc) { - return 0; - } - - size_t nsuccess = hpa_alloc_batch_psset(tsdn, shard, size, nallocs, - results, deferred_work_generated); - - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - /* - * Guard the sanity checks with config_debug because the loop cannot be - * proven non-circular by the compiler, even if everything within the - * loop is optimized away. - */ - if (config_debug) { - edata_t *edata; - ql_foreach(edata, &results->head, ql_link_active) { - emap_assert_mapped(tsdn, shard->emap, edata); - assert(edata_pai_get(edata) == EXTENT_PAI_HPA); - assert(edata_state_get(edata) == extent_state_active); - assert(edata_arena_ind_get(edata) == shard->ind); - assert(edata_szind_get_maybe_invalid(edata) == - SC_NSIZES); - assert(!edata_slab_get(edata)); - assert(edata_committed_get(edata)); - assert(edata_base_get(edata) == edata_addr_get(edata)); - assert(edata_base_get(edata) != NULL); - } - } - return nsuccess; -} - -static edata_t * -hpa_alloc(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment, bool zero, - bool guarded, bool frequent_reuse, bool *deferred_work_generated) { - assert((size & PAGE_MASK) == 0); - assert(!guarded); - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - - /* We don't handle alignment or zeroing for now. */ - if (alignment > PAGE || zero) { - return NULL; - } - /* - * An alloc with alignment == PAGE and zero == false is equivalent to a - * batch alloc of 1. Just do that, so we can share code. - */ - edata_list_active_t results; - edata_list_active_init(&results); - size_t nallocs = hpa_alloc_batch(tsdn, self, size, /* nallocs */ 1, - &results, deferred_work_generated); - assert(nallocs == 0 || nallocs == 1); - edata_t *edata = edata_list_active_first(&results); - return edata; -} - -static bool -hpa_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size, - size_t new_size, bool zero, bool *deferred_work_generated) { - /* Expand not yet supported. */ - return true; -} - -static bool -hpa_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool *deferred_work_generated) { - /* Shrink not yet supported. */ - return true; -} - -static void -hpa_dalloc_prepare_unlocked(tsdn_t *tsdn, hpa_shard_t *shard, edata_t *edata) { - malloc_mutex_assert_not_owner(tsdn, &shard->mtx); - - assert(edata_pai_get(edata) == EXTENT_PAI_HPA); - assert(edata_state_get(edata) == extent_state_active); - assert(edata_arena_ind_get(edata) == shard->ind); - assert(edata_szind_get_maybe_invalid(edata) == SC_NSIZES); - assert(edata_committed_get(edata)); - assert(edata_base_get(edata) != NULL); - - /* - * Another thread shouldn't be trying to touch the metadata of an - * allocation being freed. The one exception is a merge attempt from a - * lower-addressed PAC extent; in this case we have a nominal race on - * the edata metadata bits, but in practice the fact that the PAI bits - * are different will prevent any further access. The race is bad, but - * benign in practice, and the long term plan is to track enough state - * in the rtree to prevent these merge attempts in the first place. - */ - edata_addr_set(edata, edata_base_get(edata)); - edata_zeroed_set(edata, false); - emap_deregister_boundary(tsdn, shard->emap, edata); -} - -static void -hpa_dalloc_locked(tsdn_t *tsdn, hpa_shard_t *shard, edata_t *edata) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - - /* - * Release the metadata early, to avoid having to remember to do it - * while we're also doing tricky purging logic. First, we need to grab - * a few bits of metadata from it. - * - * Note that the shard mutex protects ps's metadata too; it wouldn't be - * correct to try to read most information out of it without the lock. - */ - hpdata_t *ps = edata_ps_get(edata); - /* Currently, all edatas come from pageslabs. */ - assert(ps != NULL); - void *unreserve_addr = edata_addr_get(edata); - size_t unreserve_size = edata_size_get(edata); - edata_cache_fast_put(tsdn, &shard->ecf, edata); - - psset_update_begin(&shard->psset, ps); - hpdata_unreserve(ps, unreserve_addr, unreserve_size); - hpa_update_purge_hugify_eligibility(tsdn, shard, ps); - psset_update_end(&shard->psset, ps); -} - -static void -hpa_dalloc_batch(tsdn_t *tsdn, pai_t *self, edata_list_active_t *list, - bool *deferred_work_generated) { - hpa_shard_t *shard = hpa_from_pai(self); - - edata_t *edata; - ql_foreach(edata, &list->head, ql_link_active) { - hpa_dalloc_prepare_unlocked(tsdn, shard, edata); - } - - malloc_mutex_lock(tsdn, &shard->mtx); - /* Now, remove from the list. */ - while ((edata = edata_list_active_first(list)) != NULL) { - edata_list_active_remove(list, edata); - hpa_dalloc_locked(tsdn, shard, edata); - } - hpa_shard_maybe_do_deferred_work(tsdn, shard, /* forced */ false); - *deferred_work_generated = - hpa_shard_has_deferred_work(tsdn, shard); - - malloc_mutex_unlock(tsdn, &shard->mtx); -} - -static void -hpa_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata, - bool *deferred_work_generated) { - assert(!edata_guarded_get(edata)); - /* Just a dalloc_batch of size 1; this lets us share logic. */ - edata_list_active_t dalloc_list; - edata_list_active_init(&dalloc_list); - edata_list_active_append(&dalloc_list, edata); - hpa_dalloc_batch(tsdn, self, &dalloc_list, deferred_work_generated); -} - -/* - * Calculate time until either purging or hugification ought to happen. - * Called by background threads. - */ -static uint64_t -hpa_time_until_deferred_work(tsdn_t *tsdn, pai_t *self) { - hpa_shard_t *shard = hpa_from_pai(self); - uint64_t time_ns = BACKGROUND_THREAD_DEFERRED_MAX; - - malloc_mutex_lock(tsdn, &shard->mtx); - - hpdata_t *to_hugify = psset_pick_hugify(&shard->psset); - if (to_hugify != NULL) { - nstime_t time_hugify_allowed = - hpdata_time_hugify_allowed(to_hugify); - uint64_t since_hugify_allowed_ms = - shard->central->hooks.ms_since(&time_hugify_allowed); - /* - * If not enough time has passed since hugification was allowed, - * sleep for the rest. - */ - if (since_hugify_allowed_ms < shard->opts.hugify_delay_ms) { - time_ns = shard->opts.hugify_delay_ms - - since_hugify_allowed_ms; - time_ns *= 1000 * 1000; - } else { - malloc_mutex_unlock(tsdn, &shard->mtx); - return BACKGROUND_THREAD_DEFERRED_MIN; - } - } - - if (hpa_should_purge(tsdn, shard)) { - /* - * If we haven't purged before, no need to check interval - * between purges. Simply purge as soon as possible. - */ - if (shard->stats.npurge_passes == 0) { - malloc_mutex_unlock(tsdn, &shard->mtx); - return BACKGROUND_THREAD_DEFERRED_MIN; - } - uint64_t since_last_purge_ms = shard->central->hooks.ms_since( - &shard->last_purge); - - if (since_last_purge_ms < shard->opts.min_purge_interval_ms) { - uint64_t until_purge_ns; - until_purge_ns = shard->opts.min_purge_interval_ms - - since_last_purge_ms; - until_purge_ns *= 1000 * 1000; - - if (until_purge_ns < time_ns) { - time_ns = until_purge_ns; - } - } else { - time_ns = BACKGROUND_THREAD_DEFERRED_MIN; - } - } - malloc_mutex_unlock(tsdn, &shard->mtx); - return time_ns; -} - -void -hpa_shard_disable(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - - malloc_mutex_lock(tsdn, &shard->mtx); - edata_cache_fast_disable(tsdn, &shard->ecf); - malloc_mutex_unlock(tsdn, &shard->mtx); -} - -static void -hpa_shard_assert_stats_empty(psset_bin_stats_t *bin_stats) { - assert(bin_stats->npageslabs == 0); - assert(bin_stats->nactive == 0); -} - -static void -hpa_assert_empty(tsdn_t *tsdn, hpa_shard_t *shard, psset_t *psset) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - for (int huge = 0; huge <= 1; huge++) { - hpa_shard_assert_stats_empty(&psset->stats.full_slabs[huge]); - for (pszind_t i = 0; i < PSSET_NPSIZES; i++) { - hpa_shard_assert_stats_empty( - &psset->stats.nonfull_slabs[i][huge]); - } - } -} - -void -hpa_shard_destroy(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - /* - * By the time we're here, the arena code should have dalloc'd all the - * active extents, which means we should have eventually evicted - * everything from the psset, so it shouldn't be able to serve even a - * 1-page allocation. - */ - if (config_debug) { - malloc_mutex_lock(tsdn, &shard->mtx); - hpa_assert_empty(tsdn, shard, &shard->psset); - malloc_mutex_unlock(tsdn, &shard->mtx); - } - hpdata_t *ps; - while ((ps = psset_pick_alloc(&shard->psset, PAGE)) != NULL) { - /* There should be no allocations anywhere. */ - assert(hpdata_empty(ps)); - psset_remove(&shard->psset, ps); - shard->central->hooks.unmap(hpdata_addr_get(ps), HUGEPAGE); - } -} - -void -hpa_shard_set_deferral_allowed(tsdn_t *tsdn, hpa_shard_t *shard, - bool deferral_allowed) { - hpa_do_consistency_checks(shard); - - malloc_mutex_lock(tsdn, &shard->mtx); - bool deferral_previously_allowed = shard->opts.deferral_allowed; - shard->opts.deferral_allowed = deferral_allowed; - if (deferral_previously_allowed && !deferral_allowed) { - hpa_shard_maybe_do_deferred_work(tsdn, shard, - /* forced */ true); - } - malloc_mutex_unlock(tsdn, &shard->mtx); -} - -void -hpa_shard_do_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - - malloc_mutex_lock(tsdn, &shard->mtx); - hpa_shard_maybe_do_deferred_work(tsdn, shard, /* forced */ true); - malloc_mutex_unlock(tsdn, &shard->mtx); -} - -void -hpa_shard_prefork3(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - - malloc_mutex_prefork(tsdn, &shard->grow_mtx); -} - -void -hpa_shard_prefork4(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - - malloc_mutex_prefork(tsdn, &shard->mtx); -} - -void -hpa_shard_postfork_parent(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - - malloc_mutex_postfork_parent(tsdn, &shard->grow_mtx); - malloc_mutex_postfork_parent(tsdn, &shard->mtx); -} - -void -hpa_shard_postfork_child(tsdn_t *tsdn, hpa_shard_t *shard) { - hpa_do_consistency_checks(shard); - - malloc_mutex_postfork_child(tsdn, &shard->grow_mtx); - malloc_mutex_postfork_child(tsdn, &shard->mtx); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/hpa_hooks.c b/fluent-bit/lib/jemalloc-5.3.0/src/hpa_hooks.c deleted file mode 100644 index ade581e8..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/hpa_hooks.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/hpa_hooks.h" - -static void *hpa_hooks_map(size_t size); -static void hpa_hooks_unmap(void *ptr, size_t size); -static void hpa_hooks_purge(void *ptr, size_t size); -static void hpa_hooks_hugify(void *ptr, size_t size); -static void hpa_hooks_dehugify(void *ptr, size_t size); -static void hpa_hooks_curtime(nstime_t *r_nstime, bool first_reading); -static uint64_t hpa_hooks_ms_since(nstime_t *past_nstime); - -hpa_hooks_t hpa_hooks_default = { - &hpa_hooks_map, - &hpa_hooks_unmap, - &hpa_hooks_purge, - &hpa_hooks_hugify, - &hpa_hooks_dehugify, - &hpa_hooks_curtime, - &hpa_hooks_ms_since -}; - -static void * -hpa_hooks_map(size_t size) { - bool commit = true; - return pages_map(NULL, size, HUGEPAGE, &commit); -} - -static void -hpa_hooks_unmap(void *ptr, size_t size) { - pages_unmap(ptr, size); -} - -static void -hpa_hooks_purge(void *ptr, size_t size) { - pages_purge_forced(ptr, size); -} - -static void -hpa_hooks_hugify(void *ptr, size_t size) { - bool err = pages_huge(ptr, size); - (void)err; -} - -static void -hpa_hooks_dehugify(void *ptr, size_t size) { - bool err = pages_nohuge(ptr, size); - (void)err; -} - -static void -hpa_hooks_curtime(nstime_t *r_nstime, bool first_reading) { - if (first_reading) { - nstime_init_zero(r_nstime); - } - nstime_update(r_nstime); -} - -static uint64_t -hpa_hooks_ms_since(nstime_t *past_nstime) { - return nstime_ns_since(past_nstime) / 1000 / 1000; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/hpdata.c b/fluent-bit/lib/jemalloc-5.3.0/src/hpdata.c deleted file mode 100644 index e7d7294c..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/hpdata.c +++ /dev/null @@ -1,325 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/hpdata.h" - -static int -hpdata_age_comp(const hpdata_t *a, const hpdata_t *b) { - uint64_t a_age = hpdata_age_get(a); - uint64_t b_age = hpdata_age_get(b); - /* - * hpdata ages are operation counts in the psset; no two should be the - * same. - */ - assert(a_age != b_age); - return (a_age > b_age) - (a_age < b_age); -} - -ph_gen(, hpdata_age_heap, hpdata_t, age_link, hpdata_age_comp) - -void -hpdata_init(hpdata_t *hpdata, void *addr, uint64_t age) { - hpdata_addr_set(hpdata, addr); - hpdata_age_set(hpdata, age); - hpdata->h_huge = false; - hpdata->h_alloc_allowed = true; - hpdata->h_in_psset_alloc_container = false; - hpdata->h_purge_allowed = false; - hpdata->h_hugify_allowed = false; - hpdata->h_in_psset_hugify_container = false; - hpdata->h_mid_purge = false; - hpdata->h_mid_hugify = false; - hpdata->h_updating = false; - hpdata->h_in_psset = false; - hpdata_longest_free_range_set(hpdata, HUGEPAGE_PAGES); - hpdata->h_nactive = 0; - fb_init(hpdata->active_pages, HUGEPAGE_PAGES); - hpdata->h_ntouched = 0; - fb_init(hpdata->touched_pages, HUGEPAGE_PAGES); - - hpdata_assert_consistent(hpdata); -} - -void * -hpdata_reserve_alloc(hpdata_t *hpdata, size_t sz) { - hpdata_assert_consistent(hpdata); - /* - * This is a metadata change; the hpdata should therefore either not be - * in the psset, or should have explicitly marked itself as being - * mid-update. - */ - assert(!hpdata->h_in_psset || hpdata->h_updating); - assert(hpdata->h_alloc_allowed); - assert((sz & PAGE_MASK) == 0); - size_t npages = sz >> LG_PAGE; - assert(npages <= hpdata_longest_free_range_get(hpdata)); - - size_t result; - - size_t start = 0; - /* - * These are dead stores, but the compiler will issue warnings on them - * since it can't tell statically that found is always true below. - */ - size_t begin = 0; - size_t len = 0; - - size_t largest_unchosen_range = 0; - while (true) { - bool found = fb_urange_iter(hpdata->active_pages, - HUGEPAGE_PAGES, start, &begin, &len); - /* - * A precondition to this function is that hpdata must be able - * to serve the allocation. - */ - assert(found); - assert(len <= hpdata_longest_free_range_get(hpdata)); - if (len >= npages) { - /* - * We use first-fit within the page slabs; this gives - * bounded worst-case fragmentation within a slab. It's - * not necessarily right; we could experiment with - * various other options. - */ - break; - } - if (len > largest_unchosen_range) { - largest_unchosen_range = len; - } - start = begin + len; - } - /* We found a range; remember it. */ - result = begin; - fb_set_range(hpdata->active_pages, HUGEPAGE_PAGES, begin, npages); - hpdata->h_nactive += npages; - - /* - * We might be about to dirty some memory for the first time; update our - * count if so. - */ - size_t new_dirty = fb_ucount(hpdata->touched_pages, HUGEPAGE_PAGES, - result, npages); - fb_set_range(hpdata->touched_pages, HUGEPAGE_PAGES, result, npages); - hpdata->h_ntouched += new_dirty; - - /* - * If we allocated out of a range that was the longest in the hpdata, it - * might be the only one of that size and we'll have to adjust the - * metadata. - */ - if (len == hpdata_longest_free_range_get(hpdata)) { - start = begin + npages; - while (start < HUGEPAGE_PAGES) { - bool found = fb_urange_iter(hpdata->active_pages, - HUGEPAGE_PAGES, start, &begin, &len); - if (!found) { - break; - } - assert(len <= hpdata_longest_free_range_get(hpdata)); - if (len == hpdata_longest_free_range_get(hpdata)) { - largest_unchosen_range = len; - break; - } - if (len > largest_unchosen_range) { - largest_unchosen_range = len; - } - start = begin + len; - } - hpdata_longest_free_range_set(hpdata, largest_unchosen_range); - } - - hpdata_assert_consistent(hpdata); - return (void *)( - (uintptr_t)hpdata_addr_get(hpdata) + (result << LG_PAGE)); -} - -void -hpdata_unreserve(hpdata_t *hpdata, void *addr, size_t sz) { - hpdata_assert_consistent(hpdata); - /* See the comment in reserve. */ - assert(!hpdata->h_in_psset || hpdata->h_updating); - assert(((uintptr_t)addr & PAGE_MASK) == 0); - assert((sz & PAGE_MASK) == 0); - size_t begin = ((uintptr_t)addr - (uintptr_t)hpdata_addr_get(hpdata)) - >> LG_PAGE; - assert(begin < HUGEPAGE_PAGES); - size_t npages = sz >> LG_PAGE; - size_t old_longest_range = hpdata_longest_free_range_get(hpdata); - - fb_unset_range(hpdata->active_pages, HUGEPAGE_PAGES, begin, npages); - /* We might have just created a new, larger range. */ - size_t new_begin = (fb_fls(hpdata->active_pages, HUGEPAGE_PAGES, - begin) + 1); - size_t new_end = fb_ffs(hpdata->active_pages, HUGEPAGE_PAGES, - begin + npages - 1); - size_t new_range_len = new_end - new_begin; - - if (new_range_len > old_longest_range) { - hpdata_longest_free_range_set(hpdata, new_range_len); - } - - hpdata->h_nactive -= npages; - - hpdata_assert_consistent(hpdata); -} - -size_t -hpdata_purge_begin(hpdata_t *hpdata, hpdata_purge_state_t *purge_state) { - hpdata_assert_consistent(hpdata); - /* - * See the comment below; we might purge any inactive extent, so it's - * unsafe for any other thread to turn any inactive extent active while - * we're operating on it. - */ - assert(!hpdata_alloc_allowed_get(hpdata)); - - purge_state->npurged = 0; - purge_state->next_purge_search_begin = 0; - - /* - * Initialize to_purge. - * - * It's possible to end up in situations where two dirty extents are - * separated by a retained extent: - * - 1 page allocated. - * - 1 page allocated. - * - 1 pages allocated. - * - * If the middle page is freed and purged, and then the first and third - * pages are freed, and then another purge pass happens, the hpdata - * looks like this: - * - 1 page dirty. - * - 1 page retained. - * - 1 page dirty. - * - * But it's safe to do a single 3-page purge. - * - * We do this by first computing the dirty pages, and then filling in - * any gaps by extending each range in the dirty bitmap to extend until - * the next active page. This purges more pages, but the expensive part - * of purging is the TLB shootdowns, rather than the kernel state - * tracking; doing a little bit more of the latter is fine if it saves - * us from doing some of the former. - */ - - /* - * The dirty pages are those that are touched but not active. Note that - * in a normal-ish case, HUGEPAGE_PAGES is something like 512 and the - * fb_group_t is 64 bits, so this is 64 bytes, spread across 8 - * fb_group_ts. - */ - fb_group_t dirty_pages[FB_NGROUPS(HUGEPAGE_PAGES)]; - fb_init(dirty_pages, HUGEPAGE_PAGES); - fb_bit_not(dirty_pages, hpdata->active_pages, HUGEPAGE_PAGES); - fb_bit_and(dirty_pages, dirty_pages, hpdata->touched_pages, - HUGEPAGE_PAGES); - - fb_init(purge_state->to_purge, HUGEPAGE_PAGES); - size_t next_bit = 0; - while (next_bit < HUGEPAGE_PAGES) { - size_t next_dirty = fb_ffs(dirty_pages, HUGEPAGE_PAGES, - next_bit); - /* Recall that fb_ffs returns nbits if no set bit is found. */ - if (next_dirty == HUGEPAGE_PAGES) { - break; - } - size_t next_active = fb_ffs(hpdata->active_pages, - HUGEPAGE_PAGES, next_dirty); - /* - * Don't purge past the end of the dirty extent, into retained - * pages. This helps the kernel a tiny bit, but honestly it's - * mostly helpful for testing (where we tend to write test cases - * that think in terms of the dirty ranges). - */ - ssize_t last_dirty = fb_fls(dirty_pages, HUGEPAGE_PAGES, - next_active - 1); - assert(last_dirty >= 0); - assert((size_t)last_dirty >= next_dirty); - assert((size_t)last_dirty - next_dirty + 1 <= HUGEPAGE_PAGES); - - fb_set_range(purge_state->to_purge, HUGEPAGE_PAGES, next_dirty, - last_dirty - next_dirty + 1); - next_bit = next_active + 1; - } - - /* We should purge, at least, everything dirty. */ - size_t ndirty = hpdata->h_ntouched - hpdata->h_nactive; - purge_state->ndirty_to_purge = ndirty; - assert(ndirty <= fb_scount( - purge_state->to_purge, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES)); - assert(ndirty == fb_scount(dirty_pages, HUGEPAGE_PAGES, 0, - HUGEPAGE_PAGES)); - - hpdata_assert_consistent(hpdata); - - return ndirty; -} - -bool -hpdata_purge_next(hpdata_t *hpdata, hpdata_purge_state_t *purge_state, - void **r_purge_addr, size_t *r_purge_size) { - /* - * Note that we don't have a consistency check here; we're accessing - * hpdata without synchronization, and therefore have no right to expect - * a consistent state. - */ - assert(!hpdata_alloc_allowed_get(hpdata)); - - if (purge_state->next_purge_search_begin == HUGEPAGE_PAGES) { - return false; - } - size_t purge_begin; - size_t purge_len; - bool found_range = fb_srange_iter(purge_state->to_purge, HUGEPAGE_PAGES, - purge_state->next_purge_search_begin, &purge_begin, &purge_len); - if (!found_range) { - return false; - } - - *r_purge_addr = (void *)( - (uintptr_t)hpdata_addr_get(hpdata) + purge_begin * PAGE); - *r_purge_size = purge_len * PAGE; - - purge_state->next_purge_search_begin = purge_begin + purge_len; - purge_state->npurged += purge_len; - assert(purge_state->npurged <= HUGEPAGE_PAGES); - - return true; -} - -void -hpdata_purge_end(hpdata_t *hpdata, hpdata_purge_state_t *purge_state) { - assert(!hpdata_alloc_allowed_get(hpdata)); - hpdata_assert_consistent(hpdata); - /* See the comment in reserve. */ - assert(!hpdata->h_in_psset || hpdata->h_updating); - - assert(purge_state->npurged == fb_scount(purge_state->to_purge, - HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES)); - assert(purge_state->npurged >= purge_state->ndirty_to_purge); - - fb_bit_not(purge_state->to_purge, purge_state->to_purge, - HUGEPAGE_PAGES); - fb_bit_and(hpdata->touched_pages, hpdata->touched_pages, - purge_state->to_purge, HUGEPAGE_PAGES); - assert(hpdata->h_ntouched >= purge_state->ndirty_to_purge); - hpdata->h_ntouched -= purge_state->ndirty_to_purge; - - hpdata_assert_consistent(hpdata); -} - -void -hpdata_hugify(hpdata_t *hpdata) { - hpdata_assert_consistent(hpdata); - hpdata->h_huge = true; - fb_set_range(hpdata->touched_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES); - hpdata->h_ntouched = HUGEPAGE_PAGES; - hpdata_assert_consistent(hpdata); -} - -void -hpdata_dehugify(hpdata_t *hpdata) { - hpdata_assert_consistent(hpdata); - hpdata->h_huge = false; - hpdata_assert_consistent(hpdata); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/inspect.c b/fluent-bit/lib/jemalloc-5.3.0/src/inspect.c deleted file mode 100644 index 911b5d52..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/inspect.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -void -inspect_extent_util_stats_get(tsdn_t *tsdn, const void *ptr, size_t *nfree, - size_t *nregs, size_t *size) { - assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL); - - const edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - if (unlikely(edata == NULL)) { - *nfree = *nregs = *size = 0; - return; - } - - *size = edata_size_get(edata); - if (!edata_slab_get(edata)) { - *nfree = 0; - *nregs = 1; - } else { - *nfree = edata_nfree_get(edata); - *nregs = bin_infos[edata_szind_get(edata)].nregs; - assert(*nfree <= *nregs); - assert(*nfree * edata_usize_get(edata) <= *size); - } -} - -void -inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr, - size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree, - size_t *bin_nregs, void **slabcur_addr) { - assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL - && bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL); - - const edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - if (unlikely(edata == NULL)) { - *nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0; - *slabcur_addr = NULL; - return; - } - - *size = edata_size_get(edata); - if (!edata_slab_get(edata)) { - *nfree = *bin_nfree = *bin_nregs = 0; - *nregs = 1; - *slabcur_addr = NULL; - return; - } - - *nfree = edata_nfree_get(edata); - const szind_t szind = edata_szind_get(edata); - *nregs = bin_infos[szind].nregs; - assert(*nfree <= *nregs); - assert(*nfree * edata_usize_get(edata) <= *size); - - arena_t *arena = (arena_t *)atomic_load_p( - &arenas[edata_arena_ind_get(edata)], ATOMIC_RELAXED); - assert(arena != NULL); - const unsigned binshard = edata_binshard_get(edata); - bin_t *bin = arena_get_bin(arena, szind, binshard); - - malloc_mutex_lock(tsdn, &bin->lock); - if (config_stats) { - *bin_nregs = *nregs * bin->stats.curslabs; - assert(*bin_nregs >= bin->stats.curregs); - *bin_nfree = *bin_nregs - bin->stats.curregs; - } else { - *bin_nfree = *bin_nregs = 0; - } - edata_t *slab; - if (bin->slabcur != NULL) { - slab = bin->slabcur; - } else { - slab = edata_heap_first(&bin->slabs_nonfull); - } - *slabcur_addr = slab != NULL ? edata_addr_get(slab) : NULL; - malloc_mutex_unlock(tsdn, &bin->lock); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/jemalloc.c b/fluent-bit/lib/jemalloc-5.3.0/src/jemalloc.c deleted file mode 100644 index 7655de4e..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/jemalloc.c +++ /dev/null @@ -1,4476 +0,0 @@ -#define JEMALLOC_C_ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/atomic.h" -#include "jemalloc/internal/buf_writer.h" -#include "jemalloc/internal/ctl.h" -#include "jemalloc/internal/emap.h" -#include "jemalloc/internal/extent_dss.h" -#include "jemalloc/internal/extent_mmap.h" -#include "jemalloc/internal/fxp.h" -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/hook.h" -#include "jemalloc/internal/jemalloc_internal_types.h" -#include "jemalloc/internal/log.h" -#include "jemalloc/internal/malloc_io.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/nstime.h" -#include "jemalloc/internal/rtree.h" -#include "jemalloc/internal/safety_check.h" -#include "jemalloc/internal/sc.h" -#include "jemalloc/internal/spin.h" -#include "jemalloc/internal/sz.h" -#include "jemalloc/internal/ticker.h" -#include "jemalloc/internal/thread_event.h" -#include "jemalloc/internal/util.h" - -/******************************************************************************/ -/* Data. */ - -/* Runtime configuration options. */ -const char *je_malloc_conf -#ifndef _WIN32 - JEMALLOC_ATTR(weak) -#endif - ; -/* - * The usual rule is that the closer to runtime you are, the higher priority - * your configuration settings are (so the jemalloc config options get lower - * priority than the per-binary setting, which gets lower priority than the /etc - * setting, which gets lower priority than the environment settings). - * - * But it's a fairly common use case in some testing environments for a user to - * be able to control the binary, but nothing else (e.g. a performancy canary - * uses the production OS and environment variables, but can run any binary in - * those circumstances). For these use cases, it's handy to have an in-binary - * mechanism for overriding environment variable settings, with the idea that if - * the results are positive they get promoted to the official settings, and - * moved from the binary to the environment variable. - * - * We don't actually want this to be widespread, so we'll give it a silly name - * and not mention it in headers or documentation. - */ -const char *je_malloc_conf_2_conf_harder -#ifndef _WIN32 - JEMALLOC_ATTR(weak) -#endif - ; - -bool opt_abort = -#ifdef JEMALLOC_DEBUG - true -#else - false -#endif - ; -bool opt_abort_conf = -#ifdef JEMALLOC_DEBUG - true -#else - false -#endif - ; -/* Intentionally default off, even with debug builds. */ -bool opt_confirm_conf = false; -const char *opt_junk = -#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL)) - "true" -#else - "false" -#endif - ; -bool opt_junk_alloc = -#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL)) - true -#else - false -#endif - ; -bool opt_junk_free = -#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL)) - true -#else - false -#endif - ; -bool opt_trust_madvise = -#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS - false -#else - true -#endif - ; - -bool opt_cache_oblivious = -#ifdef JEMALLOC_CACHE_OBLIVIOUS - true -#else - false -#endif - ; - -zero_realloc_action_t opt_zero_realloc_action = -#ifdef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE - zero_realloc_action_free -#else - zero_realloc_action_alloc -#endif - ; - -atomic_zu_t zero_realloc_count = ATOMIC_INIT(0); - -const char *zero_realloc_mode_names[] = { - "alloc", - "free", - "abort", -}; - -/* - * These are the documented values for junk fill debugging facilities -- see the - * man page. - */ -static const uint8_t junk_alloc_byte = 0xa5; -static const uint8_t junk_free_byte = 0x5a; - -static void default_junk_alloc(void *ptr, size_t usize) { - memset(ptr, junk_alloc_byte, usize); -} - -static void default_junk_free(void *ptr, size_t usize) { - memset(ptr, junk_free_byte, usize); -} - -void (*junk_alloc_callback)(void *ptr, size_t size) = &default_junk_alloc; -void (*junk_free_callback)(void *ptr, size_t size) = &default_junk_free; - -bool opt_utrace = false; -bool opt_xmalloc = false; -bool opt_experimental_infallible_new = false; -bool opt_zero = false; -unsigned opt_narenas = 0; -fxp_t opt_narenas_ratio = FXP_INIT_INT(4); - -unsigned ncpus; - -/* Protects arenas initialization. */ -malloc_mutex_t arenas_lock; - -/* The global hpa, and whether it's on. */ -bool opt_hpa = false; -hpa_shard_opts_t opt_hpa_opts = HPA_SHARD_OPTS_DEFAULT; -sec_opts_t opt_hpa_sec_opts = SEC_OPTS_DEFAULT; - -/* - * Arenas that are used to service external requests. Not all elements of the - * arenas array are necessarily used; arenas are created lazily as needed. - * - * arenas[0..narenas_auto) are used for automatic multiplexing of threads and - * arenas. arenas[narenas_auto..narenas_total) are only used if the application - * takes some action to create them and allocate from them. - * - * Points to an arena_t. - */ -JEMALLOC_ALIGNED(CACHELINE) -atomic_p_t arenas[MALLOCX_ARENA_LIMIT]; -static atomic_u_t narenas_total; /* Use narenas_total_*(). */ -/* Below three are read-only after initialization. */ -static arena_t *a0; /* arenas[0]. */ -unsigned narenas_auto; -unsigned manual_arena_base; - -malloc_init_t malloc_init_state = malloc_init_uninitialized; - -/* False should be the common case. Set to true to trigger initialization. */ -bool malloc_slow = true; - -/* When malloc_slow is true, set the corresponding bits for sanity check. */ -enum { - flag_opt_junk_alloc = (1U), - flag_opt_junk_free = (1U << 1), - flag_opt_zero = (1U << 2), - flag_opt_utrace = (1U << 3), - flag_opt_xmalloc = (1U << 4) -}; -static uint8_t malloc_slow_flags; - -#ifdef JEMALLOC_THREADED_INIT -/* Used to let the initializing thread recursively allocate. */ -# define NO_INITIALIZER ((unsigned long)0) -# define INITIALIZER pthread_self() -# define IS_INITIALIZER (malloc_initializer == pthread_self()) -static pthread_t malloc_initializer = NO_INITIALIZER; -#else -# define NO_INITIALIZER false -# define INITIALIZER true -# define IS_INITIALIZER malloc_initializer -static bool malloc_initializer = NO_INITIALIZER; -#endif - -/* Used to avoid initialization races. */ -#ifdef _WIN32 -#if _WIN32_WINNT >= 0x0600 -static malloc_mutex_t init_lock = SRWLOCK_INIT; -#else -static malloc_mutex_t init_lock; -static bool init_lock_initialized = false; - -JEMALLOC_ATTR(constructor) -static void WINAPI -_init_init_lock(void) { - /* - * If another constructor in the same binary is using mallctl to e.g. - * set up extent hooks, it may end up running before this one, and - * malloc_init_hard will crash trying to lock the uninitialized lock. So - * we force an initialization of the lock in malloc_init_hard as well. - * We don't try to care about atomicity of the accessed to the - * init_lock_initialized boolean, since it really only matters early in - * the process creation, before any separate thread normally starts - * doing anything. - */ - if (!init_lock_initialized) { - malloc_mutex_init(&init_lock, "init", WITNESS_RANK_INIT, - malloc_mutex_rank_exclusive); - } - init_lock_initialized = true; -} - -#ifdef _MSC_VER -# pragma section(".CRT$XCU", read) -JEMALLOC_SECTION(".CRT$XCU") JEMALLOC_ATTR(used) -static const void (WINAPI *init_init_lock)(void) = _init_init_lock; -#endif -#endif -#else -static malloc_mutex_t init_lock = MALLOC_MUTEX_INITIALIZER; -#endif - -typedef struct { - void *p; /* Input pointer (as in realloc(p, s)). */ - size_t s; /* Request size. */ - void *r; /* Result pointer. */ -} malloc_utrace_t; - -#ifdef JEMALLOC_UTRACE -# define UTRACE(a, b, c) do { \ - if (unlikely(opt_utrace)) { \ - int utrace_serrno = errno; \ - malloc_utrace_t ut; \ - ut.p = (a); \ - ut.s = (b); \ - ut.r = (c); \ - UTRACE_CALL(&ut, sizeof(ut)); \ - errno = utrace_serrno; \ - } \ -} while (0) -#else -# define UTRACE(a, b, c) -#endif - -/* Whether encountered any invalid config options. */ -static bool had_conf_error = false; - -/******************************************************************************/ -/* - * Function prototypes for static functions that are referenced prior to - * definition. - */ - -static bool malloc_init_hard_a0(void); -static bool malloc_init_hard(void); - -/******************************************************************************/ -/* - * Begin miscellaneous support functions. - */ - -JEMALLOC_ALWAYS_INLINE bool -malloc_init_a0(void) { - if (unlikely(malloc_init_state == malloc_init_uninitialized)) { - return malloc_init_hard_a0(); - } - return false; -} - -JEMALLOC_ALWAYS_INLINE bool -malloc_init(void) { - if (unlikely(!malloc_initialized()) && malloc_init_hard()) { - return true; - } - return false; -} - -/* - * The a0*() functions are used instead of i{d,}alloc() in situations that - * cannot tolerate TLS variable access. - */ - -static void * -a0ialloc(size_t size, bool zero, bool is_internal) { - if (unlikely(malloc_init_a0())) { - return NULL; - } - - return iallocztm(TSDN_NULL, size, sz_size2index(size), zero, NULL, - is_internal, arena_get(TSDN_NULL, 0, true), true); -} - -static void -a0idalloc(void *ptr, bool is_internal) { - idalloctm(TSDN_NULL, ptr, NULL, NULL, is_internal, true); -} - -void * -a0malloc(size_t size) { - return a0ialloc(size, false, true); -} - -void -a0dalloc(void *ptr) { - a0idalloc(ptr, true); -} - -/* - * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-sensitive - * situations that cannot tolerate TLS variable access (TLS allocation and very - * early internal data structure initialization). - */ - -void * -bootstrap_malloc(size_t size) { - if (unlikely(size == 0)) { - size = 1; - } - - return a0ialloc(size, false, false); -} - -void * -bootstrap_calloc(size_t num, size_t size) { - size_t num_size; - - num_size = num * size; - if (unlikely(num_size == 0)) { - assert(num == 0 || size == 0); - num_size = 1; - } - - return a0ialloc(num_size, true, false); -} - -void -bootstrap_free(void *ptr) { - if (unlikely(ptr == NULL)) { - return; - } - - a0idalloc(ptr, false); -} - -void -arena_set(unsigned ind, arena_t *arena) { - atomic_store_p(&arenas[ind], arena, ATOMIC_RELEASE); -} - -static void -narenas_total_set(unsigned narenas) { - atomic_store_u(&narenas_total, narenas, ATOMIC_RELEASE); -} - -static void -narenas_total_inc(void) { - atomic_fetch_add_u(&narenas_total, 1, ATOMIC_RELEASE); -} - -unsigned -narenas_total_get(void) { - return atomic_load_u(&narenas_total, ATOMIC_ACQUIRE); -} - -/* Create a new arena and insert it into the arenas array at index ind. */ -static arena_t * -arena_init_locked(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) { - arena_t *arena; - - assert(ind <= narenas_total_get()); - if (ind >= MALLOCX_ARENA_LIMIT) { - return NULL; - } - if (ind == narenas_total_get()) { - narenas_total_inc(); - } - - /* - * Another thread may have already initialized arenas[ind] if it's an - * auto arena. - */ - arena = arena_get(tsdn, ind, false); - if (arena != NULL) { - assert(arena_is_auto(arena)); - return arena; - } - - /* Actually initialize the arena. */ - arena = arena_new(tsdn, ind, config); - - return arena; -} - -static void -arena_new_create_background_thread(tsdn_t *tsdn, unsigned ind) { - if (ind == 0) { - return; - } - /* - * Avoid creating a new background thread just for the huge arena, which - * purges eagerly by default. - */ - if (have_background_thread && !arena_is_huge(ind)) { - if (background_thread_create(tsdn_tsd(tsdn), ind)) { - malloc_printf("<jemalloc>: error in background thread " - "creation for arena %u. Abort.\n", ind); - abort(); - } - } -} - -arena_t * -arena_init(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) { - arena_t *arena; - - malloc_mutex_lock(tsdn, &arenas_lock); - arena = arena_init_locked(tsdn, ind, config); - malloc_mutex_unlock(tsdn, &arenas_lock); - - arena_new_create_background_thread(tsdn, ind); - - return arena; -} - -static void -arena_bind(tsd_t *tsd, unsigned ind, bool internal) { - arena_t *arena = arena_get(tsd_tsdn(tsd), ind, false); - arena_nthreads_inc(arena, internal); - - if (internal) { - tsd_iarena_set(tsd, arena); - } else { - tsd_arena_set(tsd, arena); - unsigned shard = atomic_fetch_add_u(&arena->binshard_next, 1, - ATOMIC_RELAXED); - tsd_binshards_t *bins = tsd_binshardsp_get(tsd); - for (unsigned i = 0; i < SC_NBINS; i++) { - assert(bin_infos[i].n_shards > 0 && - bin_infos[i].n_shards <= BIN_SHARDS_MAX); - bins->binshard[i] = shard % bin_infos[i].n_shards; - } - } -} - -void -arena_migrate(tsd_t *tsd, arena_t *oldarena, arena_t *newarena) { - assert(oldarena != NULL); - assert(newarena != NULL); - - arena_nthreads_dec(oldarena, false); - arena_nthreads_inc(newarena, false); - tsd_arena_set(tsd, newarena); - - if (arena_nthreads_get(oldarena, false) == 0) { - /* Purge if the old arena has no associated threads anymore. */ - arena_decay(tsd_tsdn(tsd), oldarena, - /* is_background_thread */ false, /* all */ true); - } -} - -static void -arena_unbind(tsd_t *tsd, unsigned ind, bool internal) { - arena_t *arena; - - arena = arena_get(tsd_tsdn(tsd), ind, false); - arena_nthreads_dec(arena, internal); - - if (internal) { - tsd_iarena_set(tsd, NULL); - } else { - tsd_arena_set(tsd, NULL); - } -} - -/* Slow path, called only by arena_choose(). */ -arena_t * -arena_choose_hard(tsd_t *tsd, bool internal) { - arena_t *ret JEMALLOC_CC_SILENCE_INIT(NULL); - - if (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena)) { - unsigned choose = percpu_arena_choose(); - ret = arena_get(tsd_tsdn(tsd), choose, true); - assert(ret != NULL); - arena_bind(tsd, arena_ind_get(ret), false); - arena_bind(tsd, arena_ind_get(ret), true); - - return ret; - } - - if (narenas_auto > 1) { - unsigned i, j, choose[2], first_null; - bool is_new_arena[2]; - - /* - * Determine binding for both non-internal and internal - * allocation. - * - * choose[0]: For application allocation. - * choose[1]: For internal metadata allocation. - */ - - for (j = 0; j < 2; j++) { - choose[j] = 0; - is_new_arena[j] = false; - } - - first_null = narenas_auto; - malloc_mutex_lock(tsd_tsdn(tsd), &arenas_lock); - assert(arena_get(tsd_tsdn(tsd), 0, false) != NULL); - for (i = 1; i < narenas_auto; i++) { - if (arena_get(tsd_tsdn(tsd), i, false) != NULL) { - /* - * Choose the first arena that has the lowest - * number of threads assigned to it. - */ - for (j = 0; j < 2; j++) { - if (arena_nthreads_get(arena_get( - tsd_tsdn(tsd), i, false), !!j) < - arena_nthreads_get(arena_get( - tsd_tsdn(tsd), choose[j], false), - !!j)) { - choose[j] = i; - } - } - } else if (first_null == narenas_auto) { - /* - * Record the index of the first uninitialized - * arena, in case all extant arenas are in use. - * - * NB: It is possible for there to be - * discontinuities in terms of initialized - * versus uninitialized arenas, due to the - * "thread.arena" mallctl. - */ - first_null = i; - } - } - - for (j = 0; j < 2; j++) { - if (arena_nthreads_get(arena_get(tsd_tsdn(tsd), - choose[j], false), !!j) == 0 || first_null == - narenas_auto) { - /* - * Use an unloaded arena, or the least loaded - * arena if all arenas are already initialized. - */ - if (!!j == internal) { - ret = arena_get(tsd_tsdn(tsd), - choose[j], false); - } - } else { - arena_t *arena; - - /* Initialize a new arena. */ - choose[j] = first_null; - arena = arena_init_locked(tsd_tsdn(tsd), - choose[j], &arena_config_default); - if (arena == NULL) { - malloc_mutex_unlock(tsd_tsdn(tsd), - &arenas_lock); - return NULL; - } - is_new_arena[j] = true; - if (!!j == internal) { - ret = arena; - } - } - arena_bind(tsd, choose[j], !!j); - } - malloc_mutex_unlock(tsd_tsdn(tsd), &arenas_lock); - - for (j = 0; j < 2; j++) { - if (is_new_arena[j]) { - assert(choose[j] > 0); - arena_new_create_background_thread( - tsd_tsdn(tsd), choose[j]); - } - } - - } else { - ret = arena_get(tsd_tsdn(tsd), 0, false); - arena_bind(tsd, 0, false); - arena_bind(tsd, 0, true); - } - - return ret; -} - -void -iarena_cleanup(tsd_t *tsd) { - arena_t *iarena; - - iarena = tsd_iarena_get(tsd); - if (iarena != NULL) { - arena_unbind(tsd, arena_ind_get(iarena), true); - } -} - -void -arena_cleanup(tsd_t *tsd) { - arena_t *arena; - - arena = tsd_arena_get(tsd); - if (arena != NULL) { - arena_unbind(tsd, arena_ind_get(arena), false); - } -} - -static void -stats_print_atexit(void) { - if (config_stats) { - tsdn_t *tsdn; - unsigned narenas, i; - - tsdn = tsdn_fetch(); - - /* - * Merge stats from extant threads. This is racy, since - * individual threads do not lock when recording tcache stats - * events. As a consequence, the final stats may be slightly - * out of date by the time they are reported, if other threads - * continue to allocate. - */ - for (i = 0, narenas = narenas_total_get(); i < narenas; i++) { - arena_t *arena = arena_get(tsdn, i, false); - if (arena != NULL) { - tcache_slow_t *tcache_slow; - - malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); - ql_foreach(tcache_slow, &arena->tcache_ql, - link) { - tcache_stats_merge(tsdn, - tcache_slow->tcache, arena); - } - malloc_mutex_unlock(tsdn, - &arena->tcache_ql_mtx); - } - } - } - je_malloc_stats_print(NULL, NULL, opt_stats_print_opts); -} - -/* - * Ensure that we don't hold any locks upon entry to or exit from allocator - * code (in a "broad" sense that doesn't count a reentrant allocation as an - * entrance or exit). - */ -JEMALLOC_ALWAYS_INLINE void -check_entry_exit_locking(tsdn_t *tsdn) { - if (!config_debug) { - return; - } - if (tsdn_null(tsdn)) { - return; - } - tsd_t *tsd = tsdn_tsd(tsdn); - /* - * It's possible we hold locks at entry/exit if we're in a nested - * allocation. - */ - int8_t reentrancy_level = tsd_reentrancy_level_get(tsd); - if (reentrancy_level != 0) { - return; - } - witness_assert_lockless(tsdn_witness_tsdp_get(tsdn)); -} - -/* - * End miscellaneous support functions. - */ -/******************************************************************************/ -/* - * Begin initialization functions. - */ - -static char * -jemalloc_secure_getenv(const char *name) { -#ifdef JEMALLOC_HAVE_SECURE_GETENV - return secure_getenv(name); -#else -# ifdef JEMALLOC_HAVE_ISSETUGID - if (issetugid() != 0) { - return NULL; - } -# endif - return getenv(name); -#endif -} - -static unsigned -malloc_ncpus(void) { - long result; - -#ifdef _WIN32 - SYSTEM_INFO si; - GetSystemInfo(&si); - result = si.dwNumberOfProcessors; -#elif defined(CPU_COUNT) - /* - * glibc >= 2.6 has the CPU_COUNT macro. - * - * glibc's sysconf() uses isspace(). glibc allocates for the first time - * *before* setting up the isspace tables. Therefore we need a - * different method to get the number of CPUs. - * - * The getaffinity approach is also preferred when only a subset of CPUs - * is available, to avoid using more arenas than necessary. - */ - { -# if defined(__FreeBSD__) || defined(__DragonFly__) - cpuset_t set; -# else - cpu_set_t set; -# endif -# if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY) - sched_getaffinity(0, sizeof(set), &set); -# else - pthread_getaffinity_np(pthread_self(), sizeof(set), &set); -# endif - result = CPU_COUNT(&set); - } -#else - result = sysconf(_SC_NPROCESSORS_ONLN); -#endif - return ((result == -1) ? 1 : (unsigned)result); -} - -/* - * Ensure that number of CPUs is determistinc, i.e. it is the same based on: - * - sched_getaffinity() - * - _SC_NPROCESSORS_ONLN - * - _SC_NPROCESSORS_CONF - * Since otherwise tricky things is possible with percpu arenas in use. - */ -static bool -malloc_cpu_count_is_deterministic() -{ -#ifdef _WIN32 - return true; -#else - long cpu_onln = sysconf(_SC_NPROCESSORS_ONLN); - long cpu_conf = sysconf(_SC_NPROCESSORS_CONF); - if (cpu_onln != cpu_conf) { - return false; - } -# if defined(CPU_COUNT) -# if defined(__FreeBSD__) || defined(__DragonFly__) - cpuset_t set; -# else - cpu_set_t set; -# endif /* __FreeBSD__ */ -# if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY) - sched_getaffinity(0, sizeof(set), &set); -# else /* !JEMALLOC_HAVE_SCHED_SETAFFINITY */ - pthread_getaffinity_np(pthread_self(), sizeof(set), &set); -# endif /* JEMALLOC_HAVE_SCHED_SETAFFINITY */ - long cpu_affinity = CPU_COUNT(&set); - if (cpu_affinity != cpu_conf) { - return false; - } -# endif /* CPU_COUNT */ - return true; -#endif -} - -static void -init_opt_stats_opts(const char *v, size_t vlen, char *dest) { - size_t opts_len = strlen(dest); - assert(opts_len <= stats_print_tot_num_options); - - for (size_t i = 0; i < vlen; i++) { - switch (v[i]) { -#define OPTION(o, v, d, s) case o: break; - STATS_PRINT_OPTIONS -#undef OPTION - default: continue; - } - - if (strchr(dest, v[i]) != NULL) { - /* Ignore repeated. */ - continue; - } - - dest[opts_len++] = v[i]; - dest[opts_len] = '\0'; - assert(opts_len <= stats_print_tot_num_options); - } - assert(opts_len == strlen(dest)); -} - -/* Reads the next size pair in a multi-sized option. */ -static bool -malloc_conf_multi_sizes_next(const char **slab_size_segment_cur, - size_t *vlen_left, size_t *slab_start, size_t *slab_end, size_t *new_size) { - const char *cur = *slab_size_segment_cur; - char *end; - uintmax_t um; - - set_errno(0); - - /* First number, then '-' */ - um = malloc_strtoumax(cur, &end, 0); - if (get_errno() != 0 || *end != '-') { - return true; - } - *slab_start = (size_t)um; - cur = end + 1; - - /* Second number, then ':' */ - um = malloc_strtoumax(cur, &end, 0); - if (get_errno() != 0 || *end != ':') { - return true; - } - *slab_end = (size_t)um; - cur = end + 1; - - /* Last number */ - um = malloc_strtoumax(cur, &end, 0); - if (get_errno() != 0) { - return true; - } - *new_size = (size_t)um; - - /* Consume the separator if there is one. */ - if (*end == '|') { - end++; - } - - *vlen_left -= end - *slab_size_segment_cur; - *slab_size_segment_cur = end; - - return false; -} - -static bool -malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p, - char const **v_p, size_t *vlen_p) { - bool accept; - const char *opts = *opts_p; - - *k_p = opts; - - for (accept = false; !accept;) { - switch (*opts) { - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case '0': case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': - case '_': - opts++; - break; - case ':': - opts++; - *klen_p = (uintptr_t)opts - 1 - (uintptr_t)*k_p; - *v_p = opts; - accept = true; - break; - case '\0': - if (opts != *opts_p) { - malloc_write("<jemalloc>: Conf string ends " - "with key\n"); - had_conf_error = true; - } - return true; - default: - malloc_write("<jemalloc>: Malformed conf string\n"); - had_conf_error = true; - return true; - } - } - - for (accept = false; !accept;) { - switch (*opts) { - case ',': - opts++; - /* - * Look ahead one character here, because the next time - * this function is called, it will assume that end of - * input has been cleanly reached if no input remains, - * but we have optimistically already consumed the - * comma if one exists. - */ - if (*opts == '\0') { - malloc_write("<jemalloc>: Conf string ends " - "with comma\n"); - had_conf_error = true; - } - *vlen_p = (uintptr_t)opts - 1 - (uintptr_t)*v_p; - accept = true; - break; - case '\0': - *vlen_p = (uintptr_t)opts - (uintptr_t)*v_p; - accept = true; - break; - default: - opts++; - break; - } - } - - *opts_p = opts; - return false; -} - -static void -malloc_abort_invalid_conf(void) { - assert(opt_abort_conf); - malloc_printf("<jemalloc>: Abort (abort_conf:true) on invalid conf " - "value (see above).\n"); - abort(); -} - -static void -malloc_conf_error(const char *msg, const char *k, size_t klen, const char *v, - size_t vlen) { - malloc_printf("<jemalloc>: %s: %.*s:%.*s\n", msg, (int)klen, k, - (int)vlen, v); - /* If abort_conf is set, error out after processing all options. */ - const char *experimental = "experimental_"; - if (strncmp(k, experimental, strlen(experimental)) == 0) { - /* However, tolerate experimental features. */ - return; - } - had_conf_error = true; -} - -static void -malloc_slow_flag_init(void) { - /* - * Combine the runtime options into malloc_slow for fast path. Called - * after processing all the options. - */ - malloc_slow_flags |= (opt_junk_alloc ? flag_opt_junk_alloc : 0) - | (opt_junk_free ? flag_opt_junk_free : 0) - | (opt_zero ? flag_opt_zero : 0) - | (opt_utrace ? flag_opt_utrace : 0) - | (opt_xmalloc ? flag_opt_xmalloc : 0); - - malloc_slow = (malloc_slow_flags != 0); -} - -/* Number of sources for initializing malloc_conf */ -#define MALLOC_CONF_NSOURCES 5 - -static const char * -obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) { - if (config_debug) { - static unsigned read_source = 0; - /* - * Each source should only be read once, to minimize # of - * syscalls on init. - */ - assert(read_source++ == which_source); - } - assert(which_source < MALLOC_CONF_NSOURCES); - - const char *ret; - switch (which_source) { - case 0: - ret = config_malloc_conf; - break; - case 1: - if (je_malloc_conf != NULL) { - /* Use options that were compiled into the program. */ - ret = je_malloc_conf; - } else { - /* No configuration specified. */ - ret = NULL; - } - break; - case 2: { - ssize_t linklen = 0; -#ifndef _WIN32 - int saved_errno = errno; - const char *linkname = -# ifdef JEMALLOC_PREFIX - "/etc/"JEMALLOC_PREFIX"malloc.conf" -# else - "/etc/malloc.conf" -# endif - ; - - /* - * Try to use the contents of the "/etc/malloc.conf" symbolic - * link's name. - */ -#ifndef JEMALLOC_READLINKAT - linklen = readlink(linkname, buf, PATH_MAX); -#else - linklen = readlinkat(AT_FDCWD, linkname, buf, PATH_MAX); -#endif - if (linklen == -1) { - /* No configuration specified. */ - linklen = 0; - /* Restore errno. */ - set_errno(saved_errno); - } -#endif - buf[linklen] = '\0'; - ret = buf; - break; - } case 3: { - const char *envname = -#ifdef JEMALLOC_PREFIX - JEMALLOC_CPREFIX"MALLOC_CONF" -#else - "MALLOC_CONF" -#endif - ; - - if ((ret = jemalloc_secure_getenv(envname)) != NULL) { - /* - * Do nothing; opts is already initialized to the value - * of the MALLOC_CONF environment variable. - */ - } else { - /* No configuration specified. */ - ret = NULL; - } - break; - } case 4: { - ret = je_malloc_conf_2_conf_harder; - break; - } default: - not_reached(); - ret = NULL; - } - return ret; -} - -static void -malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], - bool initial_call, const char *opts_cache[MALLOC_CONF_NSOURCES], - char buf[PATH_MAX + 1]) { - static const char *opts_explain[MALLOC_CONF_NSOURCES] = { - "string specified via --with-malloc-conf", - "string pointed to by the global variable malloc_conf", - "\"name\" of the file referenced by the symbolic link named " - "/etc/malloc.conf", - "value of the environment variable MALLOC_CONF", - "string pointed to by the global variable " - "malloc_conf_2_conf_harder", - }; - unsigned i; - const char *opts, *k, *v; - size_t klen, vlen; - - for (i = 0; i < MALLOC_CONF_NSOURCES; i++) { - /* Get runtime configuration. */ - if (initial_call) { - opts_cache[i] = obtain_malloc_conf(i, buf); - } - opts = opts_cache[i]; - if (!initial_call && opt_confirm_conf) { - malloc_printf( - "<jemalloc>: malloc_conf #%u (%s): \"%s\"\n", - i + 1, opts_explain[i], opts != NULL ? opts : ""); - } - if (opts == NULL) { - continue; - } - - while (*opts != '\0' && !malloc_conf_next(&opts, &k, &klen, &v, - &vlen)) { - -#define CONF_ERROR(msg, k, klen, v, vlen) \ - if (!initial_call) { \ - malloc_conf_error( \ - msg, k, klen, v, vlen); \ - cur_opt_valid = false; \ - } -#define CONF_CONTINUE { \ - if (!initial_call && opt_confirm_conf \ - && cur_opt_valid) { \ - malloc_printf("<jemalloc>: -- " \ - "Set conf value: %.*s:%.*s" \ - "\n", (int)klen, k, \ - (int)vlen, v); \ - } \ - continue; \ - } -#define CONF_MATCH(n) \ - (sizeof(n)-1 == klen && strncmp(n, k, klen) == 0) -#define CONF_MATCH_VALUE(n) \ - (sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0) -#define CONF_HANDLE_BOOL(o, n) \ - if (CONF_MATCH(n)) { \ - if (CONF_MATCH_VALUE("true")) { \ - o = true; \ - } else if (CONF_MATCH_VALUE("false")) { \ - o = false; \ - } else { \ - CONF_ERROR("Invalid conf value",\ - k, klen, v, vlen); \ - } \ - CONF_CONTINUE; \ - } - /* - * One of the CONF_MIN macros below expands, in one of the use points, - * to "unsigned integer < 0", which is always false, triggering the - * GCC -Wtype-limits warning, which we disable here and re-enable below. - */ - JEMALLOC_DIAGNOSTIC_PUSH - JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS - -#define CONF_DONT_CHECK_MIN(um, min) false -#define CONF_CHECK_MIN(um, min) ((um) < (min)) -#define CONF_DONT_CHECK_MAX(um, max) false -#define CONF_CHECK_MAX(um, max) ((um) > (max)) - -#define CONF_VALUE_READ(max_t, result) \ - char *end; \ - set_errno(0); \ - result = (max_t)malloc_strtoumax(v, &end, 0); -#define CONF_VALUE_READ_FAIL() \ - (get_errno() != 0 || (uintptr_t)end - (uintptr_t)v != vlen) - -#define CONF_HANDLE_T(t, max_t, o, n, min, max, check_min, check_max, clip) \ - if (CONF_MATCH(n)) { \ - max_t mv; \ - CONF_VALUE_READ(max_t, mv) \ - if (CONF_VALUE_READ_FAIL()) { \ - CONF_ERROR("Invalid conf value",\ - k, klen, v, vlen); \ - } else if (clip) { \ - if (check_min(mv, (t)(min))) { \ - o = (t)(min); \ - } else if ( \ - check_max(mv, (t)(max))) { \ - o = (t)(max); \ - } else { \ - o = (t)mv; \ - } \ - } else { \ - if (check_min(mv, (t)(min)) || \ - check_max(mv, (t)(max))) { \ - CONF_ERROR( \ - "Out-of-range " \ - "conf value", \ - k, klen, v, vlen); \ - } else { \ - o = (t)mv; \ - } \ - } \ - CONF_CONTINUE; \ - } -#define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip) \ - CONF_HANDLE_T(t, uintmax_t, o, n, min, max, check_min, \ - check_max, clip) -#define CONF_HANDLE_T_SIGNED(t, o, n, min, max, check_min, check_max, clip)\ - CONF_HANDLE_T(t, intmax_t, o, n, min, max, check_min, \ - check_max, clip) - -#define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max, \ - clip) \ - CONF_HANDLE_T_U(unsigned, o, n, min, max, \ - check_min, check_max, clip) -#define CONF_HANDLE_SIZE_T(o, n, min, max, check_min, check_max, clip) \ - CONF_HANDLE_T_U(size_t, o, n, min, max, \ - check_min, check_max, clip) -#define CONF_HANDLE_INT64_T(o, n, min, max, check_min, check_max, clip) \ - CONF_HANDLE_T_SIGNED(int64_t, o, n, min, max, \ - check_min, check_max, clip) -#define CONF_HANDLE_UINT64_T(o, n, min, max, check_min, check_max, clip)\ - CONF_HANDLE_T_U(uint64_t, o, n, min, max, \ - check_min, check_max, clip) -#define CONF_HANDLE_SSIZE_T(o, n, min, max) \ - CONF_HANDLE_T_SIGNED(ssize_t, o, n, min, max, \ - CONF_CHECK_MIN, CONF_CHECK_MAX, false) -#define CONF_HANDLE_CHAR_P(o, n, d) \ - if (CONF_MATCH(n)) { \ - size_t cpylen = (vlen <= \ - sizeof(o)-1) ? vlen : \ - sizeof(o)-1; \ - strncpy(o, v, cpylen); \ - o[cpylen] = '\0'; \ - CONF_CONTINUE; \ - } - - bool cur_opt_valid = true; - - CONF_HANDLE_BOOL(opt_confirm_conf, "confirm_conf") - if (initial_call) { - continue; - } - - CONF_HANDLE_BOOL(opt_abort, "abort") - CONF_HANDLE_BOOL(opt_abort_conf, "abort_conf") - CONF_HANDLE_BOOL(opt_trust_madvise, "trust_madvise") - if (strncmp("metadata_thp", k, klen) == 0) { - int m; - bool match = false; - for (m = 0; m < metadata_thp_mode_limit; m++) { - if (strncmp(metadata_thp_mode_names[m], - v, vlen) == 0) { - opt_metadata_thp = m; - match = true; - break; - } - } - if (!match) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - CONF_HANDLE_BOOL(opt_retain, "retain") - if (strncmp("dss", k, klen) == 0) { - int m; - bool match = false; - for (m = 0; m < dss_prec_limit; m++) { - if (strncmp(dss_prec_names[m], v, vlen) - == 0) { - if (extent_dss_prec_set(m)) { - CONF_ERROR( - "Error setting dss", - k, klen, v, vlen); - } else { - opt_dss = - dss_prec_names[m]; - match = true; - break; - } - } - } - if (!match) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - if (CONF_MATCH("narenas")) { - if (CONF_MATCH_VALUE("default")) { - opt_narenas = 0; - CONF_CONTINUE; - } else { - CONF_HANDLE_UNSIGNED(opt_narenas, - "narenas", 1, UINT_MAX, - CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, - /* clip */ false) - } - } - if (CONF_MATCH("narenas_ratio")) { - char *end; - bool err = fxp_parse(&opt_narenas_ratio, v, - &end); - if (err || (size_t)(end - v) != vlen) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - if (CONF_MATCH("bin_shards")) { - const char *bin_shards_segment_cur = v; - size_t vlen_left = vlen; - do { - size_t size_start; - size_t size_end; - size_t nshards; - bool err = malloc_conf_multi_sizes_next( - &bin_shards_segment_cur, &vlen_left, - &size_start, &size_end, &nshards); - if (err || bin_update_shard_size( - bin_shard_sizes, size_start, - size_end, nshards)) { - CONF_ERROR( - "Invalid settings for " - "bin_shards", k, klen, v, - vlen); - break; - } - } while (vlen_left > 0); - CONF_CONTINUE; - } - CONF_HANDLE_INT64_T(opt_mutex_max_spin, - "mutex_max_spin", -1, INT64_MAX, CONF_CHECK_MIN, - CONF_DONT_CHECK_MAX, false); - CONF_HANDLE_SSIZE_T(opt_dirty_decay_ms, - "dirty_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) < - QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) : - SSIZE_MAX); - CONF_HANDLE_SSIZE_T(opt_muzzy_decay_ms, - "muzzy_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) < - QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) : - SSIZE_MAX); - CONF_HANDLE_BOOL(opt_stats_print, "stats_print") - if (CONF_MATCH("stats_print_opts")) { - init_opt_stats_opts(v, vlen, - opt_stats_print_opts); - CONF_CONTINUE; - } - CONF_HANDLE_INT64_T(opt_stats_interval, - "stats_interval", -1, INT64_MAX, - CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, false) - if (CONF_MATCH("stats_interval_opts")) { - init_opt_stats_opts(v, vlen, - opt_stats_interval_opts); - CONF_CONTINUE; - } - if (config_fill) { - if (CONF_MATCH("junk")) { - if (CONF_MATCH_VALUE("true")) { - opt_junk = "true"; - opt_junk_alloc = opt_junk_free = - true; - } else if (CONF_MATCH_VALUE("false")) { - opt_junk = "false"; - opt_junk_alloc = opt_junk_free = - false; - } else if (CONF_MATCH_VALUE("alloc")) { - opt_junk = "alloc"; - opt_junk_alloc = true; - opt_junk_free = false; - } else if (CONF_MATCH_VALUE("free")) { - opt_junk = "free"; - opt_junk_alloc = false; - opt_junk_free = true; - } else { - CONF_ERROR( - "Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - CONF_HANDLE_BOOL(opt_zero, "zero") - } - if (config_utrace) { - CONF_HANDLE_BOOL(opt_utrace, "utrace") - } - if (config_xmalloc) { - CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc") - } - if (config_enable_cxx) { - CONF_HANDLE_BOOL( - opt_experimental_infallible_new, - "experimental_infallible_new") - } - - CONF_HANDLE_BOOL(opt_tcache, "tcache") - CONF_HANDLE_SIZE_T(opt_tcache_max, "tcache_max", - 0, TCACHE_MAXCLASS_LIMIT, CONF_DONT_CHECK_MIN, - CONF_CHECK_MAX, /* clip */ true) - if (CONF_MATCH("lg_tcache_max")) { - size_t m; - CONF_VALUE_READ(size_t, m) - if (CONF_VALUE_READ_FAIL()) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } else { - /* clip if necessary */ - if (m > TCACHE_LG_MAXCLASS_LIMIT) { - m = TCACHE_LG_MAXCLASS_LIMIT; - } - opt_tcache_max = (size_t)1 << m; - } - CONF_CONTINUE; - } - /* - * Anyone trying to set a value outside -16 to 16 is - * deeply confused. - */ - CONF_HANDLE_SSIZE_T(opt_lg_tcache_nslots_mul, - "lg_tcache_nslots_mul", -16, 16) - /* Ditto with values past 2048. */ - CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_min, - "tcache_nslots_small_min", 1, 2048, - CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true) - CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_max, - "tcache_nslots_small_max", 1, 2048, - CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true) - CONF_HANDLE_UNSIGNED(opt_tcache_nslots_large, - "tcache_nslots_large", 1, 2048, - CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true) - CONF_HANDLE_SIZE_T(opt_tcache_gc_incr_bytes, - "tcache_gc_incr_bytes", 1024, SIZE_T_MAX, - CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, - /* clip */ true) - CONF_HANDLE_SIZE_T(opt_tcache_gc_delay_bytes, - "tcache_gc_delay_bytes", 0, SIZE_T_MAX, - CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, - /* clip */ false) - CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_small_div, - "lg_tcache_flush_small_div", 1, 16, - CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true) - CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_large_div, - "lg_tcache_flush_large_div", 1, 16, - CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true) - - /* - * The runtime option of oversize_threshold remains - * undocumented. It may be tweaked in the next major - * release (6.0). The default value 8M is rather - * conservative / safe. Tuning it further down may - * improve fragmentation a bit more, but may also cause - * contention on the huge arena. - */ - CONF_HANDLE_SIZE_T(opt_oversize_threshold, - "oversize_threshold", 0, SC_LARGE_MAXCLASS, - CONF_DONT_CHECK_MIN, CONF_CHECK_MAX, false) - CONF_HANDLE_SIZE_T(opt_lg_extent_max_active_fit, - "lg_extent_max_active_fit", 0, - (sizeof(size_t) << 3), CONF_DONT_CHECK_MIN, - CONF_CHECK_MAX, false) - - if (strncmp("percpu_arena", k, klen) == 0) { - bool match = false; - for (int m = percpu_arena_mode_names_base; m < - percpu_arena_mode_names_limit; m++) { - if (strncmp(percpu_arena_mode_names[m], - v, vlen) == 0) { - if (!have_percpu_arena) { - CONF_ERROR( - "No getcpu support", - k, klen, v, vlen); - } - opt_percpu_arena = m; - match = true; - break; - } - } - if (!match) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - CONF_HANDLE_BOOL(opt_background_thread, - "background_thread"); - CONF_HANDLE_SIZE_T(opt_max_background_threads, - "max_background_threads", 1, - opt_max_background_threads, - CONF_CHECK_MIN, CONF_CHECK_MAX, - true); - CONF_HANDLE_BOOL(opt_hpa, "hpa") - CONF_HANDLE_SIZE_T(opt_hpa_opts.slab_max_alloc, - "hpa_slab_max_alloc", PAGE, HUGEPAGE, - CONF_CHECK_MIN, CONF_CHECK_MAX, true); - - /* - * Accept either a ratio-based or an exact hugification - * threshold. - */ - CONF_HANDLE_SIZE_T(opt_hpa_opts.hugification_threshold, - "hpa_hugification_threshold", PAGE, HUGEPAGE, - CONF_CHECK_MIN, CONF_CHECK_MAX, true); - if (CONF_MATCH("hpa_hugification_threshold_ratio")) { - fxp_t ratio; - char *end; - bool err = fxp_parse(&ratio, v, - &end); - if (err || (size_t)(end - v) != vlen - || ratio > FXP_INIT_INT(1)) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } else { - opt_hpa_opts.hugification_threshold = - fxp_mul_frac(HUGEPAGE, ratio); - } - CONF_CONTINUE; - } - - CONF_HANDLE_UINT64_T( - opt_hpa_opts.hugify_delay_ms, "hpa_hugify_delay_ms", - 0, 0, CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, - false); - - CONF_HANDLE_UINT64_T( - opt_hpa_opts.min_purge_interval_ms, - "hpa_min_purge_interval_ms", 0, 0, - CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false); - - if (CONF_MATCH("hpa_dirty_mult")) { - if (CONF_MATCH_VALUE("-1")) { - opt_hpa_opts.dirty_mult = (fxp_t)-1; - CONF_CONTINUE; - } - fxp_t ratio; - char *end; - bool err = fxp_parse(&ratio, v, - &end); - if (err || (size_t)(end - v) != vlen) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } else { - opt_hpa_opts.dirty_mult = ratio; - } - CONF_CONTINUE; - } - - CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.nshards, - "hpa_sec_nshards", 0, 0, CONF_CHECK_MIN, - CONF_DONT_CHECK_MAX, true); - CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_alloc, - "hpa_sec_max_alloc", PAGE, 0, CONF_CHECK_MIN, - CONF_DONT_CHECK_MAX, true); - CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_bytes, - "hpa_sec_max_bytes", PAGE, 0, CONF_CHECK_MIN, - CONF_DONT_CHECK_MAX, true); - CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.bytes_after_flush, - "hpa_sec_bytes_after_flush", PAGE, 0, - CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, true); - CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.batch_fill_extra, - "hpa_sec_batch_fill_extra", 0, HUGEPAGE_PAGES, - CONF_CHECK_MIN, CONF_CHECK_MAX, true); - - if (CONF_MATCH("slab_sizes")) { - if (CONF_MATCH_VALUE("default")) { - sc_data_init(sc_data); - CONF_CONTINUE; - } - bool err; - const char *slab_size_segment_cur = v; - size_t vlen_left = vlen; - do { - size_t slab_start; - size_t slab_end; - size_t pgs; - err = malloc_conf_multi_sizes_next( - &slab_size_segment_cur, - &vlen_left, &slab_start, &slab_end, - &pgs); - if (!err) { - sc_data_update_slab_size( - sc_data, slab_start, - slab_end, (int)pgs); - } else { - CONF_ERROR("Invalid settings " - "for slab_sizes", - k, klen, v, vlen); - } - } while (!err && vlen_left > 0); - CONF_CONTINUE; - } - if (config_prof) { - CONF_HANDLE_BOOL(opt_prof, "prof") - CONF_HANDLE_CHAR_P(opt_prof_prefix, - "prof_prefix", "jeprof") - CONF_HANDLE_BOOL(opt_prof_active, "prof_active") - CONF_HANDLE_BOOL(opt_prof_thread_active_init, - "prof_thread_active_init") - CONF_HANDLE_SIZE_T(opt_lg_prof_sample, - "lg_prof_sample", 0, (sizeof(uint64_t) << 3) - - 1, CONF_DONT_CHECK_MIN, CONF_CHECK_MAX, - true) - CONF_HANDLE_BOOL(opt_prof_accum, "prof_accum") - CONF_HANDLE_SSIZE_T(opt_lg_prof_interval, - "lg_prof_interval", -1, - (sizeof(uint64_t) << 3) - 1) - CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump") - CONF_HANDLE_BOOL(opt_prof_final, "prof_final") - CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak") - CONF_HANDLE_BOOL(opt_prof_leak_error, - "prof_leak_error") - CONF_HANDLE_BOOL(opt_prof_log, "prof_log") - CONF_HANDLE_SSIZE_T(opt_prof_recent_alloc_max, - "prof_recent_alloc_max", -1, SSIZE_MAX) - CONF_HANDLE_BOOL(opt_prof_stats, "prof_stats") - CONF_HANDLE_BOOL(opt_prof_sys_thread_name, - "prof_sys_thread_name") - if (CONF_MATCH("prof_time_resolution")) { - if (CONF_MATCH_VALUE("default")) { - opt_prof_time_res = - prof_time_res_default; - } else if (CONF_MATCH_VALUE("high")) { - if (!config_high_res_timer) { - CONF_ERROR( - "No high resolution" - " timer support", - k, klen, v, vlen); - } else { - opt_prof_time_res = - prof_time_res_high; - } - } else { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - /* - * Undocumented. When set to false, don't - * correct for an unbiasing bug in jeprof - * attribution. This can be handy if you want - * to get consistent numbers from your binary - * across different jemalloc versions, even if - * those numbers are incorrect. The default is - * true. - */ - CONF_HANDLE_BOOL(opt_prof_unbias, "prof_unbias") - } - if (config_log) { - if (CONF_MATCH("log")) { - size_t cpylen = ( - vlen <= sizeof(log_var_names) ? - vlen : sizeof(log_var_names) - 1); - strncpy(log_var_names, v, cpylen); - log_var_names[cpylen] = '\0'; - CONF_CONTINUE; - } - } - if (CONF_MATCH("thp")) { - bool match = false; - for (int m = 0; m < thp_mode_names_limit; m++) { - if (strncmp(thp_mode_names[m],v, vlen) - == 0) { - if (!have_madvise_huge && !have_memcntl) { - CONF_ERROR( - "No THP support", - k, klen, v, vlen); - } - opt_thp = m; - match = true; - break; - } - } - if (!match) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - if (CONF_MATCH("zero_realloc")) { - if (CONF_MATCH_VALUE("alloc")) { - opt_zero_realloc_action - = zero_realloc_action_alloc; - } else if (CONF_MATCH_VALUE("free")) { - opt_zero_realloc_action - = zero_realloc_action_free; - } else if (CONF_MATCH_VALUE("abort")) { - opt_zero_realloc_action - = zero_realloc_action_abort; - } else { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - CONF_CONTINUE; - } - if (config_uaf_detection && - CONF_MATCH("lg_san_uaf_align")) { - ssize_t a; - CONF_VALUE_READ(ssize_t, a) - if (CONF_VALUE_READ_FAIL() || a < -1) { - CONF_ERROR("Invalid conf value", - k, klen, v, vlen); - } - if (a == -1) { - opt_lg_san_uaf_align = -1; - CONF_CONTINUE; - } - - /* clip if necessary */ - ssize_t max_allowed = (sizeof(size_t) << 3) - 1; - ssize_t min_allowed = LG_PAGE; - if (a > max_allowed) { - a = max_allowed; - } else if (a < min_allowed) { - a = min_allowed; - } - - opt_lg_san_uaf_align = a; - CONF_CONTINUE; - } - - CONF_HANDLE_SIZE_T(opt_san_guard_small, - "san_guard_small", 0, SIZE_T_MAX, - CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false) - CONF_HANDLE_SIZE_T(opt_san_guard_large, - "san_guard_large", 0, SIZE_T_MAX, - CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false) - - CONF_ERROR("Invalid conf pair", k, klen, v, vlen); -#undef CONF_ERROR -#undef CONF_CONTINUE -#undef CONF_MATCH -#undef CONF_MATCH_VALUE -#undef CONF_HANDLE_BOOL -#undef CONF_DONT_CHECK_MIN -#undef CONF_CHECK_MIN -#undef CONF_DONT_CHECK_MAX -#undef CONF_CHECK_MAX -#undef CONF_HANDLE_T -#undef CONF_HANDLE_T_U -#undef CONF_HANDLE_T_SIGNED -#undef CONF_HANDLE_UNSIGNED -#undef CONF_HANDLE_SIZE_T -#undef CONF_HANDLE_SSIZE_T -#undef CONF_HANDLE_CHAR_P - /* Re-enable diagnostic "-Wtype-limits" */ - JEMALLOC_DIAGNOSTIC_POP - } - if (opt_abort_conf && had_conf_error) { - malloc_abort_invalid_conf(); - } - } - atomic_store_b(&log_init_done, true, ATOMIC_RELEASE); -} - -static bool -malloc_conf_init_check_deps(void) { - if (opt_prof_leak_error && !opt_prof_final) { - malloc_printf("<jemalloc>: prof_leak_error is set w/o " - "prof_final.\n"); - return true; - } - - return false; -} - -static void -malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) { - const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL, - NULL}; - char buf[PATH_MAX + 1]; - - /* The first call only set the confirm_conf option and opts_cache */ - malloc_conf_init_helper(NULL, NULL, true, opts_cache, buf); - malloc_conf_init_helper(sc_data, bin_shard_sizes, false, opts_cache, - NULL); - if (malloc_conf_init_check_deps()) { - /* check_deps does warning msg only; abort below if needed. */ - if (opt_abort_conf) { - malloc_abort_invalid_conf(); - } - } -} - -#undef MALLOC_CONF_NSOURCES - -static bool -malloc_init_hard_needed(void) { - if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state == - malloc_init_recursible)) { - /* - * Another thread initialized the allocator before this one - * acquired init_lock, or this thread is the initializing - * thread, and it is recursively allocating. - */ - return false; - } -#ifdef JEMALLOC_THREADED_INIT - if (malloc_initializer != NO_INITIALIZER && !IS_INITIALIZER) { - /* Busy-wait until the initializing thread completes. */ - spin_t spinner = SPIN_INITIALIZER; - do { - malloc_mutex_unlock(TSDN_NULL, &init_lock); - spin_adaptive(&spinner); - malloc_mutex_lock(TSDN_NULL, &init_lock); - } while (!malloc_initialized()); - return false; - } -#endif - return true; -} - -static bool -malloc_init_hard_a0_locked() { - malloc_initializer = INITIALIZER; - - JEMALLOC_DIAGNOSTIC_PUSH - JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS - sc_data_t sc_data = {0}; - JEMALLOC_DIAGNOSTIC_POP - - /* - * Ordering here is somewhat tricky; we need sc_boot() first, since that - * determines what the size classes will be, and then - * malloc_conf_init(), since any slab size tweaking will need to be done - * before sz_boot and bin_info_boot, which assume that the values they - * read out of sc_data_global are final. - */ - sc_boot(&sc_data); - unsigned bin_shard_sizes[SC_NBINS]; - bin_shard_sizes_boot(bin_shard_sizes); - /* - * prof_boot0 only initializes opt_prof_prefix. We need to do it before - * we parse malloc_conf options, in case malloc_conf parsing overwrites - * it. - */ - if (config_prof) { - prof_boot0(); - } - malloc_conf_init(&sc_data, bin_shard_sizes); - san_init(opt_lg_san_uaf_align); - sz_boot(&sc_data, opt_cache_oblivious); - bin_info_boot(&sc_data, bin_shard_sizes); - - if (opt_stats_print) { - /* Print statistics at exit. */ - if (atexit(stats_print_atexit) != 0) { - malloc_write("<jemalloc>: Error in atexit()\n"); - if (opt_abort) { - abort(); - } - } - } - - if (stats_boot()) { - return true; - } - if (pages_boot()) { - return true; - } - if (base_boot(TSDN_NULL)) { - return true; - } - /* emap_global is static, hence zeroed. */ - if (emap_init(&arena_emap_global, b0get(), /* zeroed */ true)) { - return true; - } - if (extent_boot()) { - return true; - } - if (ctl_boot()) { - return true; - } - if (config_prof) { - prof_boot1(); - } - if (opt_hpa && !hpa_supported()) { - malloc_printf("<jemalloc>: HPA not supported in the current " - "configuration; %s.", - opt_abort_conf ? "aborting" : "disabling"); - if (opt_abort_conf) { - malloc_abort_invalid_conf(); - } else { - opt_hpa = false; - } - } - if (arena_boot(&sc_data, b0get(), opt_hpa)) { - return true; - } - if (tcache_boot(TSDN_NULL, b0get())) { - return true; - } - if (malloc_mutex_init(&arenas_lock, "arenas", WITNESS_RANK_ARENAS, - malloc_mutex_rank_exclusive)) { - return true; - } - hook_boot(); - /* - * Create enough scaffolding to allow recursive allocation in - * malloc_ncpus(). - */ - narenas_auto = 1; - manual_arena_base = narenas_auto + 1; - memset(arenas, 0, sizeof(arena_t *) * narenas_auto); - /* - * Initialize one arena here. The rest are lazily created in - * arena_choose_hard(). - */ - if (arena_init(TSDN_NULL, 0, &arena_config_default) == NULL) { - return true; - } - a0 = arena_get(TSDN_NULL, 0, false); - - if (opt_hpa && !hpa_supported()) { - malloc_printf("<jemalloc>: HPA not supported in the current " - "configuration; %s.", - opt_abort_conf ? "aborting" : "disabling"); - if (opt_abort_conf) { - malloc_abort_invalid_conf(); - } else { - opt_hpa = false; - } - } else if (opt_hpa) { - hpa_shard_opts_t hpa_shard_opts = opt_hpa_opts; - hpa_shard_opts.deferral_allowed = background_thread_enabled(); - if (pa_shard_enable_hpa(TSDN_NULL, &a0->pa_shard, - &hpa_shard_opts, &opt_hpa_sec_opts)) { - return true; - } - } - - malloc_init_state = malloc_init_a0_initialized; - - return false; -} - -static bool -malloc_init_hard_a0(void) { - bool ret; - - malloc_mutex_lock(TSDN_NULL, &init_lock); - ret = malloc_init_hard_a0_locked(); - malloc_mutex_unlock(TSDN_NULL, &init_lock); - return ret; -} - -/* Initialize data structures which may trigger recursive allocation. */ -static bool -malloc_init_hard_recursible(void) { - malloc_init_state = malloc_init_recursible; - - ncpus = malloc_ncpus(); - if (opt_percpu_arena != percpu_arena_disabled) { - bool cpu_count_is_deterministic = - malloc_cpu_count_is_deterministic(); - if (!cpu_count_is_deterministic) { - /* - * If # of CPU is not deterministic, and narenas not - * specified, disables per cpu arena since it may not - * detect CPU IDs properly. - */ - if (opt_narenas == 0) { - opt_percpu_arena = percpu_arena_disabled; - malloc_write("<jemalloc>: Number of CPUs " - "detected is not deterministic. Per-CPU " - "arena disabled.\n"); - if (opt_abort_conf) { - malloc_abort_invalid_conf(); - } - if (opt_abort) { - abort(); - } - } - } - } - -#if (defined(JEMALLOC_HAVE_PTHREAD_ATFORK) && !defined(JEMALLOC_MUTEX_INIT_CB) \ - && !defined(JEMALLOC_ZONE) && !defined(_WIN32) && \ - !defined(__native_client__)) - /* LinuxThreads' pthread_atfork() allocates. */ - if (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent, - jemalloc_postfork_child) != 0) { - malloc_write("<jemalloc>: Error in pthread_atfork()\n"); - if (opt_abort) { - abort(); - } - return true; - } -#endif - - if (background_thread_boot0()) { - return true; - } - - return false; -} - -static unsigned -malloc_narenas_default(void) { - assert(ncpus > 0); - /* - * For SMP systems, create more than one arena per CPU by - * default. - */ - if (ncpus > 1) { - fxp_t fxp_ncpus = FXP_INIT_INT(ncpus); - fxp_t goal = fxp_mul(fxp_ncpus, opt_narenas_ratio); - uint32_t int_goal = fxp_round_nearest(goal); - if (int_goal == 0) { - return 1; - } - return int_goal; - } else { - return 1; - } -} - -static percpu_arena_mode_t -percpu_arena_as_initialized(percpu_arena_mode_t mode) { - assert(!malloc_initialized()); - assert(mode <= percpu_arena_disabled); - - if (mode != percpu_arena_disabled) { - mode += percpu_arena_mode_enabled_base; - } - - return mode; -} - -static bool -malloc_init_narenas(void) { - assert(ncpus > 0); - - if (opt_percpu_arena != percpu_arena_disabled) { - if (!have_percpu_arena || malloc_getcpu() < 0) { - opt_percpu_arena = percpu_arena_disabled; - malloc_printf("<jemalloc>: perCPU arena getcpu() not " - "available. Setting narenas to %u.\n", opt_narenas ? - opt_narenas : malloc_narenas_default()); - if (opt_abort) { - abort(); - } - } else { - if (ncpus >= MALLOCX_ARENA_LIMIT) { - malloc_printf("<jemalloc>: narenas w/ percpu" - "arena beyond limit (%d)\n", ncpus); - if (opt_abort) { - abort(); - } - return true; - } - /* NB: opt_percpu_arena isn't fully initialized yet. */ - if (percpu_arena_as_initialized(opt_percpu_arena) == - per_phycpu_arena && ncpus % 2 != 0) { - malloc_printf("<jemalloc>: invalid " - "configuration -- per physical CPU arena " - "with odd number (%u) of CPUs (no hyper " - "threading?).\n", ncpus); - if (opt_abort) - abort(); - } - unsigned n = percpu_arena_ind_limit( - percpu_arena_as_initialized(opt_percpu_arena)); - if (opt_narenas < n) { - /* - * If narenas is specified with percpu_arena - * enabled, actual narenas is set as the greater - * of the two. percpu_arena_choose will be free - * to use any of the arenas based on CPU - * id. This is conservative (at a small cost) - * but ensures correctness. - * - * If for some reason the ncpus determined at - * boot is not the actual number (e.g. because - * of affinity setting from numactl), reserving - * narenas this way provides a workaround for - * percpu_arena. - */ - opt_narenas = n; - } - } - } - if (opt_narenas == 0) { - opt_narenas = malloc_narenas_default(); - } - assert(opt_narenas > 0); - - narenas_auto = opt_narenas; - /* - * Limit the number of arenas to the indexing range of MALLOCX_ARENA(). - */ - if (narenas_auto >= MALLOCX_ARENA_LIMIT) { - narenas_auto = MALLOCX_ARENA_LIMIT - 1; - malloc_printf("<jemalloc>: Reducing narenas to limit (%d)\n", - narenas_auto); - } - narenas_total_set(narenas_auto); - if (arena_init_huge()) { - narenas_total_inc(); - } - manual_arena_base = narenas_total_get(); - - return false; -} - -static void -malloc_init_percpu(void) { - opt_percpu_arena = percpu_arena_as_initialized(opt_percpu_arena); -} - -static bool -malloc_init_hard_finish(void) { - if (malloc_mutex_boot()) { - return true; - } - - malloc_init_state = malloc_init_initialized; - malloc_slow_flag_init(); - - return false; -} - -static void -malloc_init_hard_cleanup(tsdn_t *tsdn, bool reentrancy_set) { - malloc_mutex_assert_owner(tsdn, &init_lock); - malloc_mutex_unlock(tsdn, &init_lock); - if (reentrancy_set) { - assert(!tsdn_null(tsdn)); - tsd_t *tsd = tsdn_tsd(tsdn); - assert(tsd_reentrancy_level_get(tsd) > 0); - post_reentrancy(tsd); - } -} - -static bool -malloc_init_hard(void) { - tsd_t *tsd; - -#if defined(_WIN32) && _WIN32_WINNT < 0x0600 - _init_init_lock(); -#endif - malloc_mutex_lock(TSDN_NULL, &init_lock); - -#define UNLOCK_RETURN(tsdn, ret, reentrancy) \ - malloc_init_hard_cleanup(tsdn, reentrancy); \ - return ret; - - if (!malloc_init_hard_needed()) { - UNLOCK_RETURN(TSDN_NULL, false, false) - } - - if (malloc_init_state != malloc_init_a0_initialized && - malloc_init_hard_a0_locked()) { - UNLOCK_RETURN(TSDN_NULL, true, false) - } - - malloc_mutex_unlock(TSDN_NULL, &init_lock); - /* Recursive allocation relies on functional tsd. */ - tsd = malloc_tsd_boot0(); - if (tsd == NULL) { - return true; - } - if (malloc_init_hard_recursible()) { - return true; - } - - malloc_mutex_lock(tsd_tsdn(tsd), &init_lock); - /* Set reentrancy level to 1 during init. */ - pre_reentrancy(tsd, NULL); - /* Initialize narenas before prof_boot2 (for allocation). */ - if (malloc_init_narenas() - || background_thread_boot1(tsd_tsdn(tsd), b0get())) { - UNLOCK_RETURN(tsd_tsdn(tsd), true, true) - } - if (config_prof && prof_boot2(tsd, b0get())) { - UNLOCK_RETURN(tsd_tsdn(tsd), true, true) - } - - malloc_init_percpu(); - - if (malloc_init_hard_finish()) { - UNLOCK_RETURN(tsd_tsdn(tsd), true, true) - } - post_reentrancy(tsd); - malloc_mutex_unlock(tsd_tsdn(tsd), &init_lock); - - witness_assert_lockless(witness_tsd_tsdn( - tsd_witness_tsdp_get_unsafe(tsd))); - malloc_tsd_boot1(); - /* Update TSD after tsd_boot1. */ - tsd = tsd_fetch(); - if (opt_background_thread) { - assert(have_background_thread); - /* - * Need to finish init & unlock first before creating background - * threads (pthread_create depends on malloc). ctl_init (which - * sets isthreaded) needs to be called without holding any lock. - */ - background_thread_ctl_init(tsd_tsdn(tsd)); - if (background_thread_create(tsd, 0)) { - return true; - } - } -#undef UNLOCK_RETURN - return false; -} - -/* - * End initialization functions. - */ -/******************************************************************************/ -/* - * Begin allocation-path internal functions and data structures. - */ - -/* - * Settings determined by the documented behavior of the allocation functions. - */ -typedef struct static_opts_s static_opts_t; -struct static_opts_s { - /* Whether or not allocation size may overflow. */ - bool may_overflow; - - /* - * Whether or not allocations (with alignment) of size 0 should be - * treated as size 1. - */ - bool bump_empty_aligned_alloc; - /* - * Whether to assert that allocations are not of size 0 (after any - * bumping). - */ - bool assert_nonempty_alloc; - - /* - * Whether or not to modify the 'result' argument to malloc in case of - * error. - */ - bool null_out_result_on_error; - /* Whether to set errno when we encounter an error condition. */ - bool set_errno_on_error; - - /* - * The minimum valid alignment for functions requesting aligned storage. - */ - size_t min_alignment; - - /* The error string to use if we oom. */ - const char *oom_string; - /* The error string to use if the passed-in alignment is invalid. */ - const char *invalid_alignment_string; - - /* - * False if we're configured to skip some time-consuming operations. - * - * This isn't really a malloc "behavior", but it acts as a useful - * summary of several other static (or at least, static after program - * initialization) options. - */ - bool slow; - /* - * Return size. - */ - bool usize; -}; - -JEMALLOC_ALWAYS_INLINE void -static_opts_init(static_opts_t *static_opts) { - static_opts->may_overflow = false; - static_opts->bump_empty_aligned_alloc = false; - static_opts->assert_nonempty_alloc = false; - static_opts->null_out_result_on_error = false; - static_opts->set_errno_on_error = false; - static_opts->min_alignment = 0; - static_opts->oom_string = ""; - static_opts->invalid_alignment_string = ""; - static_opts->slow = false; - static_opts->usize = false; -} - -/* - * These correspond to the macros in jemalloc/jemalloc_macros.h. Broadly, we - * should have one constant here per magic value there. Note however that the - * representations need not be related. - */ -#define TCACHE_IND_NONE ((unsigned)-1) -#define TCACHE_IND_AUTOMATIC ((unsigned)-2) -#define ARENA_IND_AUTOMATIC ((unsigned)-1) - -typedef struct dynamic_opts_s dynamic_opts_t; -struct dynamic_opts_s { - void **result; - size_t usize; - size_t num_items; - size_t item_size; - size_t alignment; - bool zero; - unsigned tcache_ind; - unsigned arena_ind; -}; - -JEMALLOC_ALWAYS_INLINE void -dynamic_opts_init(dynamic_opts_t *dynamic_opts) { - dynamic_opts->result = NULL; - dynamic_opts->usize = 0; - dynamic_opts->num_items = 0; - dynamic_opts->item_size = 0; - dynamic_opts->alignment = 0; - dynamic_opts->zero = false; - dynamic_opts->tcache_ind = TCACHE_IND_AUTOMATIC; - dynamic_opts->arena_ind = ARENA_IND_AUTOMATIC; -} - -/* - * ind parameter is optional and is only checked and filled if alignment == 0; - * return true if result is out of range. - */ -JEMALLOC_ALWAYS_INLINE bool -aligned_usize_get(size_t size, size_t alignment, size_t *usize, szind_t *ind, - bool bump_empty_aligned_alloc) { - assert(usize != NULL); - if (alignment == 0) { - if (ind != NULL) { - *ind = sz_size2index(size); - if (unlikely(*ind >= SC_NSIZES)) { - return true; - } - *usize = sz_index2size(*ind); - assert(*usize > 0 && *usize <= SC_LARGE_MAXCLASS); - return false; - } - *usize = sz_s2u(size); - } else { - if (bump_empty_aligned_alloc && unlikely(size == 0)) { - size = 1; - } - *usize = sz_sa2u(size, alignment); - } - if (unlikely(*usize == 0 || *usize > SC_LARGE_MAXCLASS)) { - return true; - } - return false; -} - -JEMALLOC_ALWAYS_INLINE bool -zero_get(bool guarantee, bool slow) { - if (config_fill && slow && unlikely(opt_zero)) { - return true; - } else { - return guarantee; - } -} - -JEMALLOC_ALWAYS_INLINE tcache_t * -tcache_get_from_ind(tsd_t *tsd, unsigned tcache_ind, bool slow, bool is_alloc) { - tcache_t *tcache; - if (tcache_ind == TCACHE_IND_AUTOMATIC) { - if (likely(!slow)) { - /* Getting tcache ptr unconditionally. */ - tcache = tsd_tcachep_get(tsd); - assert(tcache == tcache_get(tsd)); - } else if (is_alloc || - likely(tsd_reentrancy_level_get(tsd) == 0)) { - tcache = tcache_get(tsd); - } else { - tcache = NULL; - } - } else { - /* - * Should not specify tcache on deallocation path when being - * reentrant. - */ - assert(is_alloc || tsd_reentrancy_level_get(tsd) == 0 || - tsd_state_nocleanup(tsd)); - if (tcache_ind == TCACHE_IND_NONE) { - tcache = NULL; - } else { - tcache = tcaches_get(tsd, tcache_ind); - } - } - return tcache; -} - -/* Return true if a manual arena is specified and arena_get() OOMs. */ -JEMALLOC_ALWAYS_INLINE bool -arena_get_from_ind(tsd_t *tsd, unsigned arena_ind, arena_t **arena_p) { - if (arena_ind == ARENA_IND_AUTOMATIC) { - /* - * In case of automatic arena management, we defer arena - * computation until as late as we can, hoping to fill the - * allocation out of the tcache. - */ - *arena_p = NULL; - } else { - *arena_p = arena_get(tsd_tsdn(tsd), arena_ind, true); - if (unlikely(*arena_p == NULL) && arena_ind >= narenas_auto) { - return true; - } - } - return false; -} - -/* ind is ignored if dopts->alignment > 0. */ -JEMALLOC_ALWAYS_INLINE void * -imalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd, - size_t size, size_t usize, szind_t ind) { - /* Fill in the tcache. */ - tcache_t *tcache = tcache_get_from_ind(tsd, dopts->tcache_ind, - sopts->slow, /* is_alloc */ true); - - /* Fill in the arena. */ - arena_t *arena; - if (arena_get_from_ind(tsd, dopts->arena_ind, &arena)) { - return NULL; - } - - if (unlikely(dopts->alignment != 0)) { - return ipalloct(tsd_tsdn(tsd), usize, dopts->alignment, - dopts->zero, tcache, arena); - } - - return iallocztm(tsd_tsdn(tsd), size, ind, dopts->zero, tcache, false, - arena, sopts->slow); -} - -JEMALLOC_ALWAYS_INLINE void * -imalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd, - size_t usize, szind_t ind) { - void *ret; - - /* - * For small allocations, sampling bumps the usize. If so, we allocate - * from the ind_large bucket. - */ - szind_t ind_large; - size_t bumped_usize = usize; - - dopts->alignment = prof_sample_align(dopts->alignment); - if (usize <= SC_SMALL_MAXCLASS) { - assert(((dopts->alignment == 0) ? - sz_s2u(SC_LARGE_MINCLASS) : - sz_sa2u(SC_LARGE_MINCLASS, dopts->alignment)) - == SC_LARGE_MINCLASS); - ind_large = sz_size2index(SC_LARGE_MINCLASS); - bumped_usize = sz_s2u(SC_LARGE_MINCLASS); - ret = imalloc_no_sample(sopts, dopts, tsd, bumped_usize, - bumped_usize, ind_large); - if (unlikely(ret == NULL)) { - return NULL; - } - arena_prof_promote(tsd_tsdn(tsd), ret, usize); - } else { - ret = imalloc_no_sample(sopts, dopts, tsd, usize, usize, ind); - } - assert(prof_sample_aligned(ret)); - - return ret; -} - -/* - * Returns true if the allocation will overflow, and false otherwise. Sets - * *size to the product either way. - */ -JEMALLOC_ALWAYS_INLINE bool -compute_size_with_overflow(bool may_overflow, dynamic_opts_t *dopts, - size_t *size) { - /* - * This function is just num_items * item_size, except that we may have - * to check for overflow. - */ - - if (!may_overflow) { - assert(dopts->num_items == 1); - *size = dopts->item_size; - return false; - } - - /* A size_t with its high-half bits all set to 1. */ - static const size_t high_bits = SIZE_T_MAX << (sizeof(size_t) * 8 / 2); - - *size = dopts->item_size * dopts->num_items; - - if (unlikely(*size == 0)) { - return (dopts->num_items != 0 && dopts->item_size != 0); - } - - /* - * We got a non-zero size, but we don't know if we overflowed to get - * there. To avoid having to do a divide, we'll be clever and note that - * if both A and B can be represented in N/2 bits, then their product - * can be represented in N bits (without the possibility of overflow). - */ - if (likely((high_bits & (dopts->num_items | dopts->item_size)) == 0)) { - return false; - } - if (likely(*size / dopts->item_size == dopts->num_items)) { - return false; - } - return true; -} - -JEMALLOC_ALWAYS_INLINE int -imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) { - /* Where the actual allocated memory will live. */ - void *allocation = NULL; - /* Filled in by compute_size_with_overflow below. */ - size_t size = 0; - /* - * The zero initialization for ind is actually dead store, in that its - * value is reset before any branch on its value is taken. Sometimes - * though, it's convenient to pass it as arguments before this point. - * To avoid undefined behavior then, we initialize it with dummy stores. - */ - szind_t ind = 0; - /* usize will always be properly initialized. */ - size_t usize; - - /* Reentrancy is only checked on slow path. */ - int8_t reentrancy_level; - - /* Compute the amount of memory the user wants. */ - if (unlikely(compute_size_with_overflow(sopts->may_overflow, dopts, - &size))) { - goto label_oom; - } - - if (unlikely(dopts->alignment < sopts->min_alignment - || (dopts->alignment & (dopts->alignment - 1)) != 0)) { - goto label_invalid_alignment; - } - - /* This is the beginning of the "core" algorithm. */ - dopts->zero = zero_get(dopts->zero, sopts->slow); - if (aligned_usize_get(size, dopts->alignment, &usize, &ind, - sopts->bump_empty_aligned_alloc)) { - goto label_oom; - } - dopts->usize = usize; - /* Validate the user input. */ - if (sopts->assert_nonempty_alloc) { - assert (size != 0); - } - - check_entry_exit_locking(tsd_tsdn(tsd)); - - /* - * If we need to handle reentrancy, we can do it out of a - * known-initialized arena (i.e. arena 0). - */ - reentrancy_level = tsd_reentrancy_level_get(tsd); - if (sopts->slow && unlikely(reentrancy_level > 0)) { - /* - * We should never specify particular arenas or tcaches from - * within our internal allocations. - */ - assert(dopts->tcache_ind == TCACHE_IND_AUTOMATIC || - dopts->tcache_ind == TCACHE_IND_NONE); - assert(dopts->arena_ind == ARENA_IND_AUTOMATIC); - dopts->tcache_ind = TCACHE_IND_NONE; - /* We know that arena 0 has already been initialized. */ - dopts->arena_ind = 0; - } - - /* - * If dopts->alignment > 0, then ind is still 0, but usize was computed - * in the previous if statement. Down the positive alignment path, - * imalloc_no_sample and imalloc_sample will ignore ind. - */ - - /* If profiling is on, get our profiling context. */ - if (config_prof && opt_prof) { - bool prof_active = prof_active_get_unlocked(); - bool sample_event = te_prof_sample_event_lookahead(tsd, usize); - prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, - sample_event); - - emap_alloc_ctx_t alloc_ctx; - if (likely((uintptr_t)tctx == (uintptr_t)1U)) { - alloc_ctx.slab = (usize <= SC_SMALL_MAXCLASS); - allocation = imalloc_no_sample( - sopts, dopts, tsd, usize, usize, ind); - } else if ((uintptr_t)tctx > (uintptr_t)1U) { - allocation = imalloc_sample( - sopts, dopts, tsd, usize, ind); - alloc_ctx.slab = false; - } else { - allocation = NULL; - } - - if (unlikely(allocation == NULL)) { - prof_alloc_rollback(tsd, tctx); - goto label_oom; - } - prof_malloc(tsd, allocation, size, usize, &alloc_ctx, tctx); - } else { - assert(!opt_prof); - allocation = imalloc_no_sample(sopts, dopts, tsd, size, usize, - ind); - if (unlikely(allocation == NULL)) { - goto label_oom; - } - } - - /* - * Allocation has been done at this point. We still have some - * post-allocation work to do though. - */ - - thread_alloc_event(tsd, usize); - - assert(dopts->alignment == 0 - || ((uintptr_t)allocation & (dopts->alignment - 1)) == ZU(0)); - - assert(usize == isalloc(tsd_tsdn(tsd), allocation)); - - if (config_fill && sopts->slow && !dopts->zero - && unlikely(opt_junk_alloc)) { - junk_alloc_callback(allocation, usize); - } - - if (sopts->slow) { - UTRACE(0, size, allocation); - } - - /* Success! */ - check_entry_exit_locking(tsd_tsdn(tsd)); - *dopts->result = allocation; - return 0; - -label_oom: - if (unlikely(sopts->slow) && config_xmalloc && unlikely(opt_xmalloc)) { - malloc_write(sopts->oom_string); - abort(); - } - - if (sopts->slow) { - UTRACE(NULL, size, NULL); - } - - check_entry_exit_locking(tsd_tsdn(tsd)); - - if (sopts->set_errno_on_error) { - set_errno(ENOMEM); - } - - if (sopts->null_out_result_on_error) { - *dopts->result = NULL; - } - - return ENOMEM; - - /* - * This label is only jumped to by one goto; we move it out of line - * anyways to avoid obscuring the non-error paths, and for symmetry with - * the oom case. - */ -label_invalid_alignment: - if (config_xmalloc && unlikely(opt_xmalloc)) { - malloc_write(sopts->invalid_alignment_string); - abort(); - } - - if (sopts->set_errno_on_error) { - set_errno(EINVAL); - } - - if (sopts->slow) { - UTRACE(NULL, size, NULL); - } - - check_entry_exit_locking(tsd_tsdn(tsd)); - - if (sopts->null_out_result_on_error) { - *dopts->result = NULL; - } - - return EINVAL; -} - -JEMALLOC_ALWAYS_INLINE bool -imalloc_init_check(static_opts_t *sopts, dynamic_opts_t *dopts) { - if (unlikely(!malloc_initialized()) && unlikely(malloc_init())) { - if (config_xmalloc && unlikely(opt_xmalloc)) { - malloc_write(sopts->oom_string); - abort(); - } - UTRACE(NULL, dopts->num_items * dopts->item_size, NULL); - set_errno(ENOMEM); - *dopts->result = NULL; - - return false; - } - - return true; -} - -/* Returns the errno-style error code of the allocation. */ -JEMALLOC_ALWAYS_INLINE int -imalloc(static_opts_t *sopts, dynamic_opts_t *dopts) { - if (tsd_get_allocates() && !imalloc_init_check(sopts, dopts)) { - return ENOMEM; - } - - /* We always need the tsd. Let's grab it right away. */ - tsd_t *tsd = tsd_fetch(); - assert(tsd); - if (likely(tsd_fast(tsd))) { - /* Fast and common path. */ - tsd_assert_fast(tsd); - sopts->slow = false; - return imalloc_body(sopts, dopts, tsd); - } else { - if (!tsd_get_allocates() && !imalloc_init_check(sopts, dopts)) { - return ENOMEM; - } - - sopts->slow = true; - return imalloc_body(sopts, dopts, tsd); - } -} - -JEMALLOC_NOINLINE -void * -malloc_default(size_t size) { - void *ret; - static_opts_t sopts; - dynamic_opts_t dopts; - - /* - * This variant has logging hook on exit but not on entry. It's callled - * only by je_malloc, below, which emits the entry one for us (and, if - * it calls us, does so only via tail call). - */ - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.null_out_result_on_error = true; - sopts.set_errno_on_error = true; - sopts.oom_string = "<jemalloc>: Error in malloc(): out of memory\n"; - - dopts.result = &ret; - dopts.num_items = 1; - dopts.item_size = size; - - imalloc(&sopts, &dopts); - /* - * Note that this branch gets optimized away -- it immediately follows - * the check on tsd_fast that sets sopts.slow. - */ - if (sopts.slow) { - uintptr_t args[3] = {size}; - hook_invoke_alloc(hook_alloc_malloc, ret, (uintptr_t)ret, args); - } - - LOG("core.malloc.exit", "result: %p", ret); - - return ret; -} - -/******************************************************************************/ -/* - * Begin malloc(3)-compatible functions. - */ - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1) -je_malloc(size_t size) { - return imalloc_fastpath(size, &malloc_default); -} - -JEMALLOC_EXPORT int JEMALLOC_NOTHROW -JEMALLOC_ATTR(nonnull(1)) -je_posix_memalign(void **memptr, size_t alignment, size_t size) { - int ret; - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.posix_memalign.entry", "mem ptr: %p, alignment: %zu, " - "size: %zu", memptr, alignment, size); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.bump_empty_aligned_alloc = true; - sopts.min_alignment = sizeof(void *); - sopts.oom_string = - "<jemalloc>: Error allocating aligned memory: out of memory\n"; - sopts.invalid_alignment_string = - "<jemalloc>: Error allocating aligned memory: invalid alignment\n"; - - dopts.result = memptr; - dopts.num_items = 1; - dopts.item_size = size; - dopts.alignment = alignment; - - ret = imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {(uintptr_t)memptr, (uintptr_t)alignment, - (uintptr_t)size}; - hook_invoke_alloc(hook_alloc_posix_memalign, *memptr, - (uintptr_t)ret, args); - } - - LOG("core.posix_memalign.exit", "result: %d, alloc ptr: %p", ret, - *memptr); - - return ret; -} - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2) -je_aligned_alloc(size_t alignment, size_t size) { - void *ret; - - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.aligned_alloc.entry", "alignment: %zu, size: %zu\n", - alignment, size); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.bump_empty_aligned_alloc = true; - sopts.null_out_result_on_error = true; - sopts.set_errno_on_error = true; - sopts.min_alignment = 1; - sopts.oom_string = - "<jemalloc>: Error allocating aligned memory: out of memory\n"; - sopts.invalid_alignment_string = - "<jemalloc>: Error allocating aligned memory: invalid alignment\n"; - - dopts.result = &ret; - dopts.num_items = 1; - dopts.item_size = size; - dopts.alignment = alignment; - - imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {(uintptr_t)alignment, (uintptr_t)size}; - hook_invoke_alloc(hook_alloc_aligned_alloc, ret, - (uintptr_t)ret, args); - } - - LOG("core.aligned_alloc.exit", "result: %p", ret); - - return ret; -} - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2) -je_calloc(size_t num, size_t size) { - void *ret; - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.calloc.entry", "num: %zu, size: %zu\n", num, size); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.may_overflow = true; - sopts.null_out_result_on_error = true; - sopts.set_errno_on_error = true; - sopts.oom_string = "<jemalloc>: Error in calloc(): out of memory\n"; - - dopts.result = &ret; - dopts.num_items = num; - dopts.item_size = size; - dopts.zero = true; - - imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {(uintptr_t)num, (uintptr_t)size}; - hook_invoke_alloc(hook_alloc_calloc, ret, (uintptr_t)ret, args); - } - - LOG("core.calloc.exit", "result: %p", ret); - - return ret; -} - -JEMALLOC_ALWAYS_INLINE void -ifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) { - if (!slow_path) { - tsd_assert_fast(tsd); - } - check_entry_exit_locking(tsd_tsdn(tsd)); - if (tsd_reentrancy_level_get(tsd) != 0) { - assert(slow_path); - } - - assert(ptr != NULL); - assert(malloc_initialized() || IS_INITIALIZER); - - emap_alloc_ctx_t alloc_ctx; - emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr, - &alloc_ctx); - assert(alloc_ctx.szind != SC_NSIZES); - - size_t usize = sz_index2size(alloc_ctx.szind); - if (config_prof && opt_prof) { - prof_free(tsd, ptr, usize, &alloc_ctx); - } - - if (likely(!slow_path)) { - idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false, - false); - } else { - if (config_fill && slow_path && opt_junk_free) { - junk_free_callback(ptr, usize); - } - idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false, - true); - } - thread_dalloc_event(tsd, usize); -} - -JEMALLOC_ALWAYS_INLINE bool -maybe_check_alloc_ctx(tsd_t *tsd, void *ptr, emap_alloc_ctx_t *alloc_ctx) { - if (config_opt_size_checks) { - emap_alloc_ctx_t dbg_ctx; - emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr, - &dbg_ctx); - if (alloc_ctx->szind != dbg_ctx.szind) { - safety_check_fail_sized_dealloc( - /* current_dealloc */ true, ptr, - /* true_size */ sz_size2index(dbg_ctx.szind), - /* input_size */ sz_size2index(alloc_ctx->szind)); - return true; - } - if (alloc_ctx->slab != dbg_ctx.slab) { - safety_check_fail( - "Internal heap corruption detected: " - "mismatch in slab bit"); - return true; - } - } - return false; -} - -JEMALLOC_ALWAYS_INLINE void -isfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache, bool slow_path) { - if (!slow_path) { - tsd_assert_fast(tsd); - } - check_entry_exit_locking(tsd_tsdn(tsd)); - if (tsd_reentrancy_level_get(tsd) != 0) { - assert(slow_path); - } - - assert(ptr != NULL); - assert(malloc_initialized() || IS_INITIALIZER); - - emap_alloc_ctx_t alloc_ctx; - if (!config_prof) { - alloc_ctx.szind = sz_size2index(usize); - alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS); - } else { - if (likely(!prof_sample_aligned(ptr))) { - /* - * When the ptr is not page aligned, it was not sampled. - * usize can be trusted to determine szind and slab. - */ - alloc_ctx.szind = sz_size2index(usize); - alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS); - } else if (opt_prof) { - emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, - ptr, &alloc_ctx); - - if (config_opt_safety_checks) { - /* Small alloc may have !slab (sampled). */ - if (unlikely(alloc_ctx.szind != - sz_size2index(usize))) { - safety_check_fail_sized_dealloc( - /* current_dealloc */ true, ptr, - /* true_size */ sz_index2size( - alloc_ctx.szind), - /* input_size */ usize); - } - } - } else { - alloc_ctx.szind = sz_size2index(usize); - alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS); - } - } - bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx); - if (fail) { - /* - * This is a heap corruption bug. In real life we'll crash; for - * the unit test we just want to avoid breaking anything too - * badly to get a test result out. Let's leak instead of trying - * to free. - */ - return; - } - - if (config_prof && opt_prof) { - prof_free(tsd, ptr, usize, &alloc_ctx); - } - if (likely(!slow_path)) { - isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx, - false); - } else { - if (config_fill && slow_path && opt_junk_free) { - junk_free_callback(ptr, usize); - } - isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx, - true); - } - thread_dalloc_event(tsd, usize); -} - -JEMALLOC_NOINLINE -void -free_default(void *ptr) { - UTRACE(ptr, 0, 0); - if (likely(ptr != NULL)) { - /* - * We avoid setting up tsd fully (e.g. tcache, arena binding) - * based on only free() calls -- other activities trigger the - * minimal to full transition. This is because free() may - * happen during thread shutdown after tls deallocation: if a - * thread never had any malloc activities until then, a - * fully-setup tsd won't be destructed properly. - */ - tsd_t *tsd = tsd_fetch_min(); - check_entry_exit_locking(tsd_tsdn(tsd)); - - if (likely(tsd_fast(tsd))) { - tcache_t *tcache = tcache_get_from_ind(tsd, - TCACHE_IND_AUTOMATIC, /* slow */ false, - /* is_alloc */ false); - ifree(tsd, ptr, tcache, /* slow */ false); - } else { - tcache_t *tcache = tcache_get_from_ind(tsd, - TCACHE_IND_AUTOMATIC, /* slow */ true, - /* is_alloc */ false); - uintptr_t args_raw[3] = {(uintptr_t)ptr}; - hook_invoke_dalloc(hook_dalloc_free, ptr, args_raw); - ifree(tsd, ptr, tcache, /* slow */ true); - } - - check_entry_exit_locking(tsd_tsdn(tsd)); - } -} - -JEMALLOC_ALWAYS_INLINE bool -free_fastpath_nonfast_aligned(void *ptr, bool check_prof) { - /* - * free_fastpath do not handle two uncommon cases: 1) sampled profiled - * objects and 2) sampled junk & stash for use-after-free detection. - * Both have special alignments which are used to escape the fastpath. - * - * prof_sample is page-aligned, which covers the UAF check when both - * are enabled (the assertion below). Avoiding redundant checks since - * this is on the fastpath -- at most one runtime branch from this. - */ - if (config_debug && cache_bin_nonfast_aligned(ptr)) { - assert(prof_sample_aligned(ptr)); - } - - if (config_prof && check_prof) { - /* When prof is enabled, the prof_sample alignment is enough. */ - if (prof_sample_aligned(ptr)) { - return true; - } else { - return false; - } - } - - if (config_uaf_detection) { - if (cache_bin_nonfast_aligned(ptr)) { - return true; - } else { - return false; - } - } - - return false; -} - -/* Returns whether or not the free attempt was successful. */ -JEMALLOC_ALWAYS_INLINE -bool free_fastpath(void *ptr, size_t size, bool size_hint) { - tsd_t *tsd = tsd_get(false); - /* The branch gets optimized away unless tsd_get_allocates(). */ - if (unlikely(tsd == NULL)) { - return false; - } - /* - * The tsd_fast() / initialized checks are folded into the branch - * testing (deallocated_after >= threshold) later in this function. - * The threshold will be set to 0 when !tsd_fast. - */ - assert(tsd_fast(tsd) || - *tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd) == 0); - - emap_alloc_ctx_t alloc_ctx; - if (!size_hint) { - bool err = emap_alloc_ctx_try_lookup_fast(tsd, - &arena_emap_global, ptr, &alloc_ctx); - - /* Note: profiled objects will have alloc_ctx.slab set */ - if (unlikely(err || !alloc_ctx.slab || - free_fastpath_nonfast_aligned(ptr, - /* check_prof */ false))) { - return false; - } - assert(alloc_ctx.szind != SC_NSIZES); - } else { - /* - * Check for both sizes that are too large, and for sampled / - * special aligned objects. The alignment check will also check - * for null ptr. - */ - if (unlikely(size > SC_LOOKUP_MAXCLASS || - free_fastpath_nonfast_aligned(ptr, - /* check_prof */ true))) { - return false; - } - alloc_ctx.szind = sz_size2index_lookup(size); - /* Max lookup class must be small. */ - assert(alloc_ctx.szind < SC_NBINS); - /* This is a dead store, except when opt size checking is on. */ - alloc_ctx.slab = true; - } - /* - * Currently the fastpath only handles small sizes. The branch on - * SC_LOOKUP_MAXCLASS makes sure of it. This lets us avoid checking - * tcache szind upper limit (i.e. tcache_maxclass) as well. - */ - assert(alloc_ctx.slab); - - uint64_t deallocated, threshold; - te_free_fastpath_ctx(tsd, &deallocated, &threshold); - - size_t usize = sz_index2size(alloc_ctx.szind); - uint64_t deallocated_after = deallocated + usize; - /* - * Check for events and tsd non-nominal (fast_threshold will be set to - * 0) in a single branch. Note that this handles the uninitialized case - * as well (TSD init will be triggered on the non-fastpath). Therefore - * anything depends on a functional TSD (e.g. the alloc_ctx sanity check - * below) needs to be after this branch. - */ - if (unlikely(deallocated_after >= threshold)) { - return false; - } - assert(tsd_fast(tsd)); - bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx); - if (fail) { - /* See the comment in isfree. */ - return true; - } - - tcache_t *tcache = tcache_get_from_ind(tsd, TCACHE_IND_AUTOMATIC, - /* slow */ false, /* is_alloc */ false); - cache_bin_t *bin = &tcache->bins[alloc_ctx.szind]; - - /* - * If junking were enabled, this is where we would do it. It's not - * though, since we ensured above that we're on the fast path. Assert - * that to double-check. - */ - assert(!opt_junk_free); - - if (!cache_bin_dalloc_easy(bin, ptr)) { - return false; - } - - *tsd_thread_deallocatedp_get(tsd) = deallocated_after; - - return true; -} - -JEMALLOC_EXPORT void JEMALLOC_NOTHROW -je_free(void *ptr) { - LOG("core.free.entry", "ptr: %p", ptr); - - if (!free_fastpath(ptr, 0, false)) { - free_default(ptr); - } - - LOG("core.free.exit", ""); -} - -/* - * End malloc(3)-compatible functions. - */ -/******************************************************************************/ -/* - * Begin non-standard override functions. - */ - -#ifdef JEMALLOC_OVERRIDE_MEMALIGN -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ATTR(malloc) -je_memalign(size_t alignment, size_t size) { - void *ret; - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.memalign.entry", "alignment: %zu, size: %zu\n", alignment, - size); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.min_alignment = 1; - sopts.oom_string = - "<jemalloc>: Error allocating aligned memory: out of memory\n"; - sopts.invalid_alignment_string = - "<jemalloc>: Error allocating aligned memory: invalid alignment\n"; - sopts.null_out_result_on_error = true; - - dopts.result = &ret; - dopts.num_items = 1; - dopts.item_size = size; - dopts.alignment = alignment; - - imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {alignment, size}; - hook_invoke_alloc(hook_alloc_memalign, ret, (uintptr_t)ret, - args); - } - - LOG("core.memalign.exit", "result: %p", ret); - return ret; -} -#endif - -#ifdef JEMALLOC_OVERRIDE_VALLOC -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ATTR(malloc) -je_valloc(size_t size) { - void *ret; - - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.valloc.entry", "size: %zu\n", size); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.null_out_result_on_error = true; - sopts.min_alignment = PAGE; - sopts.oom_string = - "<jemalloc>: Error allocating aligned memory: out of memory\n"; - sopts.invalid_alignment_string = - "<jemalloc>: Error allocating aligned memory: invalid alignment\n"; - - dopts.result = &ret; - dopts.num_items = 1; - dopts.item_size = size; - dopts.alignment = PAGE; - - imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {size}; - hook_invoke_alloc(hook_alloc_valloc, ret, (uintptr_t)ret, args); - } - - LOG("core.valloc.exit", "result: %p\n", ret); - return ret; -} -#endif - -#if defined(JEMALLOC_IS_MALLOC) && defined(JEMALLOC_GLIBC_MALLOC_HOOK) -/* - * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible - * to inconsistently reference libc's malloc(3)-compatible functions - * (https://bugzilla.mozilla.org/show_bug.cgi?id=493541). - * - * These definitions interpose hooks in glibc. The functions are actually - * passed an extra argument for the caller return address, which will be - * ignored. - */ -#include <features.h> // defines __GLIBC__ if we are compiling against glibc - -JEMALLOC_EXPORT void (*__free_hook)(void *ptr) = je_free; -JEMALLOC_EXPORT void *(*__malloc_hook)(size_t size) = je_malloc; -JEMALLOC_EXPORT void *(*__realloc_hook)(void *ptr, size_t size) = je_realloc; -# ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK -JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) = - je_memalign; -# endif - -# ifdef __GLIBC__ -/* - * To enable static linking with glibc, the libc specific malloc interface must - * be implemented also, so none of glibc's malloc.o functions are added to the - * link. - */ -# define ALIAS(je_fn) __attribute__((alias (#je_fn), used)) -/* To force macro expansion of je_ prefix before stringification. */ -# define PREALIAS(je_fn) ALIAS(je_fn) -# ifdef JEMALLOC_OVERRIDE___LIBC_CALLOC -void *__libc_calloc(size_t n, size_t size) PREALIAS(je_calloc); -# endif -# ifdef JEMALLOC_OVERRIDE___LIBC_FREE -void __libc_free(void* ptr) PREALIAS(je_free); -# endif -# ifdef JEMALLOC_OVERRIDE___LIBC_MALLOC -void *__libc_malloc(size_t size) PREALIAS(je_malloc); -# endif -# ifdef JEMALLOC_OVERRIDE___LIBC_MEMALIGN -void *__libc_memalign(size_t align, size_t s) PREALIAS(je_memalign); -# endif -# ifdef JEMALLOC_OVERRIDE___LIBC_REALLOC -void *__libc_realloc(void* ptr, size_t size) PREALIAS(je_realloc); -# endif -# ifdef JEMALLOC_OVERRIDE___LIBC_VALLOC -void *__libc_valloc(size_t size) PREALIAS(je_valloc); -# endif -# ifdef JEMALLOC_OVERRIDE___POSIX_MEMALIGN -int __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign); -# endif -# undef PREALIAS -# undef ALIAS -# endif -#endif - -/* - * End non-standard override functions. - */ -/******************************************************************************/ -/* - * Begin non-standard functions. - */ - -JEMALLOC_ALWAYS_INLINE unsigned -mallocx_tcache_get(int flags) { - if (likely((flags & MALLOCX_TCACHE_MASK) == 0)) { - return TCACHE_IND_AUTOMATIC; - } else if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) { - return TCACHE_IND_NONE; - } else { - return MALLOCX_TCACHE_GET(flags); - } -} - -JEMALLOC_ALWAYS_INLINE unsigned -mallocx_arena_get(int flags) { - if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) { - return MALLOCX_ARENA_GET(flags); - } else { - return ARENA_IND_AUTOMATIC; - } -} - -#ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API - -#define JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y) x ## y -#define JEMALLOC_SMALLOCX_CONCAT_HELPER2(x, y) \ - JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y) - -typedef struct { - void *ptr; - size_t size; -} smallocx_return_t; - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -smallocx_return_t JEMALLOC_NOTHROW -/* - * The attribute JEMALLOC_ATTR(malloc) cannot be used due to: - * - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86488 - */ -JEMALLOC_SMALLOCX_CONCAT_HELPER2(je_smallocx_, JEMALLOC_VERSION_GID_IDENT) - (size_t size, int flags) { - /* - * Note: the attribute JEMALLOC_ALLOC_SIZE(1) cannot be - * used here because it makes writing beyond the `size` - * of the `ptr` undefined behavior, but the objective - * of this function is to allow writing beyond `size` - * up to `smallocx_return_t::size`. - */ - smallocx_return_t ret; - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.smallocx.entry", "size: %zu, flags: %d", size, flags); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.assert_nonempty_alloc = true; - sopts.null_out_result_on_error = true; - sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n"; - sopts.usize = true; - - dopts.result = &ret.ptr; - dopts.num_items = 1; - dopts.item_size = size; - if (unlikely(flags != 0)) { - dopts.alignment = MALLOCX_ALIGN_GET(flags); - dopts.zero = MALLOCX_ZERO_GET(flags); - dopts.tcache_ind = mallocx_tcache_get(flags); - dopts.arena_ind = mallocx_arena_get(flags); - } - - imalloc(&sopts, &dopts); - assert(dopts.usize == je_nallocx(size, flags)); - ret.size = dopts.usize; - - LOG("core.smallocx.exit", "result: %p, size: %zu", ret.ptr, ret.size); - return ret; -} -#undef JEMALLOC_SMALLOCX_CONCAT_HELPER -#undef JEMALLOC_SMALLOCX_CONCAT_HELPER2 -#endif - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1) -je_mallocx(size_t size, int flags) { - void *ret; - static_opts_t sopts; - dynamic_opts_t dopts; - - LOG("core.mallocx.entry", "size: %zu, flags: %d", size, flags); - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.assert_nonempty_alloc = true; - sopts.null_out_result_on_error = true; - sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n"; - - dopts.result = &ret; - dopts.num_items = 1; - dopts.item_size = size; - if (unlikely(flags != 0)) { - dopts.alignment = MALLOCX_ALIGN_GET(flags); - dopts.zero = MALLOCX_ZERO_GET(flags); - dopts.tcache_ind = mallocx_tcache_get(flags); - dopts.arena_ind = mallocx_arena_get(flags); - } - - imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {size, flags}; - hook_invoke_alloc(hook_alloc_mallocx, ret, (uintptr_t)ret, - args); - } - - LOG("core.mallocx.exit", "result: %p", ret); - return ret; -} - -static void * -irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize, - size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena, - prof_tctx_t *tctx, hook_ralloc_args_t *hook_args) { - void *p; - - if (tctx == NULL) { - return NULL; - } - - alignment = prof_sample_align(alignment); - if (usize <= SC_SMALL_MAXCLASS) { - p = iralloct(tsdn, old_ptr, old_usize, - SC_LARGE_MINCLASS, alignment, zero, tcache, - arena, hook_args); - if (p == NULL) { - return NULL; - } - arena_prof_promote(tsdn, p, usize); - } else { - p = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero, - tcache, arena, hook_args); - } - assert(prof_sample_aligned(p)); - - return p; -} - -JEMALLOC_ALWAYS_INLINE void * -irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size, - size_t alignment, size_t usize, bool zero, tcache_t *tcache, - arena_t *arena, emap_alloc_ctx_t *alloc_ctx, - hook_ralloc_args_t *hook_args) { - prof_info_t old_prof_info; - prof_info_get_and_reset_recent(tsd, old_ptr, alloc_ctx, &old_prof_info); - bool prof_active = prof_active_get_unlocked(); - bool sample_event = te_prof_sample_event_lookahead(tsd, usize); - prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event); - void *p; - if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) { - p = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize, - usize, alignment, zero, tcache, arena, tctx, hook_args); - } else { - p = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment, - zero, tcache, arena, hook_args); - } - if (unlikely(p == NULL)) { - prof_alloc_rollback(tsd, tctx); - return NULL; - } - assert(usize == isalloc(tsd_tsdn(tsd), p)); - prof_realloc(tsd, p, size, usize, tctx, prof_active, old_ptr, - old_usize, &old_prof_info, sample_event); - - return p; -} - -static void * -do_rallocx(void *ptr, size_t size, int flags, bool is_realloc) { - void *p; - tsd_t *tsd; - size_t usize; - size_t old_usize; - size_t alignment = MALLOCX_ALIGN_GET(flags); - arena_t *arena; - - assert(ptr != NULL); - assert(size != 0); - assert(malloc_initialized() || IS_INITIALIZER); - tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - - bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true); - - unsigned arena_ind = mallocx_arena_get(flags); - if (arena_get_from_ind(tsd, arena_ind, &arena)) { - goto label_oom; - } - - unsigned tcache_ind = mallocx_tcache_get(flags); - tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, - /* slow */ true, /* is_alloc */ true); - - emap_alloc_ctx_t alloc_ctx; - emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr, - &alloc_ctx); - assert(alloc_ctx.szind != SC_NSIZES); - old_usize = sz_index2size(alloc_ctx.szind); - assert(old_usize == isalloc(tsd_tsdn(tsd), ptr)); - if (aligned_usize_get(size, alignment, &usize, NULL, false)) { - goto label_oom; - } - - hook_ralloc_args_t hook_args = {is_realloc, {(uintptr_t)ptr, size, - flags, 0}}; - if (config_prof && opt_prof) { - p = irallocx_prof(tsd, ptr, old_usize, size, alignment, usize, - zero, tcache, arena, &alloc_ctx, &hook_args); - if (unlikely(p == NULL)) { - goto label_oom; - } - } else { - p = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment, - zero, tcache, arena, &hook_args); - if (unlikely(p == NULL)) { - goto label_oom; - } - assert(usize == isalloc(tsd_tsdn(tsd), p)); - } - assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0)); - thread_alloc_event(tsd, usize); - thread_dalloc_event(tsd, old_usize); - - UTRACE(ptr, size, p); - check_entry_exit_locking(tsd_tsdn(tsd)); - - if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize - && !zero) { - size_t excess_len = usize - old_usize; - void *excess_start = (void *)((uintptr_t)p + old_usize); - junk_alloc_callback(excess_start, excess_len); - } - - return p; -label_oom: - if (config_xmalloc && unlikely(opt_xmalloc)) { - malloc_write("<jemalloc>: Error in rallocx(): out of memory\n"); - abort(); - } - UTRACE(ptr, size, 0); - check_entry_exit_locking(tsd_tsdn(tsd)); - - return NULL; -} - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ALLOC_SIZE(2) -je_rallocx(void *ptr, size_t size, int flags) { - LOG("core.rallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr, - size, flags); - void *ret = do_rallocx(ptr, size, flags, false); - LOG("core.rallocx.exit", "result: %p", ret); - return ret; -} - -static void * -do_realloc_nonnull_zero(void *ptr) { - if (config_stats) { - atomic_fetch_add_zu(&zero_realloc_count, 1, ATOMIC_RELAXED); - } - if (opt_zero_realloc_action == zero_realloc_action_alloc) { - /* - * The user might have gotten an alloc setting while expecting a - * free setting. If that's the case, we at least try to - * reduce the harm, and turn off the tcache while allocating, so - * that we'll get a true first fit. - */ - return do_rallocx(ptr, 1, MALLOCX_TCACHE_NONE, true); - } else if (opt_zero_realloc_action == zero_realloc_action_free) { - UTRACE(ptr, 0, 0); - tsd_t *tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - - tcache_t *tcache = tcache_get_from_ind(tsd, - TCACHE_IND_AUTOMATIC, /* slow */ true, - /* is_alloc */ false); - uintptr_t args[3] = {(uintptr_t)ptr, 0}; - hook_invoke_dalloc(hook_dalloc_realloc, ptr, args); - ifree(tsd, ptr, tcache, true); - - check_entry_exit_locking(tsd_tsdn(tsd)); - return NULL; - } else { - safety_check_fail("Called realloc(non-null-ptr, 0) with " - "zero_realloc:abort set\n"); - /* In real code, this will never run; the safety check failure - * will call abort. In the unit test, we just want to bail out - * without corrupting internal state that the test needs to - * finish. - */ - return NULL; - } -} - -JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN -void JEMALLOC_NOTHROW * -JEMALLOC_ALLOC_SIZE(2) -je_realloc(void *ptr, size_t size) { - LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size); - - if (likely(ptr != NULL && size != 0)) { - void *ret = do_rallocx(ptr, size, 0, true); - LOG("core.realloc.exit", "result: %p", ret); - return ret; - } else if (ptr != NULL && size == 0) { - void *ret = do_realloc_nonnull_zero(ptr); - LOG("core.realloc.exit", "result: %p", ret); - return ret; - } else { - /* realloc(NULL, size) is equivalent to malloc(size). */ - void *ret; - - static_opts_t sopts; - dynamic_opts_t dopts; - - static_opts_init(&sopts); - dynamic_opts_init(&dopts); - - sopts.null_out_result_on_error = true; - sopts.set_errno_on_error = true; - sopts.oom_string = - "<jemalloc>: Error in realloc(): out of memory\n"; - - dopts.result = &ret; - dopts.num_items = 1; - dopts.item_size = size; - - imalloc(&sopts, &dopts); - if (sopts.slow) { - uintptr_t args[3] = {(uintptr_t)ptr, size}; - hook_invoke_alloc(hook_alloc_realloc, ret, - (uintptr_t)ret, args); - } - LOG("core.realloc.exit", "result: %p", ret); - return ret; - } -} - -JEMALLOC_ALWAYS_INLINE size_t -ixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size, - size_t extra, size_t alignment, bool zero) { - size_t newsize; - - if (ixalloc(tsdn, ptr, old_usize, size, extra, alignment, zero, - &newsize)) { - return old_usize; - } - - return newsize; -} - -static size_t -ixallocx_prof_sample(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size, - size_t extra, size_t alignment, bool zero, prof_tctx_t *tctx) { - /* Sampled allocation needs to be page aligned. */ - if (tctx == NULL || !prof_sample_aligned(ptr)) { - return old_usize; - } - - return ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment, - zero); -} - -JEMALLOC_ALWAYS_INLINE size_t -ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size, - size_t extra, size_t alignment, bool zero, emap_alloc_ctx_t *alloc_ctx) { - /* - * old_prof_info is only used for asserting that the profiling info - * isn't changed by the ixalloc() call. - */ - prof_info_t old_prof_info; - prof_info_get(tsd, ptr, alloc_ctx, &old_prof_info); - - /* - * usize isn't knowable before ixalloc() returns when extra is non-zero. - * Therefore, compute its maximum possible value and use that in - * prof_alloc_prep() to decide whether to capture a backtrace. - * prof_realloc() will use the actual usize to decide whether to sample. - */ - size_t usize_max; - if (aligned_usize_get(size + extra, alignment, &usize_max, NULL, - false)) { - /* - * usize_max is out of range, and chances are that allocation - * will fail, but use the maximum possible value and carry on - * with prof_alloc_prep(), just in case allocation succeeds. - */ - usize_max = SC_LARGE_MAXCLASS; - } - bool prof_active = prof_active_get_unlocked(); - bool sample_event = te_prof_sample_event_lookahead(tsd, usize_max); - prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event); - - size_t usize; - if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) { - usize = ixallocx_prof_sample(tsd_tsdn(tsd), ptr, old_usize, - size, extra, alignment, zero, tctx); - } else { - usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size, - extra, alignment, zero); - } - - /* - * At this point we can still safely get the original profiling - * information associated with the ptr, because (a) the edata_t object - * associated with the ptr still lives and (b) the profiling info - * fields are not touched. "(a)" is asserted in the outer je_xallocx() - * function, and "(b)" is indirectly verified below by checking that - * the alloc_tctx field is unchanged. - */ - prof_info_t prof_info; - if (usize == old_usize) { - prof_info_get(tsd, ptr, alloc_ctx, &prof_info); - prof_alloc_rollback(tsd, tctx); - } else { - prof_info_get_and_reset_recent(tsd, ptr, alloc_ctx, &prof_info); - assert(usize <= usize_max); - sample_event = te_prof_sample_event_lookahead(tsd, usize); - prof_realloc(tsd, ptr, size, usize, tctx, prof_active, ptr, - old_usize, &prof_info, sample_event); - } - - assert(old_prof_info.alloc_tctx == prof_info.alloc_tctx); - return usize; -} - -JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW -je_xallocx(void *ptr, size_t size, size_t extra, int flags) { - tsd_t *tsd; - size_t usize, old_usize; - size_t alignment = MALLOCX_ALIGN_GET(flags); - bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true); - - LOG("core.xallocx.entry", "ptr: %p, size: %zu, extra: %zu, " - "flags: %d", ptr, size, extra, flags); - - assert(ptr != NULL); - assert(size != 0); - assert(SIZE_T_MAX - size >= extra); - assert(malloc_initialized() || IS_INITIALIZER); - tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - - /* - * old_edata is only for verifying that xallocx() keeps the edata_t - * object associated with the ptr (though the content of the edata_t - * object can be changed). - */ - edata_t *old_edata = emap_edata_lookup(tsd_tsdn(tsd), - &arena_emap_global, ptr); - - emap_alloc_ctx_t alloc_ctx; - emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr, - &alloc_ctx); - assert(alloc_ctx.szind != SC_NSIZES); - old_usize = sz_index2size(alloc_ctx.szind); - assert(old_usize == isalloc(tsd_tsdn(tsd), ptr)); - /* - * The API explicitly absolves itself of protecting against (size + - * extra) numerical overflow, but we may need to clamp extra to avoid - * exceeding SC_LARGE_MAXCLASS. - * - * Ordinarily, size limit checking is handled deeper down, but here we - * have to check as part of (size + extra) clamping, since we need the - * clamped value in the above helper functions. - */ - if (unlikely(size > SC_LARGE_MAXCLASS)) { - usize = old_usize; - goto label_not_resized; - } - if (unlikely(SC_LARGE_MAXCLASS - size < extra)) { - extra = SC_LARGE_MAXCLASS - size; - } - - if (config_prof && opt_prof) { - usize = ixallocx_prof(tsd, ptr, old_usize, size, extra, - alignment, zero, &alloc_ctx); - } else { - usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size, - extra, alignment, zero); - } - - /* - * xallocx() should keep using the same edata_t object (though its - * content can be changed). - */ - assert(emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr) - == old_edata); - - if (unlikely(usize == old_usize)) { - goto label_not_resized; - } - thread_alloc_event(tsd, usize); - thread_dalloc_event(tsd, old_usize); - - if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize && - !zero) { - size_t excess_len = usize - old_usize; - void *excess_start = (void *)((uintptr_t)ptr + old_usize); - junk_alloc_callback(excess_start, excess_len); - } -label_not_resized: - if (unlikely(!tsd_fast(tsd))) { - uintptr_t args[4] = {(uintptr_t)ptr, size, extra, flags}; - hook_invoke_expand(hook_expand_xallocx, ptr, old_usize, - usize, (uintptr_t)usize, args); - } - - UTRACE(ptr, size, ptr); - check_entry_exit_locking(tsd_tsdn(tsd)); - - LOG("core.xallocx.exit", "result: %zu", usize); - return usize; -} - -JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW -JEMALLOC_ATTR(pure) -je_sallocx(const void *ptr, int flags) { - size_t usize; - tsdn_t *tsdn; - - LOG("core.sallocx.entry", "ptr: %p, flags: %d", ptr, flags); - - assert(malloc_initialized() || IS_INITIALIZER); - assert(ptr != NULL); - - tsdn = tsdn_fetch(); - check_entry_exit_locking(tsdn); - - if (config_debug || force_ivsalloc) { - usize = ivsalloc(tsdn, ptr); - assert(force_ivsalloc || usize != 0); - } else { - usize = isalloc(tsdn, ptr); - } - - check_entry_exit_locking(tsdn); - - LOG("core.sallocx.exit", "result: %zu", usize); - return usize; -} - -JEMALLOC_EXPORT void JEMALLOC_NOTHROW -je_dallocx(void *ptr, int flags) { - LOG("core.dallocx.entry", "ptr: %p, flags: %d", ptr, flags); - - assert(ptr != NULL); - assert(malloc_initialized() || IS_INITIALIZER); - - tsd_t *tsd = tsd_fetch_min(); - bool fast = tsd_fast(tsd); - check_entry_exit_locking(tsd_tsdn(tsd)); - - unsigned tcache_ind = mallocx_tcache_get(flags); - tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast, - /* is_alloc */ false); - - UTRACE(ptr, 0, 0); - if (likely(fast)) { - tsd_assert_fast(tsd); - ifree(tsd, ptr, tcache, false); - } else { - uintptr_t args_raw[3] = {(uintptr_t)ptr, flags}; - hook_invoke_dalloc(hook_dalloc_dallocx, ptr, args_raw); - ifree(tsd, ptr, tcache, true); - } - check_entry_exit_locking(tsd_tsdn(tsd)); - - LOG("core.dallocx.exit", ""); -} - -JEMALLOC_ALWAYS_INLINE size_t -inallocx(tsdn_t *tsdn, size_t size, int flags) { - check_entry_exit_locking(tsdn); - size_t usize; - /* In case of out of range, let the user see it rather than fail. */ - aligned_usize_get(size, MALLOCX_ALIGN_GET(flags), &usize, NULL, false); - check_entry_exit_locking(tsdn); - return usize; -} - -JEMALLOC_NOINLINE void -sdallocx_default(void *ptr, size_t size, int flags) { - assert(ptr != NULL); - assert(malloc_initialized() || IS_INITIALIZER); - - tsd_t *tsd = tsd_fetch_min(); - bool fast = tsd_fast(tsd); - size_t usize = inallocx(tsd_tsdn(tsd), size, flags); - check_entry_exit_locking(tsd_tsdn(tsd)); - - unsigned tcache_ind = mallocx_tcache_get(flags); - tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast, - /* is_alloc */ false); - - UTRACE(ptr, 0, 0); - if (likely(fast)) { - tsd_assert_fast(tsd); - isfree(tsd, ptr, usize, tcache, false); - } else { - uintptr_t args_raw[3] = {(uintptr_t)ptr, size, flags}; - hook_invoke_dalloc(hook_dalloc_sdallocx, ptr, args_raw); - isfree(tsd, ptr, usize, tcache, true); - } - check_entry_exit_locking(tsd_tsdn(tsd)); -} - -JEMALLOC_EXPORT void JEMALLOC_NOTHROW -je_sdallocx(void *ptr, size_t size, int flags) { - LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr, - size, flags); - - if (flags != 0 || !free_fastpath(ptr, size, true)) { - sdallocx_default(ptr, size, flags); - } - - LOG("core.sdallocx.exit", ""); -} - -void JEMALLOC_NOTHROW -je_sdallocx_noflags(void *ptr, size_t size) { - LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: 0", ptr, - size); - - if (!free_fastpath(ptr, size, true)) { - sdallocx_default(ptr, size, 0); - } - - LOG("core.sdallocx.exit", ""); -} - -JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW -JEMALLOC_ATTR(pure) -je_nallocx(size_t size, int flags) { - size_t usize; - tsdn_t *tsdn; - - assert(size != 0); - - if (unlikely(malloc_init())) { - LOG("core.nallocx.exit", "result: %zu", ZU(0)); - return 0; - } - - tsdn = tsdn_fetch(); - check_entry_exit_locking(tsdn); - - usize = inallocx(tsdn, size, flags); - if (unlikely(usize > SC_LARGE_MAXCLASS)) { - LOG("core.nallocx.exit", "result: %zu", ZU(0)); - return 0; - } - - check_entry_exit_locking(tsdn); - LOG("core.nallocx.exit", "result: %zu", usize); - return usize; -} - -JEMALLOC_EXPORT int JEMALLOC_NOTHROW -je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) { - int ret; - tsd_t *tsd; - - LOG("core.mallctl.entry", "name: %s", name); - - if (unlikely(malloc_init())) { - LOG("core.mallctl.exit", "result: %d", EAGAIN); - return EAGAIN; - } - - tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - ret = ctl_byname(tsd, name, oldp, oldlenp, newp, newlen); - check_entry_exit_locking(tsd_tsdn(tsd)); - - LOG("core.mallctl.exit", "result: %d", ret); - return ret; -} - -JEMALLOC_EXPORT int JEMALLOC_NOTHROW -je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) { - int ret; - - LOG("core.mallctlnametomib.entry", "name: %s", name); - - if (unlikely(malloc_init())) { - LOG("core.mallctlnametomib.exit", "result: %d", EAGAIN); - return EAGAIN; - } - - tsd_t *tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - ret = ctl_nametomib(tsd, name, mibp, miblenp); - check_entry_exit_locking(tsd_tsdn(tsd)); - - LOG("core.mallctlnametomib.exit", "result: %d", ret); - return ret; -} - -JEMALLOC_EXPORT int JEMALLOC_NOTHROW -je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) { - int ret; - tsd_t *tsd; - - LOG("core.mallctlbymib.entry", ""); - - if (unlikely(malloc_init())) { - LOG("core.mallctlbymib.exit", "result: %d", EAGAIN); - return EAGAIN; - } - - tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - ret = ctl_bymib(tsd, mib, miblen, oldp, oldlenp, newp, newlen); - check_entry_exit_locking(tsd_tsdn(tsd)); - LOG("core.mallctlbymib.exit", "result: %d", ret); - return ret; -} - -#define STATS_PRINT_BUFSIZE 65536 -JEMALLOC_EXPORT void JEMALLOC_NOTHROW -je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, - const char *opts) { - tsdn_t *tsdn; - - LOG("core.malloc_stats_print.entry", ""); - - tsdn = tsdn_fetch(); - check_entry_exit_locking(tsdn); - - if (config_debug) { - stats_print(write_cb, cbopaque, opts); - } else { - buf_writer_t buf_writer; - buf_writer_init(tsdn, &buf_writer, write_cb, cbopaque, NULL, - STATS_PRINT_BUFSIZE); - stats_print(buf_writer_cb, &buf_writer, opts); - buf_writer_terminate(tsdn, &buf_writer); - } - - check_entry_exit_locking(tsdn); - LOG("core.malloc_stats_print.exit", ""); -} -#undef STATS_PRINT_BUFSIZE - -JEMALLOC_ALWAYS_INLINE size_t -je_malloc_usable_size_impl(JEMALLOC_USABLE_SIZE_CONST void *ptr) { - assert(malloc_initialized() || IS_INITIALIZER); - - tsdn_t *tsdn = tsdn_fetch(); - check_entry_exit_locking(tsdn); - - size_t ret; - if (unlikely(ptr == NULL)) { - ret = 0; - } else { - if (config_debug || force_ivsalloc) { - ret = ivsalloc(tsdn, ptr); - assert(force_ivsalloc || ret != 0); - } else { - ret = isalloc(tsdn, ptr); - } - } - check_entry_exit_locking(tsdn); - - return ret; -} - -JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW -je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) { - LOG("core.malloc_usable_size.entry", "ptr: %p", ptr); - - size_t ret = je_malloc_usable_size_impl(ptr); - - LOG("core.malloc_usable_size.exit", "result: %zu", ret); - return ret; -} - -#ifdef JEMALLOC_HAVE_MALLOC_SIZE -JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW -je_malloc_size(const void *ptr) { - LOG("core.malloc_size.entry", "ptr: %p", ptr); - - size_t ret = je_malloc_usable_size_impl(ptr); - - LOG("core.malloc_size.exit", "result: %zu", ret); - return ret; -} -#endif - -static void -batch_alloc_prof_sample_assert(tsd_t *tsd, size_t batch, size_t usize) { - assert(config_prof && opt_prof); - bool prof_sample_event = te_prof_sample_event_lookahead(tsd, - batch * usize); - assert(!prof_sample_event); - size_t surplus; - prof_sample_event = te_prof_sample_event_lookahead_surplus(tsd, - (batch + 1) * usize, &surplus); - assert(prof_sample_event); - assert(surplus < usize); -} - -size_t -batch_alloc(void **ptrs, size_t num, size_t size, int flags) { - LOG("core.batch_alloc.entry", - "ptrs: %p, num: %zu, size: %zu, flags: %d", ptrs, num, size, flags); - - tsd_t *tsd = tsd_fetch(); - check_entry_exit_locking(tsd_tsdn(tsd)); - - size_t filled = 0; - - if (unlikely(tsd == NULL || tsd_reentrancy_level_get(tsd) > 0)) { - goto label_done; - } - - size_t alignment = MALLOCX_ALIGN_GET(flags); - size_t usize; - if (aligned_usize_get(size, alignment, &usize, NULL, false)) { - goto label_done; - } - szind_t ind = sz_size2index(usize); - bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true); - - /* - * The cache bin and arena will be lazily initialized; it's hard to - * know in advance whether each of them needs to be initialized. - */ - cache_bin_t *bin = NULL; - arena_t *arena = NULL; - - size_t nregs = 0; - if (likely(ind < SC_NBINS)) { - nregs = bin_infos[ind].nregs; - assert(nregs > 0); - } - - while (filled < num) { - size_t batch = num - filled; - size_t surplus = SIZE_MAX; /* Dead store. */ - bool prof_sample_event = config_prof && opt_prof - && prof_active_get_unlocked() - && te_prof_sample_event_lookahead_surplus(tsd, - batch * usize, &surplus); - - if (prof_sample_event) { - /* - * Adjust so that the batch does not trigger prof - * sampling. - */ - batch -= surplus / usize + 1; - batch_alloc_prof_sample_assert(tsd, batch, usize); - } - - size_t progress = 0; - - if (likely(ind < SC_NBINS) && batch >= nregs) { - if (arena == NULL) { - unsigned arena_ind = mallocx_arena_get(flags); - if (arena_get_from_ind(tsd, arena_ind, - &arena)) { - goto label_done; - } - if (arena == NULL) { - arena = arena_choose(tsd, NULL); - } - if (unlikely(arena == NULL)) { - goto label_done; - } - } - size_t arena_batch = batch - batch % nregs; - size_t n = arena_fill_small_fresh(tsd_tsdn(tsd), arena, - ind, ptrs + filled, arena_batch, zero); - progress += n; - filled += n; - } - - if (likely(ind < nhbins) && progress < batch) { - if (bin == NULL) { - unsigned tcache_ind = mallocx_tcache_get(flags); - tcache_t *tcache = tcache_get_from_ind(tsd, - tcache_ind, /* slow */ true, - /* is_alloc */ true); - if (tcache != NULL) { - bin = &tcache->bins[ind]; - } - } - /* - * If we don't have a tcache bin, we don't want to - * immediately give up, because there's the possibility - * that the user explicitly requested to bypass the - * tcache, or that the user explicitly turned off the - * tcache; in such cases, we go through the slow path, - * i.e. the mallocx() call at the end of the while loop. - */ - if (bin != NULL) { - size_t bin_batch = batch - progress; - /* - * n can be less than bin_batch, meaning that - * the cache bin does not have enough memory. - * In such cases, we rely on the slow path, - * i.e. the mallocx() call at the end of the - * while loop, to fill in the cache, and in the - * next iteration of the while loop, the tcache - * will contain a lot of memory, and we can - * harvest them here. Compared to the - * alternative approach where we directly go to - * the arena bins here, the overhead of our - * current approach should usually be minimal, - * since we never try to fetch more memory than - * what a slab contains via the tcache. An - * additional benefit is that the tcache will - * not be empty for the next allocation request. - */ - size_t n = cache_bin_alloc_batch(bin, bin_batch, - ptrs + filled); - if (config_stats) { - bin->tstats.nrequests += n; - } - if (zero) { - for (size_t i = 0; i < n; ++i) { - memset(ptrs[filled + i], 0, - usize); - } - } - if (config_prof && opt_prof - && unlikely(ind >= SC_NBINS)) { - for (size_t i = 0; i < n; ++i) { - prof_tctx_reset_sampled(tsd, - ptrs[filled + i]); - } - } - progress += n; - filled += n; - } - } - - /* - * For thread events other than prof sampling, trigger them as - * if there's a single allocation of size (n * usize). This is - * fine because: - * (a) these events do not alter the allocation itself, and - * (b) it's possible that some event would have been triggered - * multiple times, instead of only once, if the allocations - * were handled individually, but it would do no harm (or - * even be beneficial) to coalesce the triggerings. - */ - thread_alloc_event(tsd, progress * usize); - - if (progress < batch || prof_sample_event) { - void *p = je_mallocx(size, flags); - if (p == NULL) { /* OOM */ - break; - } - if (progress == batch) { - assert(prof_sampled(tsd, p)); - } - ptrs[filled++] = p; - } - } - -label_done: - check_entry_exit_locking(tsd_tsdn(tsd)); - LOG("core.batch_alloc.exit", "result: %zu", filled); - return filled; -} - -/* - * End non-standard functions. - */ -/******************************************************************************/ -/* - * The following functions are used by threading libraries for protection of - * malloc during fork(). - */ - -/* - * If an application creates a thread before doing any allocation in the main - * thread, then calls fork(2) in the main thread followed by memory allocation - * in the child process, a race can occur that results in deadlock within the - * child: the main thread may have forked while the created thread had - * partially initialized the allocator. Ordinarily jemalloc prevents - * fork/malloc races via the following functions it registers during - * initialization using pthread_atfork(), but of course that does no good if - * the allocator isn't fully initialized at fork time. The following library - * constructor is a partial solution to this problem. It may still be possible - * to trigger the deadlock described above, but doing so would involve forking - * via a library constructor that runs before jemalloc's runs. - */ -#ifndef JEMALLOC_JET -JEMALLOC_ATTR(constructor) -static void -jemalloc_constructor(void) { - malloc_init(); -} -#endif - -#ifndef JEMALLOC_MUTEX_INIT_CB -void -jemalloc_prefork(void) -#else -JEMALLOC_EXPORT void -_malloc_prefork(void) -#endif -{ - tsd_t *tsd; - unsigned i, j, narenas; - arena_t *arena; - -#ifdef JEMALLOC_MUTEX_INIT_CB - if (!malloc_initialized()) { - return; - } -#endif - assert(malloc_initialized()); - - tsd = tsd_fetch(); - - narenas = narenas_total_get(); - - witness_prefork(tsd_witness_tsdp_get(tsd)); - /* Acquire all mutexes in a safe order. */ - ctl_prefork(tsd_tsdn(tsd)); - tcache_prefork(tsd_tsdn(tsd)); - malloc_mutex_prefork(tsd_tsdn(tsd), &arenas_lock); - if (have_background_thread) { - background_thread_prefork0(tsd_tsdn(tsd)); - } - prof_prefork0(tsd_tsdn(tsd)); - if (have_background_thread) { - background_thread_prefork1(tsd_tsdn(tsd)); - } - /* Break arena prefork into stages to preserve lock order. */ - for (i = 0; i < 9; i++) { - for (j = 0; j < narenas; j++) { - if ((arena = arena_get(tsd_tsdn(tsd), j, false)) != - NULL) { - switch (i) { - case 0: - arena_prefork0(tsd_tsdn(tsd), arena); - break; - case 1: - arena_prefork1(tsd_tsdn(tsd), arena); - break; - case 2: - arena_prefork2(tsd_tsdn(tsd), arena); - break; - case 3: - arena_prefork3(tsd_tsdn(tsd), arena); - break; - case 4: - arena_prefork4(tsd_tsdn(tsd), arena); - break; - case 5: - arena_prefork5(tsd_tsdn(tsd), arena); - break; - case 6: - arena_prefork6(tsd_tsdn(tsd), arena); - break; - case 7: - arena_prefork7(tsd_tsdn(tsd), arena); - break; - case 8: - arena_prefork8(tsd_tsdn(tsd), arena); - break; - default: not_reached(); - } - } - } - - } - prof_prefork1(tsd_tsdn(tsd)); - stats_prefork(tsd_tsdn(tsd)); - tsd_prefork(tsd); -} - -#ifndef JEMALLOC_MUTEX_INIT_CB -void -jemalloc_postfork_parent(void) -#else -JEMALLOC_EXPORT void -_malloc_postfork(void) -#endif -{ - tsd_t *tsd; - unsigned i, narenas; - -#ifdef JEMALLOC_MUTEX_INIT_CB - if (!malloc_initialized()) { - return; - } -#endif - assert(malloc_initialized()); - - tsd = tsd_fetch(); - - tsd_postfork_parent(tsd); - - witness_postfork_parent(tsd_witness_tsdp_get(tsd)); - /* Release all mutexes, now that fork() has completed. */ - stats_postfork_parent(tsd_tsdn(tsd)); - for (i = 0, narenas = narenas_total_get(); i < narenas; i++) { - arena_t *arena; - - if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) { - arena_postfork_parent(tsd_tsdn(tsd), arena); - } - } - prof_postfork_parent(tsd_tsdn(tsd)); - if (have_background_thread) { - background_thread_postfork_parent(tsd_tsdn(tsd)); - } - malloc_mutex_postfork_parent(tsd_tsdn(tsd), &arenas_lock); - tcache_postfork_parent(tsd_tsdn(tsd)); - ctl_postfork_parent(tsd_tsdn(tsd)); -} - -void -jemalloc_postfork_child(void) { - tsd_t *tsd; - unsigned i, narenas; - - assert(malloc_initialized()); - - tsd = tsd_fetch(); - - tsd_postfork_child(tsd); - - witness_postfork_child(tsd_witness_tsdp_get(tsd)); - /* Release all mutexes, now that fork() has completed. */ - stats_postfork_child(tsd_tsdn(tsd)); - for (i = 0, narenas = narenas_total_get(); i < narenas; i++) { - arena_t *arena; - - if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) { - arena_postfork_child(tsd_tsdn(tsd), arena); - } - } - prof_postfork_child(tsd_tsdn(tsd)); - if (have_background_thread) { - background_thread_postfork_child(tsd_tsdn(tsd)); - } - malloc_mutex_postfork_child(tsd_tsdn(tsd), &arenas_lock); - tcache_postfork_child(tsd_tsdn(tsd)); - ctl_postfork_child(tsd_tsdn(tsd)); -} - -/******************************************************************************/ diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/jemalloc_cpp.cpp b/fluent-bit/lib/jemalloc-5.3.0/src/jemalloc_cpp.cpp deleted file mode 100644 index 451655f1..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/jemalloc_cpp.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include <mutex> -#include <new> - -#define JEMALLOC_CPP_CPP_ -#ifdef __cplusplus -extern "C" { -#endif - -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#ifdef __cplusplus -} -#endif - -// All operators in this file are exported. - -// Possibly alias hidden versions of malloc and sdallocx to avoid an extra plt -// thunk? -// -// extern __typeof (sdallocx) sdallocx_int -// __attribute ((alias ("sdallocx"), -// visibility ("hidden"))); -// -// ... but it needs to work with jemalloc namespaces. - -void *operator new(std::size_t size); -void *operator new[](std::size_t size); -void *operator new(std::size_t size, const std::nothrow_t &) noexcept; -void *operator new[](std::size_t size, const std::nothrow_t &) noexcept; -void operator delete(void *ptr) noexcept; -void operator delete[](void *ptr) noexcept; -void operator delete(void *ptr, const std::nothrow_t &) noexcept; -void operator delete[](void *ptr, const std::nothrow_t &) noexcept; - -#if __cpp_sized_deallocation >= 201309 -/* C++14's sized-delete operators. */ -void operator delete(void *ptr, std::size_t size) noexcept; -void operator delete[](void *ptr, std::size_t size) noexcept; -#endif - -#if __cpp_aligned_new >= 201606 -/* C++17's over-aligned operators. */ -void *operator new(std::size_t size, std::align_val_t); -void *operator new(std::size_t size, std::align_val_t, const std::nothrow_t &) noexcept; -void *operator new[](std::size_t size, std::align_val_t); -void *operator new[](std::size_t size, std::align_val_t, const std::nothrow_t &) noexcept; -void operator delete(void* ptr, std::align_val_t) noexcept; -void operator delete(void* ptr, std::align_val_t, const std::nothrow_t &) noexcept; -void operator delete(void* ptr, std::size_t size, std::align_val_t al) noexcept; -void operator delete[](void* ptr, std::align_val_t) noexcept; -void operator delete[](void* ptr, std::align_val_t, const std::nothrow_t &) noexcept; -void operator delete[](void* ptr, std::size_t size, std::align_val_t al) noexcept; -#endif - -JEMALLOC_NOINLINE -static void * -handleOOM(std::size_t size, bool nothrow) { - if (opt_experimental_infallible_new) { - safety_check_fail("<jemalloc>: Allocation failed and " - "opt.experimental_infallible_new is true. Aborting.\n"); - return nullptr; - } - - void *ptr = nullptr; - - while (ptr == nullptr) { - std::new_handler handler; - // GCC-4.8 and clang 4.0 do not have std::get_new_handler. - { - static std::mutex mtx; - std::lock_guard<std::mutex> lock(mtx); - - handler = std::set_new_handler(nullptr); - std::set_new_handler(handler); - } - if (handler == nullptr) - break; - - try { - handler(); - } catch (const std::bad_alloc &) { - break; - } - - ptr = je_malloc(size); - } - - if (ptr == nullptr && !nothrow) - std::__throw_bad_alloc(); - return ptr; -} - -template <bool IsNoExcept> -JEMALLOC_NOINLINE -static void * -fallback_impl(std::size_t size) noexcept(IsNoExcept) { - void *ptr = malloc_default(size); - if (likely(ptr != nullptr)) { - return ptr; - } - return handleOOM(size, IsNoExcept); -} - -template <bool IsNoExcept> -JEMALLOC_ALWAYS_INLINE -void * -newImpl(std::size_t size) noexcept(IsNoExcept) { - return imalloc_fastpath(size, &fallback_impl<IsNoExcept>); -} - -void * -operator new(std::size_t size) { - return newImpl<false>(size); -} - -void * -operator new[](std::size_t size) { - return newImpl<false>(size); -} - -void * -operator new(std::size_t size, const std::nothrow_t &) noexcept { - return newImpl<true>(size); -} - -void * -operator new[](std::size_t size, const std::nothrow_t &) noexcept { - return newImpl<true>(size); -} - -#if __cpp_aligned_new >= 201606 - -template <bool IsNoExcept> -JEMALLOC_ALWAYS_INLINE -void * -alignedNewImpl(std::size_t size, std::align_val_t alignment) noexcept(IsNoExcept) { - void *ptr = je_aligned_alloc(static_cast<std::size_t>(alignment), size); - if (likely(ptr != nullptr)) { - return ptr; - } - - return handleOOM(size, IsNoExcept); -} - -void * -operator new(std::size_t size, std::align_val_t alignment) { - return alignedNewImpl<false>(size, alignment); -} - -void * -operator new[](std::size_t size, std::align_val_t alignment) { - return alignedNewImpl<false>(size, alignment); -} - -void * -operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept { - return alignedNewImpl<true>(size, alignment); -} - -void * -operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept { - return alignedNewImpl<true>(size, alignment); -} - -#endif // __cpp_aligned_new - -void -operator delete(void *ptr) noexcept { - je_free(ptr); -} - -void -operator delete[](void *ptr) noexcept { - je_free(ptr); -} - -void -operator delete(void *ptr, const std::nothrow_t &) noexcept { - je_free(ptr); -} - -void operator delete[](void *ptr, const std::nothrow_t &) noexcept { - je_free(ptr); -} - -#if __cpp_sized_deallocation >= 201309 - -JEMALLOC_ALWAYS_INLINE -void -sizedDeleteImpl(void* ptr, std::size_t size) noexcept { - if (unlikely(ptr == nullptr)) { - return; - } - je_sdallocx_noflags(ptr, size); -} - -void -operator delete(void *ptr, std::size_t size) noexcept { - sizedDeleteImpl(ptr, size); -} - -void -operator delete[](void *ptr, std::size_t size) noexcept { - sizedDeleteImpl(ptr, size); -} - -#endif // __cpp_sized_deallocation - -#if __cpp_aligned_new >= 201606 - -JEMALLOC_ALWAYS_INLINE -void -alignedSizedDeleteImpl(void* ptr, std::size_t size, std::align_val_t alignment) noexcept { - if (config_debug) { - assert(((size_t)alignment & ((size_t)alignment - 1)) == 0); - } - if (unlikely(ptr == nullptr)) { - return; - } - je_sdallocx(ptr, size, MALLOCX_ALIGN(alignment)); -} - -void -operator delete(void* ptr, std::align_val_t) noexcept { - je_free(ptr); -} - -void -operator delete[](void* ptr, std::align_val_t) noexcept { - je_free(ptr); -} - -void -operator delete(void* ptr, std::align_val_t, const std::nothrow_t&) noexcept { - je_free(ptr); -} - -void -operator delete[](void* ptr, std::align_val_t, const std::nothrow_t&) noexcept { - je_free(ptr); -} - -void -operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept { - alignedSizedDeleteImpl(ptr, size, alignment); -} - -void -operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept { - alignedSizedDeleteImpl(ptr, size, alignment); -} - -#endif // __cpp_aligned_new diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/large.c b/fluent-bit/lib/jemalloc-5.3.0/src/large.c deleted file mode 100644 index 5fc4bf58..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/large.c +++ /dev/null @@ -1,322 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/emap.h" -#include "jemalloc/internal/extent_mmap.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/prof_recent.h" -#include "jemalloc/internal/util.h" - -/******************************************************************************/ - -void * -large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero) { - assert(usize == sz_s2u(usize)); - - return large_palloc(tsdn, arena, usize, CACHELINE, zero); -} - -void * -large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment, - bool zero) { - size_t ausize; - edata_t *edata; - UNUSED bool idump JEMALLOC_CC_SILENCE_INIT(false); - - assert(!tsdn_null(tsdn) || arena != NULL); - - ausize = sz_sa2u(usize, alignment); - if (unlikely(ausize == 0 || ausize > SC_LARGE_MAXCLASS)) { - return NULL; - } - - if (likely(!tsdn_null(tsdn))) { - arena = arena_choose_maybe_huge(tsdn_tsd(tsdn), arena, usize); - } - if (unlikely(arena == NULL) || (edata = arena_extent_alloc_large(tsdn, - arena, usize, alignment, zero)) == NULL) { - return NULL; - } - - /* See comments in arena_bin_slabs_full_insert(). */ - if (!arena_is_auto(arena)) { - /* Insert edata into large. */ - malloc_mutex_lock(tsdn, &arena->large_mtx); - edata_list_active_append(&arena->large, edata); - malloc_mutex_unlock(tsdn, &arena->large_mtx); - } - - arena_decay_tick(tsdn, arena); - return edata_addr_get(edata); -} - -static bool -large_ralloc_no_move_shrink(tsdn_t *tsdn, edata_t *edata, size_t usize) { - arena_t *arena = arena_get_from_edata(edata); - ehooks_t *ehooks = arena_get_ehooks(arena); - size_t old_size = edata_size_get(edata); - size_t old_usize = edata_usize_get(edata); - - assert(old_usize > usize); - - if (ehooks_split_will_fail(ehooks)) { - return true; - } - - bool deferred_work_generated = false; - bool err = pa_shrink(tsdn, &arena->pa_shard, edata, old_size, - usize + sz_large_pad, sz_size2index(usize), - &deferred_work_generated); - if (err) { - return true; - } - if (deferred_work_generated) { - arena_handle_deferred_work(tsdn, arena); - } - arena_extent_ralloc_large_shrink(tsdn, arena, edata, old_usize); - - return false; -} - -static bool -large_ralloc_no_move_expand(tsdn_t *tsdn, edata_t *edata, size_t usize, - bool zero) { - arena_t *arena = arena_get_from_edata(edata); - - size_t old_size = edata_size_get(edata); - size_t old_usize = edata_usize_get(edata); - size_t new_size = usize + sz_large_pad; - - szind_t szind = sz_size2index(usize); - - bool deferred_work_generated = false; - bool err = pa_expand(tsdn, &arena->pa_shard, edata, old_size, new_size, - szind, zero, &deferred_work_generated); - - if (deferred_work_generated) { - arena_handle_deferred_work(tsdn, arena); - } - - if (err) { - return true; - } - - if (zero) { - if (opt_cache_oblivious) { - assert(sz_large_pad == PAGE); - /* - * Zero the trailing bytes of the original allocation's - * last page, since they are in an indeterminate state. - * There will always be trailing bytes, because ptr's - * offset from the beginning of the extent is a multiple - * of CACHELINE in [0 .. PAGE). - */ - void *zbase = (void *) - ((uintptr_t)edata_addr_get(edata) + old_usize); - void *zpast = PAGE_ADDR2BASE((void *)((uintptr_t)zbase + - PAGE)); - size_t nzero = (uintptr_t)zpast - (uintptr_t)zbase; - assert(nzero > 0); - memset(zbase, 0, nzero); - } - } - arena_extent_ralloc_large_expand(tsdn, arena, edata, old_usize); - - return false; -} - -bool -large_ralloc_no_move(tsdn_t *tsdn, edata_t *edata, size_t usize_min, - size_t usize_max, bool zero) { - size_t oldusize = edata_usize_get(edata); - - /* The following should have been caught by callers. */ - assert(usize_min > 0 && usize_max <= SC_LARGE_MAXCLASS); - /* Both allocation sizes must be large to avoid a move. */ - assert(oldusize >= SC_LARGE_MINCLASS - && usize_max >= SC_LARGE_MINCLASS); - - if (usize_max > oldusize) { - /* Attempt to expand the allocation in-place. */ - if (!large_ralloc_no_move_expand(tsdn, edata, usize_max, - zero)) { - arena_decay_tick(tsdn, arena_get_from_edata(edata)); - return false; - } - /* Try again, this time with usize_min. */ - if (usize_min < usize_max && usize_min > oldusize && - large_ralloc_no_move_expand(tsdn, edata, usize_min, zero)) { - arena_decay_tick(tsdn, arena_get_from_edata(edata)); - return false; - } - } - - /* - * Avoid moving the allocation if the existing extent size accommodates - * the new size. - */ - if (oldusize >= usize_min && oldusize <= usize_max) { - arena_decay_tick(tsdn, arena_get_from_edata(edata)); - return false; - } - - /* Attempt to shrink the allocation in-place. */ - if (oldusize > usize_max) { - if (!large_ralloc_no_move_shrink(tsdn, edata, usize_max)) { - arena_decay_tick(tsdn, arena_get_from_edata(edata)); - return false; - } - } - return true; -} - -static void * -large_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize, - size_t alignment, bool zero) { - if (alignment <= CACHELINE) { - return large_malloc(tsdn, arena, usize, zero); - } - return large_palloc(tsdn, arena, usize, alignment, zero); -} - -void * -large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize, - size_t alignment, bool zero, tcache_t *tcache, - hook_ralloc_args_t *hook_args) { - edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); - - size_t oldusize = edata_usize_get(edata); - /* The following should have been caught by callers. */ - assert(usize > 0 && usize <= SC_LARGE_MAXCLASS); - /* Both allocation sizes must be large to avoid a move. */ - assert(oldusize >= SC_LARGE_MINCLASS - && usize >= SC_LARGE_MINCLASS); - - /* Try to avoid moving the allocation. */ - if (!large_ralloc_no_move(tsdn, edata, usize, usize, zero)) { - hook_invoke_expand(hook_args->is_realloc - ? hook_expand_realloc : hook_expand_rallocx, ptr, oldusize, - usize, (uintptr_t)ptr, hook_args->args); - return edata_addr_get(edata); - } - - /* - * usize and old size are different enough that we need to use a - * different size class. In that case, fall back to allocating new - * space and copying. - */ - void *ret = large_ralloc_move_helper(tsdn, arena, usize, alignment, - zero); - if (ret == NULL) { - return NULL; - } - - hook_invoke_alloc(hook_args->is_realloc - ? hook_alloc_realloc : hook_alloc_rallocx, ret, (uintptr_t)ret, - hook_args->args); - hook_invoke_dalloc(hook_args->is_realloc - ? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args); - - size_t copysize = (usize < oldusize) ? usize : oldusize; - memcpy(ret, edata_addr_get(edata), copysize); - isdalloct(tsdn, edata_addr_get(edata), oldusize, tcache, NULL, true); - return ret; -} - -/* - * locked indicates whether the arena's large_mtx is currently held. - */ -static void -large_dalloc_prep_impl(tsdn_t *tsdn, arena_t *arena, edata_t *edata, - bool locked) { - if (!locked) { - /* See comments in arena_bin_slabs_full_insert(). */ - if (!arena_is_auto(arena)) { - malloc_mutex_lock(tsdn, &arena->large_mtx); - edata_list_active_remove(&arena->large, edata); - malloc_mutex_unlock(tsdn, &arena->large_mtx); - } - } else { - /* Only hold the large_mtx if necessary. */ - if (!arena_is_auto(arena)) { - malloc_mutex_assert_owner(tsdn, &arena->large_mtx); - edata_list_active_remove(&arena->large, edata); - } - } - arena_extent_dalloc_large_prep(tsdn, arena, edata); -} - -static void -large_dalloc_finish_impl(tsdn_t *tsdn, arena_t *arena, edata_t *edata) { - bool deferred_work_generated = false; - pa_dalloc(tsdn, &arena->pa_shard, edata, &deferred_work_generated); - if (deferred_work_generated) { - arena_handle_deferred_work(tsdn, arena); - } -} - -void -large_dalloc_prep_locked(tsdn_t *tsdn, edata_t *edata) { - large_dalloc_prep_impl(tsdn, arena_get_from_edata(edata), edata, true); -} - -void -large_dalloc_finish(tsdn_t *tsdn, edata_t *edata) { - large_dalloc_finish_impl(tsdn, arena_get_from_edata(edata), edata); -} - -void -large_dalloc(tsdn_t *tsdn, edata_t *edata) { - arena_t *arena = arena_get_from_edata(edata); - large_dalloc_prep_impl(tsdn, arena, edata, false); - large_dalloc_finish_impl(tsdn, arena, edata); - arena_decay_tick(tsdn, arena); -} - -size_t -large_salloc(tsdn_t *tsdn, const edata_t *edata) { - return edata_usize_get(edata); -} - -void -large_prof_info_get(tsd_t *tsd, edata_t *edata, prof_info_t *prof_info, - bool reset_recent) { - assert(prof_info != NULL); - - prof_tctx_t *alloc_tctx = edata_prof_tctx_get(edata); - prof_info->alloc_tctx = alloc_tctx; - - if ((uintptr_t)alloc_tctx > (uintptr_t)1U) { - nstime_copy(&prof_info->alloc_time, - edata_prof_alloc_time_get(edata)); - prof_info->alloc_size = edata_prof_alloc_size_get(edata); - if (reset_recent) { - /* - * Reset the pointer on the recent allocation record, - * so that this allocation is recorded as released. - */ - prof_recent_alloc_reset(tsd, edata); - } - } -} - -static void -large_prof_tctx_set(edata_t *edata, prof_tctx_t *tctx) { - edata_prof_tctx_set(edata, tctx); -} - -void -large_prof_tctx_reset(edata_t *edata) { - large_prof_tctx_set(edata, (prof_tctx_t *)(uintptr_t)1U); -} - -void -large_prof_info_set(edata_t *edata, prof_tctx_t *tctx, size_t size) { - nstime_t t; - nstime_prof_init_update(&t); - edata_prof_alloc_time_set(edata, &t); - edata_prof_alloc_size_set(edata, size); - edata_prof_recent_alloc_init(edata); - large_prof_tctx_set(edata, tctx); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/log.c b/fluent-bit/lib/jemalloc-5.3.0/src/log.c deleted file mode 100644 index 778902fb..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/log.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/log.h" - -char log_var_names[JEMALLOC_LOG_VAR_BUFSIZE]; -atomic_b_t log_init_done = ATOMIC_INIT(false); - -/* - * Returns true if we were able to pick out a segment. Fills in r_segment_end - * with a pointer to the first character after the end of the string. - */ -static const char * -log_var_extract_segment(const char* segment_begin) { - const char *end; - for (end = segment_begin; *end != '\0' && *end != '|'; end++) { - } - return end; -} - -static bool -log_var_matches_segment(const char *segment_begin, const char *segment_end, - const char *log_var_begin, const char *log_var_end) { - assert(segment_begin <= segment_end); - assert(log_var_begin < log_var_end); - - ptrdiff_t segment_len = segment_end - segment_begin; - ptrdiff_t log_var_len = log_var_end - log_var_begin; - /* The special '.' segment matches everything. */ - if (segment_len == 1 && *segment_begin == '.') { - return true; - } - if (segment_len == log_var_len) { - return strncmp(segment_begin, log_var_begin, segment_len) == 0; - } else if (segment_len < log_var_len) { - return strncmp(segment_begin, log_var_begin, segment_len) == 0 - && log_var_begin[segment_len] == '.'; - } else { - return false; - } -} - -unsigned -log_var_update_state(log_var_t *log_var) { - const char *log_var_begin = log_var->name; - const char *log_var_end = log_var->name + strlen(log_var->name); - - /* Pointer to one before the beginning of the current segment. */ - const char *segment_begin = log_var_names; - - /* - * If log_init done is false, we haven't parsed the malloc conf yet. To - * avoid log-spew, we default to not displaying anything. - */ - if (!atomic_load_b(&log_init_done, ATOMIC_ACQUIRE)) { - return LOG_INITIALIZED_NOT_ENABLED; - } - - while (true) { - const char *segment_end = log_var_extract_segment( - segment_begin); - assert(segment_end < log_var_names + JEMALLOC_LOG_VAR_BUFSIZE); - if (log_var_matches_segment(segment_begin, segment_end, - log_var_begin, log_var_end)) { - atomic_store_u(&log_var->state, LOG_ENABLED, - ATOMIC_RELAXED); - return LOG_ENABLED; - } - if (*segment_end == '\0') { - /* Hit the end of the segment string with no match. */ - atomic_store_u(&log_var->state, - LOG_INITIALIZED_NOT_ENABLED, ATOMIC_RELAXED); - return LOG_INITIALIZED_NOT_ENABLED; - } - /* Otherwise, skip the delimiter and continue. */ - segment_begin = segment_end + 1; - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/malloc_io.c b/fluent-bit/lib/jemalloc-5.3.0/src/malloc_io.c deleted file mode 100644 index b76885cb..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/malloc_io.c +++ /dev/null @@ -1,697 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/malloc_io.h" -#include "jemalloc/internal/util.h" - -#ifdef assert -# undef assert -#endif -#ifdef not_reached -# undef not_reached -#endif -#ifdef not_implemented -# undef not_implemented -#endif -#ifdef assert_not_implemented -# undef assert_not_implemented -#endif - -/* - * Define simple versions of assertion macros that won't recurse in case - * of assertion failures in malloc_*printf(). - */ -#define assert(e) do { \ - if (config_debug && !(e)) { \ - malloc_write("<jemalloc>: Failed assertion\n"); \ - abort(); \ - } \ -} while (0) - -#define not_reached() do { \ - if (config_debug) { \ - malloc_write("<jemalloc>: Unreachable code reached\n"); \ - abort(); \ - } \ - unreachable(); \ -} while (0) - -#define not_implemented() do { \ - if (config_debug) { \ - malloc_write("<jemalloc>: Not implemented\n"); \ - abort(); \ - } \ -} while (0) - -#define assert_not_implemented(e) do { \ - if (unlikely(config_debug && !(e))) { \ - not_implemented(); \ - } \ -} while (0) - -/******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -#define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1) -static char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s, - size_t *slen_p); -#define D2S_BUFSIZE (1 + U2S_BUFSIZE) -static char *d2s(intmax_t x, char sign, char *s, size_t *slen_p); -#define O2S_BUFSIZE (1 + U2S_BUFSIZE) -static char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p); -#define X2S_BUFSIZE (2 + U2S_BUFSIZE) -static char *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, - size_t *slen_p); - -/******************************************************************************/ - -/* malloc_message() setup. */ -void -wrtmessage(void *cbopaque, const char *s) { - malloc_write_fd(STDERR_FILENO, s, strlen(s)); -} - -JEMALLOC_EXPORT void (*je_malloc_message)(void *, const char *s); - -/* - * Wrapper around malloc_message() that avoids the need for - * je_malloc_message(...) throughout the code. - */ -void -malloc_write(const char *s) { - if (je_malloc_message != NULL) { - je_malloc_message(NULL, s); - } else { - wrtmessage(NULL, s); - } -} - -/* - * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so - * provide a wrapper. - */ -int -buferror(int err, char *buf, size_t buflen) { -#ifdef _WIN32 - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, - (LPSTR)buf, (DWORD)buflen, NULL); - return 0; -#elif defined(JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE) && defined(_GNU_SOURCE) - char *b = strerror_r(err, buf, buflen); - if (b != buf) { - strncpy(buf, b, buflen); - buf[buflen-1] = '\0'; - } - return 0; -#else - return strerror_r(err, buf, buflen); -#endif -} - -uintmax_t -malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) { - uintmax_t ret, digit; - unsigned b; - bool neg; - const char *p, *ns; - - p = nptr; - if (base < 0 || base == 1 || base > 36) { - ns = p; - set_errno(EINVAL); - ret = UINTMAX_MAX; - goto label_return; - } - b = base; - - /* Swallow leading whitespace and get sign, if any. */ - neg = false; - while (true) { - switch (*p) { - case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': - p++; - break; - case '-': - neg = true; - JEMALLOC_FALLTHROUGH; - case '+': - p++; - JEMALLOC_FALLTHROUGH; - default: - goto label_prefix; - } - } - - /* Get prefix, if any. */ - label_prefix: - /* - * Note where the first non-whitespace/sign character is so that it is - * possible to tell whether any digits are consumed (e.g., " 0" vs. - * " -x"). - */ - ns = p; - if (*p == '0') { - switch (p[1]) { - case '0': case '1': case '2': case '3': case '4': case '5': - case '6': case '7': - if (b == 0) { - b = 8; - } - if (b == 8) { - p++; - } - break; - case 'X': case 'x': - switch (p[2]) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'A': case 'B': case 'C': case 'D': case 'E': - case 'F': - case 'a': case 'b': case 'c': case 'd': case 'e': - case 'f': - if (b == 0) { - b = 16; - } - if (b == 16) { - p += 2; - } - break; - default: - break; - } - break; - default: - p++; - ret = 0; - goto label_return; - } - } - if (b == 0) { - b = 10; - } - - /* Convert. */ - ret = 0; - while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b) - || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b) - || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) { - uintmax_t pret = ret; - ret *= b; - ret += digit; - if (ret < pret) { - /* Overflow. */ - set_errno(ERANGE); - ret = UINTMAX_MAX; - goto label_return; - } - p++; - } - if (neg) { - ret = (uintmax_t)(-((intmax_t)ret)); - } - - if (p == ns) { - /* No conversion performed. */ - set_errno(EINVAL); - ret = UINTMAX_MAX; - goto label_return; - } - -label_return: - if (endptr != NULL) { - if (p == ns) { - /* No characters were converted. */ - *endptr = (char *)nptr; - } else { - *endptr = (char *)p; - } - } - return ret; -} - -static char * -u2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p) { - unsigned i; - - i = U2S_BUFSIZE - 1; - s[i] = '\0'; - switch (base) { - case 10: - do { - i--; - s[i] = "0123456789"[x % (uint64_t)10]; - x /= (uint64_t)10; - } while (x > 0); - break; - case 16: { - const char *digits = (uppercase) - ? "0123456789ABCDEF" - : "0123456789abcdef"; - - do { - i--; - s[i] = digits[x & 0xf]; - x >>= 4; - } while (x > 0); - break; - } default: { - const char *digits = (uppercase) - ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - : "0123456789abcdefghijklmnopqrstuvwxyz"; - - assert(base >= 2 && base <= 36); - do { - i--; - s[i] = digits[x % (uint64_t)base]; - x /= (uint64_t)base; - } while (x > 0); - }} - - *slen_p = U2S_BUFSIZE - 1 - i; - return &s[i]; -} - -static char * -d2s(intmax_t x, char sign, char *s, size_t *slen_p) { - bool neg; - - if ((neg = (x < 0))) { - x = -x; - } - s = u2s(x, 10, false, s, slen_p); - if (neg) { - sign = '-'; - } - switch (sign) { - case '-': - if (!neg) { - break; - } - JEMALLOC_FALLTHROUGH; - case ' ': - case '+': - s--; - (*slen_p)++; - *s = sign; - break; - default: not_reached(); - } - return s; -} - -static char * -o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p) { - s = u2s(x, 8, false, s, slen_p); - if (alt_form && *s != '0') { - s--; - (*slen_p)++; - *s = '0'; - } - return s; -} - -static char * -x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p) { - s = u2s(x, 16, uppercase, s, slen_p); - if (alt_form) { - s -= 2; - (*slen_p) += 2; - memcpy(s, uppercase ? "0X" : "0x", 2); - } - return s; -} - -JEMALLOC_COLD -size_t -malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) { - size_t i; - const char *f; - -#define APPEND_C(c) do { \ - if (i < size) { \ - str[i] = (c); \ - } \ - i++; \ -} while (0) -#define APPEND_S(s, slen) do { \ - if (i < size) { \ - size_t cpylen = (slen <= size - i) ? slen : size - i; \ - memcpy(&str[i], s, cpylen); \ - } \ - i += slen; \ -} while (0) -#define APPEND_PADDED_S(s, slen, width, left_justify) do { \ - /* Left padding. */ \ - size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ? \ - (size_t)width - slen : 0); \ - if (!left_justify && pad_len != 0) { \ - size_t j; \ - for (j = 0; j < pad_len; j++) { \ - if (pad_zero) { \ - APPEND_C('0'); \ - } else { \ - APPEND_C(' '); \ - } \ - } \ - } \ - /* Value. */ \ - APPEND_S(s, slen); \ - /* Right padding. */ \ - if (left_justify && pad_len != 0) { \ - size_t j; \ - for (j = 0; j < pad_len; j++) { \ - APPEND_C(' '); \ - } \ - } \ -} while (0) -#define GET_ARG_NUMERIC(val, len) do { \ - switch ((unsigned char)len) { \ - case '?': \ - val = va_arg(ap, int); \ - break; \ - case '?' | 0x80: \ - val = va_arg(ap, unsigned int); \ - break; \ - case 'l': \ - val = va_arg(ap, long); \ - break; \ - case 'l' | 0x80: \ - val = va_arg(ap, unsigned long); \ - break; \ - case 'q': \ - val = va_arg(ap, long long); \ - break; \ - case 'q' | 0x80: \ - val = va_arg(ap, unsigned long long); \ - break; \ - case 'j': \ - val = va_arg(ap, intmax_t); \ - break; \ - case 'j' | 0x80: \ - val = va_arg(ap, uintmax_t); \ - break; \ - case 't': \ - val = va_arg(ap, ptrdiff_t); \ - break; \ - case 'z': \ - val = va_arg(ap, ssize_t); \ - break; \ - case 'z' | 0x80: \ - val = va_arg(ap, size_t); \ - break; \ - case 'p': /* Synthetic; used for %p. */ \ - val = va_arg(ap, uintptr_t); \ - break; \ - default: \ - not_reached(); \ - val = 0; \ - } \ -} while (0) - - i = 0; - f = format; - while (true) { - switch (*f) { - case '\0': goto label_out; - case '%': { - bool alt_form = false; - bool left_justify = false; - bool plus_space = false; - bool plus_plus = false; - int prec = -1; - int width = -1; - unsigned char len = '?'; - char *s; - size_t slen; - bool first_width_digit = true; - bool pad_zero = false; - - f++; - /* Flags. */ - while (true) { - switch (*f) { - case '#': - assert(!alt_form); - alt_form = true; - break; - case '-': - assert(!left_justify); - left_justify = true; - break; - case ' ': - assert(!plus_space); - plus_space = true; - break; - case '+': - assert(!plus_plus); - plus_plus = true; - break; - default: goto label_width; - } - f++; - } - /* Width. */ - label_width: - switch (*f) { - case '*': - width = va_arg(ap, int); - f++; - if (width < 0) { - left_justify = true; - width = -width; - } - break; - case '0': - if (first_width_digit) { - pad_zero = true; - } - JEMALLOC_FALLTHROUGH; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - uintmax_t uwidth; - set_errno(0); - uwidth = malloc_strtoumax(f, (char **)&f, 10); - assert(uwidth != UINTMAX_MAX || get_errno() != - ERANGE); - width = (int)uwidth; - first_width_digit = false; - break; - } default: - break; - } - /* Width/precision separator. */ - if (*f == '.') { - f++; - } else { - goto label_length; - } - /* Precision. */ - switch (*f) { - case '*': - prec = va_arg(ap, int); - f++; - break; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - uintmax_t uprec; - set_errno(0); - uprec = malloc_strtoumax(f, (char **)&f, 10); - assert(uprec != UINTMAX_MAX || get_errno() != - ERANGE); - prec = (int)uprec; - break; - } - default: break; - } - /* Length. */ - label_length: - switch (*f) { - case 'l': - f++; - if (*f == 'l') { - len = 'q'; - f++; - } else { - len = 'l'; - } - break; - case 'q': case 'j': case 't': case 'z': - len = *f; - f++; - break; - default: break; - } - /* Conversion specifier. */ - switch (*f) { - case '%': - /* %% */ - APPEND_C(*f); - f++; - break; - case 'd': case 'i': { - intmax_t val JEMALLOC_CC_SILENCE_INIT(0); - char buf[D2S_BUFSIZE]; - - /* - * Outputting negative, zero-padded numbers - * would require a nontrivial rework of the - * interaction between the width and padding - * (since 0 padding goes between the '-' and the - * number, while ' ' padding goes either before - * the - or after the number. Since we - * currently don't ever need 0-padded negative - * numbers, just don't bother supporting it. - */ - assert(!pad_zero); - - GET_ARG_NUMERIC(val, len); - s = d2s(val, (plus_plus ? '+' : (plus_space ? - ' ' : '-')), buf, &slen); - APPEND_PADDED_S(s, slen, width, left_justify); - f++; - break; - } case 'o': { - uintmax_t val JEMALLOC_CC_SILENCE_INIT(0); - char buf[O2S_BUFSIZE]; - - GET_ARG_NUMERIC(val, len | 0x80); - s = o2s(val, alt_form, buf, &slen); - APPEND_PADDED_S(s, slen, width, left_justify); - f++; - break; - } case 'u': { - uintmax_t val JEMALLOC_CC_SILENCE_INIT(0); - char buf[U2S_BUFSIZE]; - - GET_ARG_NUMERIC(val, len | 0x80); - s = u2s(val, 10, false, buf, &slen); - APPEND_PADDED_S(s, slen, width, left_justify); - f++; - break; - } case 'x': case 'X': { - uintmax_t val JEMALLOC_CC_SILENCE_INIT(0); - char buf[X2S_BUFSIZE]; - - GET_ARG_NUMERIC(val, len | 0x80); - s = x2s(val, alt_form, *f == 'X', buf, &slen); - APPEND_PADDED_S(s, slen, width, left_justify); - f++; - break; - } case 'c': { - unsigned char val; - char buf[2]; - - assert(len == '?' || len == 'l'); - assert_not_implemented(len != 'l'); - val = va_arg(ap, int); - buf[0] = val; - buf[1] = '\0'; - APPEND_PADDED_S(buf, 1, width, left_justify); - f++; - break; - } case 's': - assert(len == '?' || len == 'l'); - assert_not_implemented(len != 'l'); - s = va_arg(ap, char *); - slen = (prec < 0) ? strlen(s) : (size_t)prec; - APPEND_PADDED_S(s, slen, width, left_justify); - f++; - break; - case 'p': { - uintmax_t val; - char buf[X2S_BUFSIZE]; - - GET_ARG_NUMERIC(val, 'p'); - s = x2s(val, true, false, buf, &slen); - APPEND_PADDED_S(s, slen, width, left_justify); - f++; - break; - } default: not_reached(); - } - break; - } default: { - APPEND_C(*f); - f++; - break; - }} - } - label_out: - if (i < size) { - str[i] = '\0'; - } else { - str[size - 1] = '\0'; - } - -#undef APPEND_C -#undef APPEND_S -#undef APPEND_PADDED_S -#undef GET_ARG_NUMERIC - return i; -} - -JEMALLOC_FORMAT_PRINTF(3, 4) -size_t -malloc_snprintf(char *str, size_t size, const char *format, ...) { - size_t ret; - va_list ap; - - va_start(ap, format); - ret = malloc_vsnprintf(str, size, format, ap); - va_end(ap); - - return ret; -} - -void -malloc_vcprintf(write_cb_t *write_cb, void *cbopaque, const char *format, - va_list ap) { - char buf[MALLOC_PRINTF_BUFSIZE]; - - if (write_cb == NULL) { - /* - * The caller did not provide an alternate write_cb callback - * function, so use the default one. malloc_write() is an - * inline function, so use malloc_message() directly here. - */ - write_cb = (je_malloc_message != NULL) ? je_malloc_message : - wrtmessage; - } - - malloc_vsnprintf(buf, sizeof(buf), format, ap); - write_cb(cbopaque, buf); -} - -/* - * Print to a callback function in such a way as to (hopefully) avoid memory - * allocation. - */ -JEMALLOC_FORMAT_PRINTF(3, 4) -void -malloc_cprintf(write_cb_t *write_cb, void *cbopaque, const char *format, ...) { - va_list ap; - - va_start(ap, format); - malloc_vcprintf(write_cb, cbopaque, format, ap); - va_end(ap); -} - -/* Print to stderr in such a way as to avoid memory allocation. */ -JEMALLOC_FORMAT_PRINTF(1, 2) -void -malloc_printf(const char *format, ...) { - va_list ap; - - va_start(ap, format); - malloc_vcprintf(NULL, NULL, format, ap); - va_end(ap); -} - -/* - * Restore normal assertion macros, in order to make it possible to compile all - * C files as a single concatenation. - */ -#undef assert -#undef not_reached -#undef not_implemented -#undef assert_not_implemented -#include "jemalloc/internal/assert.h" diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/mutex.c b/fluent-bit/lib/jemalloc-5.3.0/src/mutex.c deleted file mode 100644 index 0b3547a8..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/mutex.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/malloc_io.h" -#include "jemalloc/internal/spin.h" - -#ifndef _CRT_SPINCOUNT -#define _CRT_SPINCOUNT 4000 -#endif - -/* - * Based on benchmark results, a fixed spin with this amount of retries works - * well for our critical sections. - */ -int64_t opt_mutex_max_spin = 600; - -/******************************************************************************/ -/* Data. */ - -#ifdef JEMALLOC_LAZY_LOCK -bool isthreaded = false; -#endif -#ifdef JEMALLOC_MUTEX_INIT_CB -static bool postpone_init = true; -static malloc_mutex_t *postponed_mutexes = NULL; -#endif - -/******************************************************************************/ -/* - * We intercept pthread_create() calls in order to toggle isthreaded if the - * process goes multi-threaded. - */ - -#if defined(JEMALLOC_LAZY_LOCK) && !defined(_WIN32) -JEMALLOC_EXPORT int -pthread_create(pthread_t *__restrict thread, - const pthread_attr_t *__restrict attr, void *(*start_routine)(void *), - void *__restrict arg) { - return pthread_create_wrapper(thread, attr, start_routine, arg); -} -#endif - -/******************************************************************************/ - -#ifdef JEMALLOC_MUTEX_INIT_CB -JEMALLOC_EXPORT int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, - void *(calloc_cb)(size_t, size_t)); -#endif - -void -malloc_mutex_lock_slow(malloc_mutex_t *mutex) { - mutex_prof_data_t *data = &mutex->prof_data; - nstime_t before; - - if (ncpus == 1) { - goto label_spin_done; - } - - int cnt = 0; - do { - spin_cpu_spinwait(); - if (!atomic_load_b(&mutex->locked, ATOMIC_RELAXED) - && !malloc_mutex_trylock_final(mutex)) { - data->n_spin_acquired++; - return; - } - } while (cnt++ < opt_mutex_max_spin || opt_mutex_max_spin == -1); - - if (!config_stats) { - /* Only spin is useful when stats is off. */ - malloc_mutex_lock_final(mutex); - return; - } -label_spin_done: - nstime_init_update(&before); - /* Copy before to after to avoid clock skews. */ - nstime_t after; - nstime_copy(&after, &before); - uint32_t n_thds = atomic_fetch_add_u32(&data->n_waiting_thds, 1, - ATOMIC_RELAXED) + 1; - /* One last try as above two calls may take quite some cycles. */ - if (!malloc_mutex_trylock_final(mutex)) { - atomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED); - data->n_spin_acquired++; - return; - } - - /* True slow path. */ - malloc_mutex_lock_final(mutex); - /* Update more slow-path only counters. */ - atomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED); - nstime_update(&after); - - nstime_t delta; - nstime_copy(&delta, &after); - nstime_subtract(&delta, &before); - - data->n_wait_times++; - nstime_add(&data->tot_wait_time, &delta); - if (nstime_compare(&data->max_wait_time, &delta) < 0) { - nstime_copy(&data->max_wait_time, &delta); - } - if (n_thds > data->max_n_thds) { - data->max_n_thds = n_thds; - } -} - -static void -mutex_prof_data_init(mutex_prof_data_t *data) { - memset(data, 0, sizeof(mutex_prof_data_t)); - nstime_init_zero(&data->max_wait_time); - nstime_init_zero(&data->tot_wait_time); - data->prev_owner = NULL; -} - -void -malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex) { - malloc_mutex_assert_owner(tsdn, mutex); - mutex_prof_data_init(&mutex->prof_data); -} - -static int -mutex_addr_comp(const witness_t *witness1, void *mutex1, - const witness_t *witness2, void *mutex2) { - assert(mutex1 != NULL); - assert(mutex2 != NULL); - uintptr_t mu1int = (uintptr_t)mutex1; - uintptr_t mu2int = (uintptr_t)mutex2; - if (mu1int < mu2int) { - return -1; - } else if (mu1int == mu2int) { - return 0; - } else { - return 1; - } -} - -bool -malloc_mutex_init(malloc_mutex_t *mutex, const char *name, - witness_rank_t rank, malloc_mutex_lock_order_t lock_order) { - mutex_prof_data_init(&mutex->prof_data); -#ifdef _WIN32 -# if _WIN32_WINNT >= 0x0600 - InitializeSRWLock(&mutex->lock); -# else - if (!InitializeCriticalSectionAndSpinCount(&mutex->lock, - _CRT_SPINCOUNT)) { - return true; - } -# endif -#elif (defined(JEMALLOC_OS_UNFAIR_LOCK)) - mutex->lock = OS_UNFAIR_LOCK_INIT; -#elif (defined(JEMALLOC_MUTEX_INIT_CB)) - if (postpone_init) { - mutex->postponed_next = postponed_mutexes; - postponed_mutexes = mutex; - } else { - if (_pthread_mutex_init_calloc_cb(&mutex->lock, - bootstrap_calloc) != 0) { - return true; - } - } -#else - pthread_mutexattr_t attr; - - if (pthread_mutexattr_init(&attr) != 0) { - return true; - } - pthread_mutexattr_settype(&attr, MALLOC_MUTEX_TYPE); - if (pthread_mutex_init(&mutex->lock, &attr) != 0) { - pthread_mutexattr_destroy(&attr); - return true; - } - pthread_mutexattr_destroy(&attr); -#endif - if (config_debug) { - mutex->lock_order = lock_order; - if (lock_order == malloc_mutex_address_ordered) { - witness_init(&mutex->witness, name, rank, - mutex_addr_comp, mutex); - } else { - witness_init(&mutex->witness, name, rank, NULL, NULL); - } - } - return false; -} - -void -malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex) { - malloc_mutex_lock(tsdn, mutex); -} - -void -malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex) { - malloc_mutex_unlock(tsdn, mutex); -} - -void -malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex) { -#ifdef JEMALLOC_MUTEX_INIT_CB - malloc_mutex_unlock(tsdn, mutex); -#else - if (malloc_mutex_init(mutex, mutex->witness.name, - mutex->witness.rank, mutex->lock_order)) { - malloc_printf("<jemalloc>: Error re-initializing mutex in " - "child\n"); - if (opt_abort) { - abort(); - } - } -#endif -} - -bool -malloc_mutex_boot(void) { -#ifdef JEMALLOC_MUTEX_INIT_CB - postpone_init = false; - while (postponed_mutexes != NULL) { - if (_pthread_mutex_init_calloc_cb(&postponed_mutexes->lock, - bootstrap_calloc) != 0) { - return true; - } - postponed_mutexes = postponed_mutexes->postponed_next; - } -#endif - return false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/nstime.c b/fluent-bit/lib/jemalloc-5.3.0/src/nstime.c deleted file mode 100644 index a1a53777..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/nstime.c +++ /dev/null @@ -1,289 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/nstime.h" - -#include "jemalloc/internal/assert.h" - -#define BILLION UINT64_C(1000000000) -#define MILLION UINT64_C(1000000) - -static void -nstime_set_initialized(nstime_t *time) { -#ifdef JEMALLOC_DEBUG - time->magic = NSTIME_MAGIC; -#endif -} - -static void -nstime_assert_initialized(const nstime_t *time) { -#ifdef JEMALLOC_DEBUG - /* - * Some parts (e.g. stats) rely on memset to zero initialize. Treat - * these as valid initialization. - */ - assert(time->magic == NSTIME_MAGIC || - (time->magic == 0 && time->ns == 0)); -#endif -} - -static void -nstime_pair_assert_initialized(const nstime_t *t1, const nstime_t *t2) { - nstime_assert_initialized(t1); - nstime_assert_initialized(t2); -} - -static void -nstime_initialize_operand(nstime_t *time) { - /* - * Operations like nstime_add may have the initial operand being zero - * initialized (covered by the assert below). Full-initialize needed - * before changing it to non-zero. - */ - nstime_assert_initialized(time); - nstime_set_initialized(time); -} - -void -nstime_init(nstime_t *time, uint64_t ns) { - nstime_set_initialized(time); - time->ns = ns; -} - -void -nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) { - nstime_set_initialized(time); - time->ns = sec * BILLION + nsec; -} - -uint64_t -nstime_ns(const nstime_t *time) { - nstime_assert_initialized(time); - return time->ns; -} - -uint64_t -nstime_msec(const nstime_t *time) { - nstime_assert_initialized(time); - return time->ns / MILLION; -} - -uint64_t -nstime_sec(const nstime_t *time) { - nstime_assert_initialized(time); - return time->ns / BILLION; -} - -uint64_t -nstime_nsec(const nstime_t *time) { - nstime_assert_initialized(time); - return time->ns % BILLION; -} - -void -nstime_copy(nstime_t *time, const nstime_t *source) { - /* Source is required to be initialized. */ - nstime_assert_initialized(source); - *time = *source; - nstime_assert_initialized(time); -} - -int -nstime_compare(const nstime_t *a, const nstime_t *b) { - nstime_pair_assert_initialized(a, b); - return (a->ns > b->ns) - (a->ns < b->ns); -} - -void -nstime_add(nstime_t *time, const nstime_t *addend) { - nstime_pair_assert_initialized(time, addend); - assert(UINT64_MAX - time->ns >= addend->ns); - - nstime_initialize_operand(time); - time->ns += addend->ns; -} - -void -nstime_iadd(nstime_t *time, uint64_t addend) { - nstime_assert_initialized(time); - assert(UINT64_MAX - time->ns >= addend); - - nstime_initialize_operand(time); - time->ns += addend; -} - -void -nstime_subtract(nstime_t *time, const nstime_t *subtrahend) { - nstime_pair_assert_initialized(time, subtrahend); - assert(nstime_compare(time, subtrahend) >= 0); - - /* No initialize operand -- subtraction must be initialized. */ - time->ns -= subtrahend->ns; -} - -void -nstime_isubtract(nstime_t *time, uint64_t subtrahend) { - nstime_assert_initialized(time); - assert(time->ns >= subtrahend); - - /* No initialize operand -- subtraction must be initialized. */ - time->ns -= subtrahend; -} - -void -nstime_imultiply(nstime_t *time, uint64_t multiplier) { - nstime_assert_initialized(time); - assert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) << - 2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns)); - - nstime_initialize_operand(time); - time->ns *= multiplier; -} - -void -nstime_idivide(nstime_t *time, uint64_t divisor) { - nstime_assert_initialized(time); - assert(divisor != 0); - - nstime_initialize_operand(time); - time->ns /= divisor; -} - -uint64_t -nstime_divide(const nstime_t *time, const nstime_t *divisor) { - nstime_pair_assert_initialized(time, divisor); - assert(divisor->ns != 0); - - /* No initialize operand -- *time itself remains unchanged. */ - return time->ns / divisor->ns; -} - -/* Returns time since *past, w/o updating *past. */ -uint64_t -nstime_ns_since(const nstime_t *past) { - nstime_assert_initialized(past); - - nstime_t now; - nstime_copy(&now, past); - nstime_update(&now); - - assert(nstime_compare(&now, past) >= 0); - return now.ns - past->ns; -} - -#ifdef _WIN32 -# define NSTIME_MONOTONIC true -static void -nstime_get(nstime_t *time) { - FILETIME ft; - uint64_t ticks_100ns; - - GetSystemTimeAsFileTime(&ft); - ticks_100ns = (((uint64_t)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; - - nstime_init(time, ticks_100ns * 100); -} -#elif defined(JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE) -# define NSTIME_MONOTONIC true -static void -nstime_get(nstime_t *time) { - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); - nstime_init2(time, ts.tv_sec, ts.tv_nsec); -} -#elif defined(JEMALLOC_HAVE_CLOCK_MONOTONIC) -# define NSTIME_MONOTONIC true -static void -nstime_get(nstime_t *time) { - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - nstime_init2(time, ts.tv_sec, ts.tv_nsec); -} -#elif defined(JEMALLOC_HAVE_MACH_ABSOLUTE_TIME) -# define NSTIME_MONOTONIC true -static void -nstime_get(nstime_t *time) { - nstime_init(time, mach_absolute_time()); -} -#else -# define NSTIME_MONOTONIC false -static void -nstime_get(nstime_t *time) { - struct timeval tv; - - gettimeofday(&tv, NULL); - nstime_init2(time, tv.tv_sec, tv.tv_usec * 1000); -} -#endif - -static bool -nstime_monotonic_impl(void) { - return NSTIME_MONOTONIC; -#undef NSTIME_MONOTONIC -} -nstime_monotonic_t *JET_MUTABLE nstime_monotonic = nstime_monotonic_impl; - -prof_time_res_t opt_prof_time_res = - prof_time_res_default; - -const char *prof_time_res_mode_names[] = { - "default", - "high", -}; - - -static void -nstime_get_realtime(nstime_t *time) { -#if defined(JEMALLOC_HAVE_CLOCK_REALTIME) && !defined(_WIN32) - struct timespec ts; - - clock_gettime(CLOCK_REALTIME, &ts); - nstime_init2(time, ts.tv_sec, ts.tv_nsec); -#else - unreachable(); -#endif -} - -static void -nstime_prof_update_impl(nstime_t *time) { - nstime_t old_time; - - nstime_copy(&old_time, time); - - if (opt_prof_time_res == prof_time_res_high) { - nstime_get_realtime(time); - } else { - nstime_get(time); - } -} -nstime_prof_update_t *JET_MUTABLE nstime_prof_update = nstime_prof_update_impl; - -static void -nstime_update_impl(nstime_t *time) { - nstime_t old_time; - - nstime_copy(&old_time, time); - nstime_get(time); - - /* Handle non-monotonic clocks. */ - if (unlikely(nstime_compare(&old_time, time) > 0)) { - nstime_copy(time, &old_time); - } -} -nstime_update_t *JET_MUTABLE nstime_update = nstime_update_impl; - -void -nstime_init_update(nstime_t *time) { - nstime_init_zero(time); - nstime_update(time); -} - -void -nstime_prof_init_update(nstime_t *time) { - nstime_init_zero(time); - nstime_prof_update(time); -} - - diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/pa.c b/fluent-bit/lib/jemalloc-5.3.0/src/pa.c deleted file mode 100644 index eb7e4620..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/pa.c +++ /dev/null @@ -1,277 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/hpa.h" - -static void -pa_nactive_add(pa_shard_t *shard, size_t add_pages) { - atomic_fetch_add_zu(&shard->nactive, add_pages, ATOMIC_RELAXED); -} - -static void -pa_nactive_sub(pa_shard_t *shard, size_t sub_pages) { - assert(atomic_load_zu(&shard->nactive, ATOMIC_RELAXED) >= sub_pages); - atomic_fetch_sub_zu(&shard->nactive, sub_pages, ATOMIC_RELAXED); -} - -bool -pa_central_init(pa_central_t *central, base_t *base, bool hpa, - hpa_hooks_t *hpa_hooks) { - bool err; - if (hpa) { - err = hpa_central_init(¢ral->hpa, base, hpa_hooks); - if (err) { - return true; - } - } - return false; -} - -bool -pa_shard_init(tsdn_t *tsdn, pa_shard_t *shard, pa_central_t *central, - emap_t *emap, base_t *base, unsigned ind, pa_shard_stats_t *stats, - malloc_mutex_t *stats_mtx, nstime_t *cur_time, - size_t pac_oversize_threshold, ssize_t dirty_decay_ms, - ssize_t muzzy_decay_ms) { - /* This will change eventually, but for now it should hold. */ - assert(base_ind_get(base) == ind); - if (edata_cache_init(&shard->edata_cache, base)) { - return true; - } - - if (pac_init(tsdn, &shard->pac, base, emap, &shard->edata_cache, - cur_time, pac_oversize_threshold, dirty_decay_ms, muzzy_decay_ms, - &stats->pac_stats, stats_mtx)) { - return true; - } - - shard->ind = ind; - - shard->ever_used_hpa = false; - atomic_store_b(&shard->use_hpa, false, ATOMIC_RELAXED); - - atomic_store_zu(&shard->nactive, 0, ATOMIC_RELAXED); - - shard->stats_mtx = stats_mtx; - shard->stats = stats; - memset(shard->stats, 0, sizeof(*shard->stats)); - - shard->central = central; - shard->emap = emap; - shard->base = base; - - return false; -} - -bool -pa_shard_enable_hpa(tsdn_t *tsdn, pa_shard_t *shard, - const hpa_shard_opts_t *hpa_opts, const sec_opts_t *hpa_sec_opts) { - if (hpa_shard_init(&shard->hpa_shard, &shard->central->hpa, shard->emap, - shard->base, &shard->edata_cache, shard->ind, hpa_opts)) { - return true; - } - if (sec_init(tsdn, &shard->hpa_sec, shard->base, &shard->hpa_shard.pai, - hpa_sec_opts)) { - return true; - } - shard->ever_used_hpa = true; - atomic_store_b(&shard->use_hpa, true, ATOMIC_RELAXED); - - return false; -} - -void -pa_shard_disable_hpa(tsdn_t *tsdn, pa_shard_t *shard) { - atomic_store_b(&shard->use_hpa, false, ATOMIC_RELAXED); - if (shard->ever_used_hpa) { - sec_disable(tsdn, &shard->hpa_sec); - hpa_shard_disable(tsdn, &shard->hpa_shard); - } -} - -void -pa_shard_reset(tsdn_t *tsdn, pa_shard_t *shard) { - atomic_store_zu(&shard->nactive, 0, ATOMIC_RELAXED); - if (shard->ever_used_hpa) { - sec_flush(tsdn, &shard->hpa_sec); - } -} - -static bool -pa_shard_uses_hpa(pa_shard_t *shard) { - return atomic_load_b(&shard->use_hpa, ATOMIC_RELAXED); -} - -void -pa_shard_destroy(tsdn_t *tsdn, pa_shard_t *shard) { - pac_destroy(tsdn, &shard->pac); - if (shard->ever_used_hpa) { - sec_flush(tsdn, &shard->hpa_sec); - hpa_shard_disable(tsdn, &shard->hpa_shard); - } -} - -static pai_t * -pa_get_pai(pa_shard_t *shard, edata_t *edata) { - return (edata_pai_get(edata) == EXTENT_PAI_PAC - ? &shard->pac.pai : &shard->hpa_sec.pai); -} - -edata_t * -pa_alloc(tsdn_t *tsdn, pa_shard_t *shard, size_t size, size_t alignment, - bool slab, szind_t szind, bool zero, bool guarded, - bool *deferred_work_generated) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - assert(!guarded || alignment <= PAGE); - - edata_t *edata = NULL; - if (!guarded && pa_shard_uses_hpa(shard)) { - edata = pai_alloc(tsdn, &shard->hpa_sec.pai, size, alignment, - zero, /* guarded */ false, slab, deferred_work_generated); - } - /* - * Fall back to the PAC if the HPA is off or couldn't serve the given - * allocation request. - */ - if (edata == NULL) { - edata = pai_alloc(tsdn, &shard->pac.pai, size, alignment, zero, - guarded, slab, deferred_work_generated); - } - if (edata != NULL) { - assert(edata_size_get(edata) == size); - pa_nactive_add(shard, size >> LG_PAGE); - emap_remap(tsdn, shard->emap, edata, szind, slab); - edata_szind_set(edata, szind); - edata_slab_set(edata, slab); - if (slab && (size > 2 * PAGE)) { - emap_register_interior(tsdn, shard->emap, edata, szind); - } - assert(edata_arena_ind_get(edata) == shard->ind); - } - return edata; -} - -bool -pa_expand(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, size_t old_size, - size_t new_size, szind_t szind, bool zero, bool *deferred_work_generated) { - assert(new_size > old_size); - assert(edata_size_get(edata) == old_size); - assert((new_size & PAGE_MASK) == 0); - if (edata_guarded_get(edata)) { - return true; - } - size_t expand_amount = new_size - old_size; - - pai_t *pai = pa_get_pai(shard, edata); - - bool error = pai_expand(tsdn, pai, edata, old_size, new_size, zero, - deferred_work_generated); - if (error) { - return true; - } - - pa_nactive_add(shard, expand_amount >> LG_PAGE); - edata_szind_set(edata, szind); - emap_remap(tsdn, shard->emap, edata, szind, /* slab */ false); - return false; -} - -bool -pa_shrink(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, size_t old_size, - size_t new_size, szind_t szind, bool *deferred_work_generated) { - assert(new_size < old_size); - assert(edata_size_get(edata) == old_size); - assert((new_size & PAGE_MASK) == 0); - if (edata_guarded_get(edata)) { - return true; - } - size_t shrink_amount = old_size - new_size; - - pai_t *pai = pa_get_pai(shard, edata); - bool error = pai_shrink(tsdn, pai, edata, old_size, new_size, - deferred_work_generated); - if (error) { - return true; - } - pa_nactive_sub(shard, shrink_amount >> LG_PAGE); - - edata_szind_set(edata, szind); - emap_remap(tsdn, shard->emap, edata, szind, /* slab */ false); - return false; -} - -void -pa_dalloc(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, - bool *deferred_work_generated) { - emap_remap(tsdn, shard->emap, edata, SC_NSIZES, /* slab */ false); - if (edata_slab_get(edata)) { - emap_deregister_interior(tsdn, shard->emap, edata); - /* - * The slab state of the extent isn't cleared. It may be used - * by the pai implementation, e.g. to make caching decisions. - */ - } - edata_addr_set(edata, edata_base_get(edata)); - edata_szind_set(edata, SC_NSIZES); - pa_nactive_sub(shard, edata_size_get(edata) >> LG_PAGE); - pai_t *pai = pa_get_pai(shard, edata); - pai_dalloc(tsdn, pai, edata, deferred_work_generated); -} - -bool -pa_shard_retain_grow_limit_get_set(tsdn_t *tsdn, pa_shard_t *shard, - size_t *old_limit, size_t *new_limit) { - return pac_retain_grow_limit_get_set(tsdn, &shard->pac, old_limit, - new_limit); -} - -bool -pa_decay_ms_set(tsdn_t *tsdn, pa_shard_t *shard, extent_state_t state, - ssize_t decay_ms, pac_purge_eagerness_t eagerness) { - return pac_decay_ms_set(tsdn, &shard->pac, state, decay_ms, eagerness); -} - -ssize_t -pa_decay_ms_get(pa_shard_t *shard, extent_state_t state) { - return pac_decay_ms_get(&shard->pac, state); -} - -void -pa_shard_set_deferral_allowed(tsdn_t *tsdn, pa_shard_t *shard, - bool deferral_allowed) { - if (pa_shard_uses_hpa(shard)) { - hpa_shard_set_deferral_allowed(tsdn, &shard->hpa_shard, - deferral_allowed); - } -} - -void -pa_shard_do_deferred_work(tsdn_t *tsdn, pa_shard_t *shard) { - if (pa_shard_uses_hpa(shard)) { - hpa_shard_do_deferred_work(tsdn, &shard->hpa_shard); - } -} - -/* - * Get time until next deferred work ought to happen. If there are multiple - * things that have been deferred, this function calculates the time until - * the soonest of those things. - */ -uint64_t -pa_shard_time_until_deferred_work(tsdn_t *tsdn, pa_shard_t *shard) { - uint64_t time = pai_time_until_deferred_work(tsdn, &shard->pac.pai); - if (time == BACKGROUND_THREAD_DEFERRED_MIN) { - return time; - } - - if (pa_shard_uses_hpa(shard)) { - uint64_t hpa = - pai_time_until_deferred_work(tsdn, &shard->hpa_shard.pai); - if (hpa < time) { - time = hpa; - } - } - return time; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/pa_extra.c b/fluent-bit/lib/jemalloc-5.3.0/src/pa_extra.c deleted file mode 100644 index 0f488be6..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/pa_extra.c +++ /dev/null @@ -1,191 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -/* - * This file is logically part of the PA module. While pa.c contains the core - * allocator functionality, this file contains boring integration functionality; - * things like the pre- and post- fork handlers, and stats merging for CTL - * refreshes. - */ - -void -pa_shard_prefork0(tsdn_t *tsdn, pa_shard_t *shard) { - malloc_mutex_prefork(tsdn, &shard->pac.decay_dirty.mtx); - malloc_mutex_prefork(tsdn, &shard->pac.decay_muzzy.mtx); -} - -void -pa_shard_prefork2(tsdn_t *tsdn, pa_shard_t *shard) { - if (shard->ever_used_hpa) { - sec_prefork2(tsdn, &shard->hpa_sec); - } -} - -void -pa_shard_prefork3(tsdn_t *tsdn, pa_shard_t *shard) { - malloc_mutex_prefork(tsdn, &shard->pac.grow_mtx); - if (shard->ever_used_hpa) { - hpa_shard_prefork3(tsdn, &shard->hpa_shard); - } -} - -void -pa_shard_prefork4(tsdn_t *tsdn, pa_shard_t *shard) { - ecache_prefork(tsdn, &shard->pac.ecache_dirty); - ecache_prefork(tsdn, &shard->pac.ecache_muzzy); - ecache_prefork(tsdn, &shard->pac.ecache_retained); - if (shard->ever_used_hpa) { - hpa_shard_prefork4(tsdn, &shard->hpa_shard); - } -} - -void -pa_shard_prefork5(tsdn_t *tsdn, pa_shard_t *shard) { - edata_cache_prefork(tsdn, &shard->edata_cache); -} - -void -pa_shard_postfork_parent(tsdn_t *tsdn, pa_shard_t *shard) { - edata_cache_postfork_parent(tsdn, &shard->edata_cache); - ecache_postfork_parent(tsdn, &shard->pac.ecache_dirty); - ecache_postfork_parent(tsdn, &shard->pac.ecache_muzzy); - ecache_postfork_parent(tsdn, &shard->pac.ecache_retained); - malloc_mutex_postfork_parent(tsdn, &shard->pac.grow_mtx); - malloc_mutex_postfork_parent(tsdn, &shard->pac.decay_dirty.mtx); - malloc_mutex_postfork_parent(tsdn, &shard->pac.decay_muzzy.mtx); - if (shard->ever_used_hpa) { - sec_postfork_parent(tsdn, &shard->hpa_sec); - hpa_shard_postfork_parent(tsdn, &shard->hpa_shard); - } -} - -void -pa_shard_postfork_child(tsdn_t *tsdn, pa_shard_t *shard) { - edata_cache_postfork_child(tsdn, &shard->edata_cache); - ecache_postfork_child(tsdn, &shard->pac.ecache_dirty); - ecache_postfork_child(tsdn, &shard->pac.ecache_muzzy); - ecache_postfork_child(tsdn, &shard->pac.ecache_retained); - malloc_mutex_postfork_child(tsdn, &shard->pac.grow_mtx); - malloc_mutex_postfork_child(tsdn, &shard->pac.decay_dirty.mtx); - malloc_mutex_postfork_child(tsdn, &shard->pac.decay_muzzy.mtx); - if (shard->ever_used_hpa) { - sec_postfork_child(tsdn, &shard->hpa_sec); - hpa_shard_postfork_child(tsdn, &shard->hpa_shard); - } -} - -void -pa_shard_basic_stats_merge(pa_shard_t *shard, size_t *nactive, size_t *ndirty, - size_t *nmuzzy) { - *nactive += atomic_load_zu(&shard->nactive, ATOMIC_RELAXED); - *ndirty += ecache_npages_get(&shard->pac.ecache_dirty); - *nmuzzy += ecache_npages_get(&shard->pac.ecache_muzzy); -} - -void -pa_shard_stats_merge(tsdn_t *tsdn, pa_shard_t *shard, - pa_shard_stats_t *pa_shard_stats_out, pac_estats_t *estats_out, - hpa_shard_stats_t *hpa_stats_out, sec_stats_t *sec_stats_out, - size_t *resident) { - cassert(config_stats); - - pa_shard_stats_out->pac_stats.retained += - ecache_npages_get(&shard->pac.ecache_retained) << LG_PAGE; - pa_shard_stats_out->edata_avail += atomic_load_zu( - &shard->edata_cache.count, ATOMIC_RELAXED); - - size_t resident_pgs = 0; - resident_pgs += atomic_load_zu(&shard->nactive, ATOMIC_RELAXED); - resident_pgs += ecache_npages_get(&shard->pac.ecache_dirty); - *resident += (resident_pgs << LG_PAGE); - - /* Dirty decay stats */ - locked_inc_u64_unsynchronized( - &pa_shard_stats_out->pac_stats.decay_dirty.npurge, - locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx), - &shard->pac.stats->decay_dirty.npurge)); - locked_inc_u64_unsynchronized( - &pa_shard_stats_out->pac_stats.decay_dirty.nmadvise, - locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx), - &shard->pac.stats->decay_dirty.nmadvise)); - locked_inc_u64_unsynchronized( - &pa_shard_stats_out->pac_stats.decay_dirty.purged, - locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx), - &shard->pac.stats->decay_dirty.purged)); - - /* Muzzy decay stats */ - locked_inc_u64_unsynchronized( - &pa_shard_stats_out->pac_stats.decay_muzzy.npurge, - locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx), - &shard->pac.stats->decay_muzzy.npurge)); - locked_inc_u64_unsynchronized( - &pa_shard_stats_out->pac_stats.decay_muzzy.nmadvise, - locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx), - &shard->pac.stats->decay_muzzy.nmadvise)); - locked_inc_u64_unsynchronized( - &pa_shard_stats_out->pac_stats.decay_muzzy.purged, - locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx), - &shard->pac.stats->decay_muzzy.purged)); - - atomic_load_add_store_zu(&pa_shard_stats_out->pac_stats.abandoned_vm, - atomic_load_zu(&shard->pac.stats->abandoned_vm, ATOMIC_RELAXED)); - - for (pszind_t i = 0; i < SC_NPSIZES; i++) { - size_t dirty, muzzy, retained, dirty_bytes, muzzy_bytes, - retained_bytes; - dirty = ecache_nextents_get(&shard->pac.ecache_dirty, i); - muzzy = ecache_nextents_get(&shard->pac.ecache_muzzy, i); - retained = ecache_nextents_get(&shard->pac.ecache_retained, i); - dirty_bytes = ecache_nbytes_get(&shard->pac.ecache_dirty, i); - muzzy_bytes = ecache_nbytes_get(&shard->pac.ecache_muzzy, i); - retained_bytes = ecache_nbytes_get(&shard->pac.ecache_retained, - i); - - estats_out[i].ndirty = dirty; - estats_out[i].nmuzzy = muzzy; - estats_out[i].nretained = retained; - estats_out[i].dirty_bytes = dirty_bytes; - estats_out[i].muzzy_bytes = muzzy_bytes; - estats_out[i].retained_bytes = retained_bytes; - } - - if (shard->ever_used_hpa) { - hpa_shard_stats_merge(tsdn, &shard->hpa_shard, hpa_stats_out); - sec_stats_merge(tsdn, &shard->hpa_sec, sec_stats_out); - } -} - -static void -pa_shard_mtx_stats_read_single(tsdn_t *tsdn, mutex_prof_data_t *mutex_prof_data, - malloc_mutex_t *mtx, int ind) { - malloc_mutex_lock(tsdn, mtx); - malloc_mutex_prof_read(tsdn, &mutex_prof_data[ind], mtx); - malloc_mutex_unlock(tsdn, mtx); -} - -void -pa_shard_mtx_stats_read(tsdn_t *tsdn, pa_shard_t *shard, - mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes]) { - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->edata_cache.mtx, arena_prof_mutex_extent_avail); - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->pac.ecache_dirty.mtx, arena_prof_mutex_extents_dirty); - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->pac.ecache_muzzy.mtx, arena_prof_mutex_extents_muzzy); - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->pac.ecache_retained.mtx, arena_prof_mutex_extents_retained); - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->pac.decay_dirty.mtx, arena_prof_mutex_decay_dirty); - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->pac.decay_muzzy.mtx, arena_prof_mutex_decay_muzzy); - - if (shard->ever_used_hpa) { - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->hpa_shard.mtx, arena_prof_mutex_hpa_shard); - pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data, - &shard->hpa_shard.grow_mtx, - arena_prof_mutex_hpa_shard_grow); - sec_mutex_stats_read(tsdn, &shard->hpa_sec, - &mutex_prof_data[arena_prof_mutex_hpa_sec]); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/pac.c b/fluent-bit/lib/jemalloc-5.3.0/src/pac.c deleted file mode 100644 index 53e3d823..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/pac.c +++ /dev/null @@ -1,587 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/pac.h" -#include "jemalloc/internal/san.h" - -static edata_t *pac_alloc_impl(tsdn_t *tsdn, pai_t *self, size_t size, - size_t alignment, bool zero, bool guarded, bool frequent_reuse, - bool *deferred_work_generated); -static bool pac_expand_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool zero, bool *deferred_work_generated); -static bool pac_shrink_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool *deferred_work_generated); -static void pac_dalloc_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, - bool *deferred_work_generated); -static uint64_t pac_time_until_deferred_work(tsdn_t *tsdn, pai_t *self); - -static inline void -pac_decay_data_get(pac_t *pac, extent_state_t state, - decay_t **r_decay, pac_decay_stats_t **r_decay_stats, ecache_t **r_ecache) { - switch(state) { - case extent_state_dirty: - *r_decay = &pac->decay_dirty; - *r_decay_stats = &pac->stats->decay_dirty; - *r_ecache = &pac->ecache_dirty; - return; - case extent_state_muzzy: - *r_decay = &pac->decay_muzzy; - *r_decay_stats = &pac->stats->decay_muzzy; - *r_ecache = &pac->ecache_muzzy; - return; - default: - unreachable(); - } -} - -bool -pac_init(tsdn_t *tsdn, pac_t *pac, base_t *base, emap_t *emap, - edata_cache_t *edata_cache, nstime_t *cur_time, - size_t pac_oversize_threshold, ssize_t dirty_decay_ms, - ssize_t muzzy_decay_ms, pac_stats_t *pac_stats, malloc_mutex_t *stats_mtx) { - unsigned ind = base_ind_get(base); - /* - * Delay coalescing for dirty extents despite the disruptive effect on - * memory layout for best-fit extent allocation, since cached extents - * are likely to be reused soon after deallocation, and the cost of - * merging/splitting extents is non-trivial. - */ - if (ecache_init(tsdn, &pac->ecache_dirty, extent_state_dirty, ind, - /* delay_coalesce */ true)) { - return true; - } - /* - * Coalesce muzzy extents immediately, because operations on them are in - * the critical path much less often than for dirty extents. - */ - if (ecache_init(tsdn, &pac->ecache_muzzy, extent_state_muzzy, ind, - /* delay_coalesce */ false)) { - return true; - } - /* - * Coalesce retained extents immediately, in part because they will - * never be evicted (and therefore there's no opportunity for delayed - * coalescing), but also because operations on retained extents are not - * in the critical path. - */ - if (ecache_init(tsdn, &pac->ecache_retained, extent_state_retained, - ind, /* delay_coalesce */ false)) { - return true; - } - exp_grow_init(&pac->exp_grow); - if (malloc_mutex_init(&pac->grow_mtx, "extent_grow", - WITNESS_RANK_EXTENT_GROW, malloc_mutex_rank_exclusive)) { - return true; - } - atomic_store_zu(&pac->oversize_threshold, pac_oversize_threshold, - ATOMIC_RELAXED); - if (decay_init(&pac->decay_dirty, cur_time, dirty_decay_ms)) { - return true; - } - if (decay_init(&pac->decay_muzzy, cur_time, muzzy_decay_ms)) { - return true; - } - if (san_bump_alloc_init(&pac->sba)) { - return true; - } - - pac->base = base; - pac->emap = emap; - pac->edata_cache = edata_cache; - pac->stats = pac_stats; - pac->stats_mtx = stats_mtx; - atomic_store_zu(&pac->extent_sn_next, 0, ATOMIC_RELAXED); - - pac->pai.alloc = &pac_alloc_impl; - pac->pai.alloc_batch = &pai_alloc_batch_default; - pac->pai.expand = &pac_expand_impl; - pac->pai.shrink = &pac_shrink_impl; - pac->pai.dalloc = &pac_dalloc_impl; - pac->pai.dalloc_batch = &pai_dalloc_batch_default; - pac->pai.time_until_deferred_work = &pac_time_until_deferred_work; - - return false; -} - -static inline bool -pac_may_have_muzzy(pac_t *pac) { - return pac_decay_ms_get(pac, extent_state_muzzy) != 0; -} - -static edata_t * -pac_alloc_real(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, size_t size, - size_t alignment, bool zero, bool guarded) { - assert(!guarded || alignment <= PAGE); - - edata_t *edata = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_dirty, - NULL, size, alignment, zero, guarded); - - if (edata == NULL && pac_may_have_muzzy(pac)) { - edata = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_muzzy, - NULL, size, alignment, zero, guarded); - } - if (edata == NULL) { - edata = ecache_alloc_grow(tsdn, pac, ehooks, - &pac->ecache_retained, NULL, size, alignment, zero, - guarded); - if (config_stats && edata != NULL) { - atomic_fetch_add_zu(&pac->stats->pac_mapped, size, - ATOMIC_RELAXED); - } - } - - return edata; -} - -static edata_t * -pac_alloc_new_guarded(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, size_t size, - size_t alignment, bool zero, bool frequent_reuse) { - assert(alignment <= PAGE); - - edata_t *edata; - if (san_bump_enabled() && frequent_reuse) { - edata = san_bump_alloc(tsdn, &pac->sba, pac, ehooks, size, - zero); - } else { - size_t size_with_guards = san_two_side_guarded_sz(size); - /* Alloc a non-guarded extent first.*/ - edata = pac_alloc_real(tsdn, pac, ehooks, size_with_guards, - /* alignment */ PAGE, zero, /* guarded */ false); - if (edata != NULL) { - /* Add guards around it. */ - assert(edata_size_get(edata) == size_with_guards); - san_guard_pages_two_sided(tsdn, ehooks, edata, - pac->emap, true); - } - } - assert(edata == NULL || (edata_guarded_get(edata) && - edata_size_get(edata) == size)); - - return edata; -} - -static edata_t * -pac_alloc_impl(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment, - bool zero, bool guarded, bool frequent_reuse, - bool *deferred_work_generated) { - pac_t *pac = (pac_t *)self; - ehooks_t *ehooks = pac_ehooks_get(pac); - - edata_t *edata = NULL; - /* - * The condition is an optimization - not frequently reused guarded - * allocations are never put in the ecache. pac_alloc_real also - * doesn't grow retained for guarded allocations. So pac_alloc_real - * for such allocations would always return NULL. - * */ - if (!guarded || frequent_reuse) { - edata = pac_alloc_real(tsdn, pac, ehooks, size, alignment, - zero, guarded); - } - if (edata == NULL && guarded) { - /* No cached guarded extents; creating a new one. */ - edata = pac_alloc_new_guarded(tsdn, pac, ehooks, size, - alignment, zero, frequent_reuse); - } - - return edata; -} - -static bool -pac_expand_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size, - size_t new_size, bool zero, bool *deferred_work_generated) { - pac_t *pac = (pac_t *)self; - ehooks_t *ehooks = pac_ehooks_get(pac); - - size_t mapped_add = 0; - size_t expand_amount = new_size - old_size; - - if (ehooks_merge_will_fail(ehooks)) { - return true; - } - edata_t *trail = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_dirty, - edata, expand_amount, PAGE, zero, /* guarded*/ false); - if (trail == NULL) { - trail = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_muzzy, - edata, expand_amount, PAGE, zero, /* guarded*/ false); - } - if (trail == NULL) { - trail = ecache_alloc_grow(tsdn, pac, ehooks, - &pac->ecache_retained, edata, expand_amount, PAGE, zero, - /* guarded */ false); - mapped_add = expand_amount; - } - if (trail == NULL) { - return true; - } - if (extent_merge_wrapper(tsdn, pac, ehooks, edata, trail)) { - extent_dalloc_wrapper(tsdn, pac, ehooks, trail); - return true; - } - if (config_stats && mapped_add > 0) { - atomic_fetch_add_zu(&pac->stats->pac_mapped, mapped_add, - ATOMIC_RELAXED); - } - return false; -} - -static bool -pac_shrink_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size, - size_t new_size, bool *deferred_work_generated) { - pac_t *pac = (pac_t *)self; - ehooks_t *ehooks = pac_ehooks_get(pac); - - size_t shrink_amount = old_size - new_size; - - if (ehooks_split_will_fail(ehooks)) { - return true; - } - - edata_t *trail = extent_split_wrapper(tsdn, pac, ehooks, edata, - new_size, shrink_amount, /* holding_core_locks */ false); - if (trail == NULL) { - return true; - } - ecache_dalloc(tsdn, pac, ehooks, &pac->ecache_dirty, trail); - *deferred_work_generated = true; - return false; -} - -static void -pac_dalloc_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, - bool *deferred_work_generated) { - pac_t *pac = (pac_t *)self; - ehooks_t *ehooks = pac_ehooks_get(pac); - - if (edata_guarded_get(edata)) { - /* - * Because cached guarded extents do exact fit only, large - * guarded extents are restored on dalloc eagerly (otherwise - * they will not be reused efficiently). Slab sizes have a - * limited number of size classes, and tend to cycle faster. - * - * In the case where coalesce is restrained (VirtualFree on - * Windows), guarded extents are also not cached -- otherwise - * during arena destroy / reset, the retained extents would not - * be whole regions (i.e. they are split between regular and - * guarded). - */ - if (!edata_slab_get(edata) || !maps_coalesce) { - assert(edata_size_get(edata) >= SC_LARGE_MINCLASS || - !maps_coalesce); - san_unguard_pages_two_sided(tsdn, ehooks, edata, - pac->emap); - } - } - - ecache_dalloc(tsdn, pac, ehooks, &pac->ecache_dirty, edata); - /* Purging of deallocated pages is deferred */ - *deferred_work_generated = true; -} - -static inline uint64_t -pac_ns_until_purge(tsdn_t *tsdn, decay_t *decay, size_t npages) { - if (malloc_mutex_trylock(tsdn, &decay->mtx)) { - /* Use minimal interval if decay is contended. */ - return BACKGROUND_THREAD_DEFERRED_MIN; - } - uint64_t result = decay_ns_until_purge(decay, npages, - ARENA_DEFERRED_PURGE_NPAGES_THRESHOLD); - - malloc_mutex_unlock(tsdn, &decay->mtx); - return result; -} - -static uint64_t -pac_time_until_deferred_work(tsdn_t *tsdn, pai_t *self) { - uint64_t time; - pac_t *pac = (pac_t *)self; - - time = pac_ns_until_purge(tsdn, - &pac->decay_dirty, - ecache_npages_get(&pac->ecache_dirty)); - if (time == BACKGROUND_THREAD_DEFERRED_MIN) { - return time; - } - - uint64_t muzzy = pac_ns_until_purge(tsdn, - &pac->decay_muzzy, - ecache_npages_get(&pac->ecache_muzzy)); - if (muzzy < time) { - time = muzzy; - } - return time; -} - -bool -pac_retain_grow_limit_get_set(tsdn_t *tsdn, pac_t *pac, size_t *old_limit, - size_t *new_limit) { - pszind_t new_ind JEMALLOC_CC_SILENCE_INIT(0); - if (new_limit != NULL) { - size_t limit = *new_limit; - /* Grow no more than the new limit. */ - if ((new_ind = sz_psz2ind(limit + 1) - 1) >= SC_NPSIZES) { - return true; - } - } - - malloc_mutex_lock(tsdn, &pac->grow_mtx); - if (old_limit != NULL) { - *old_limit = sz_pind2sz(pac->exp_grow.limit); - } - if (new_limit != NULL) { - pac->exp_grow.limit = new_ind; - } - malloc_mutex_unlock(tsdn, &pac->grow_mtx); - - return false; -} - -static size_t -pac_stash_decayed(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, - size_t npages_limit, size_t npages_decay_max, - edata_list_inactive_t *result) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 0); - ehooks_t *ehooks = pac_ehooks_get(pac); - - /* Stash extents according to npages_limit. */ - size_t nstashed = 0; - while (nstashed < npages_decay_max) { - edata_t *edata = ecache_evict(tsdn, pac, ehooks, ecache, - npages_limit); - if (edata == NULL) { - break; - } - edata_list_inactive_append(result, edata); - nstashed += edata_size_get(edata) >> LG_PAGE; - } - return nstashed; -} - -static size_t -pac_decay_stashed(tsdn_t *tsdn, pac_t *pac, decay_t *decay, - pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay, - edata_list_inactive_t *decay_extents) { - bool err; - - size_t nmadvise = 0; - size_t nunmapped = 0; - size_t npurged = 0; - - ehooks_t *ehooks = pac_ehooks_get(pac); - - bool try_muzzy = !fully_decay - && pac_decay_ms_get(pac, extent_state_muzzy) != 0; - - for (edata_t *edata = edata_list_inactive_first(decay_extents); edata != - NULL; edata = edata_list_inactive_first(decay_extents)) { - edata_list_inactive_remove(decay_extents, edata); - - size_t size = edata_size_get(edata); - size_t npages = size >> LG_PAGE; - - nmadvise++; - npurged += npages; - - switch (ecache->state) { - case extent_state_active: - not_reached(); - case extent_state_dirty: - if (try_muzzy) { - err = extent_purge_lazy_wrapper(tsdn, ehooks, - edata, /* offset */ 0, size); - if (!err) { - ecache_dalloc(tsdn, pac, ehooks, - &pac->ecache_muzzy, edata); - break; - } - } - JEMALLOC_FALLTHROUGH; - case extent_state_muzzy: - extent_dalloc_wrapper(tsdn, pac, ehooks, edata); - nunmapped += npages; - break; - case extent_state_retained: - default: - not_reached(); - } - } - - if (config_stats) { - LOCKEDINT_MTX_LOCK(tsdn, *pac->stats_mtx); - locked_inc_u64(tsdn, LOCKEDINT_MTX(*pac->stats_mtx), - &decay_stats->npurge, 1); - locked_inc_u64(tsdn, LOCKEDINT_MTX(*pac->stats_mtx), - &decay_stats->nmadvise, nmadvise); - locked_inc_u64(tsdn, LOCKEDINT_MTX(*pac->stats_mtx), - &decay_stats->purged, npurged); - LOCKEDINT_MTX_UNLOCK(tsdn, *pac->stats_mtx); - atomic_fetch_sub_zu(&pac->stats->pac_mapped, - nunmapped << LG_PAGE, ATOMIC_RELAXED); - } - - return npurged; -} - -/* - * npages_limit: Decay at most npages_decay_max pages without violating the - * invariant: (ecache_npages_get(ecache) >= npages_limit). We need an upper - * bound on number of pages in order to prevent unbounded growth (namely in - * stashed), otherwise unbounded new pages could be added to extents during the - * current decay run, so that the purging thread never finishes. - */ -static void -pac_decay_to_limit(tsdn_t *tsdn, pac_t *pac, decay_t *decay, - pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay, - size_t npages_limit, size_t npages_decay_max) { - witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), - WITNESS_RANK_CORE, 1); - - if (decay->purging || npages_decay_max == 0) { - return; - } - decay->purging = true; - malloc_mutex_unlock(tsdn, &decay->mtx); - - edata_list_inactive_t decay_extents; - edata_list_inactive_init(&decay_extents); - size_t npurge = pac_stash_decayed(tsdn, pac, ecache, npages_limit, - npages_decay_max, &decay_extents); - if (npurge != 0) { - size_t npurged = pac_decay_stashed(tsdn, pac, decay, - decay_stats, ecache, fully_decay, &decay_extents); - assert(npurged == npurge); - } - - malloc_mutex_lock(tsdn, &decay->mtx); - decay->purging = false; -} - -void -pac_decay_all(tsdn_t *tsdn, pac_t *pac, decay_t *decay, - pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay) { - malloc_mutex_assert_owner(tsdn, &decay->mtx); - pac_decay_to_limit(tsdn, pac, decay, decay_stats, ecache, fully_decay, - /* npages_limit */ 0, ecache_npages_get(ecache)); -} - -static void -pac_decay_try_purge(tsdn_t *tsdn, pac_t *pac, decay_t *decay, - pac_decay_stats_t *decay_stats, ecache_t *ecache, - size_t current_npages, size_t npages_limit) { - if (current_npages > npages_limit) { - pac_decay_to_limit(tsdn, pac, decay, decay_stats, ecache, - /* fully_decay */ false, npages_limit, - current_npages - npages_limit); - } -} - -bool -pac_maybe_decay_purge(tsdn_t *tsdn, pac_t *pac, decay_t *decay, - pac_decay_stats_t *decay_stats, ecache_t *ecache, - pac_purge_eagerness_t eagerness) { - malloc_mutex_assert_owner(tsdn, &decay->mtx); - - /* Purge all or nothing if the option is disabled. */ - ssize_t decay_ms = decay_ms_read(decay); - if (decay_ms <= 0) { - if (decay_ms == 0) { - pac_decay_to_limit(tsdn, pac, decay, decay_stats, - ecache, /* fully_decay */ false, - /* npages_limit */ 0, ecache_npages_get(ecache)); - } - return false; - } - - /* - * If the deadline has been reached, advance to the current epoch and - * purge to the new limit if necessary. Note that dirty pages created - * during the current epoch are not subject to purge until a future - * epoch, so as a result purging only happens during epoch advances, or - * being triggered by background threads (scheduled event). - */ - nstime_t time; - nstime_init_update(&time); - size_t npages_current = ecache_npages_get(ecache); - bool epoch_advanced = decay_maybe_advance_epoch(decay, &time, - npages_current); - if (eagerness == PAC_PURGE_ALWAYS - || (epoch_advanced && eagerness == PAC_PURGE_ON_EPOCH_ADVANCE)) { - size_t npages_limit = decay_npages_limit_get(decay); - pac_decay_try_purge(tsdn, pac, decay, decay_stats, ecache, - npages_current, npages_limit); - } - - return epoch_advanced; -} - -bool -pac_decay_ms_set(tsdn_t *tsdn, pac_t *pac, extent_state_t state, - ssize_t decay_ms, pac_purge_eagerness_t eagerness) { - decay_t *decay; - pac_decay_stats_t *decay_stats; - ecache_t *ecache; - pac_decay_data_get(pac, state, &decay, &decay_stats, &ecache); - - if (!decay_ms_valid(decay_ms)) { - return true; - } - - malloc_mutex_lock(tsdn, &decay->mtx); - /* - * Restart decay backlog from scratch, which may cause many dirty pages - * to be immediately purged. It would conceptually be possible to map - * the old backlog onto the new backlog, but there is no justification - * for such complexity since decay_ms changes are intended to be - * infrequent, either between the {-1, 0, >0} states, or a one-time - * arbitrary change during initial arena configuration. - */ - nstime_t cur_time; - nstime_init_update(&cur_time); - decay_reinit(decay, &cur_time, decay_ms); - pac_maybe_decay_purge(tsdn, pac, decay, decay_stats, ecache, eagerness); - malloc_mutex_unlock(tsdn, &decay->mtx); - - return false; -} - -ssize_t -pac_decay_ms_get(pac_t *pac, extent_state_t state) { - decay_t *decay; - pac_decay_stats_t *decay_stats; - ecache_t *ecache; - pac_decay_data_get(pac, state, &decay, &decay_stats, &ecache); - return decay_ms_read(decay); -} - -void -pac_reset(tsdn_t *tsdn, pac_t *pac) { - /* - * No-op for now; purging is still done at the arena-level. It should - * get moved in here, though. - */ - (void)tsdn; - (void)pac; -} - -void -pac_destroy(tsdn_t *tsdn, pac_t *pac) { - assert(ecache_npages_get(&pac->ecache_dirty) == 0); - assert(ecache_npages_get(&pac->ecache_muzzy) == 0); - /* - * Iterate over the retained extents and destroy them. This gives the - * extent allocator underlying the extent hooks an opportunity to unmap - * all retained memory without having to keep its own metadata - * structures. In practice, virtual memory for dss-allocated extents is - * leaked here, so best practice is to avoid dss for arenas to be - * destroyed, or provide custom extent hooks that track retained - * dss-based extents for later reuse. - */ - ehooks_t *ehooks = pac_ehooks_get(pac); - edata_t *edata; - while ((edata = ecache_evict(tsdn, pac, ehooks, - &pac->ecache_retained, 0)) != NULL) { - extent_destroy_wrapper(tsdn, pac, ehooks, edata); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/pages.c b/fluent-bit/lib/jemalloc-5.3.0/src/pages.c deleted file mode 100644 index 8c83a7de..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/pages.c +++ /dev/null @@ -1,824 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" - -#include "jemalloc/internal/pages.h" - -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/malloc_io.h" - -#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT -#include <sys/sysctl.h> -#ifdef __FreeBSD__ -#include <vm/vm_param.h> -#endif -#endif -#ifdef __NetBSD__ -#include <sys/bitops.h> /* ilog2 */ -#endif -#ifdef JEMALLOC_HAVE_VM_MAKE_TAG -#define PAGES_FD_TAG VM_MAKE_TAG(101U) -#else -#define PAGES_FD_TAG -1 -#endif - -/******************************************************************************/ -/* Data. */ - -/* Actual operating system page size, detected during bootstrap, <= PAGE. */ -static size_t os_page; - -#ifndef _WIN32 -# define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE) -# define PAGES_PROT_DECOMMIT (PROT_NONE) -static int mmap_flags; -#endif -static bool os_overcommits; - -const char *thp_mode_names[] = { - "default", - "always", - "never", - "not supported" -}; -thp_mode_t opt_thp = THP_MODE_DEFAULT; -thp_mode_t init_system_thp_mode; - -/* Runtime support for lazy purge. Irrelevant when !pages_can_purge_lazy. */ -static bool pages_can_purge_lazy_runtime = true; - -#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS -static int madvise_dont_need_zeros_is_faulty = -1; -/** - * Check that MADV_DONTNEED will actually zero pages on subsequent access. - * - * Since qemu does not support this, yet [1], and you can get very tricky - * assert if you will run program with jemalloc in use under qemu: - * - * <jemalloc>: ../contrib/jemalloc/src/extent.c:1195: Failed assertion: "p[i] == 0" - * - * [1]: https://patchwork.kernel.org/patch/10576637/ - */ -static int madvise_MADV_DONTNEED_zeroes_pages() -{ - int works = -1; - size_t size = PAGE; - - void * addr = mmap(NULL, size, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) { - malloc_write("<jemalloc>: Cannot allocate memory for " - "MADV_DONTNEED check\n"); - if (opt_abort) { - abort(); - } - } - - memset(addr, 'A', size); - if (madvise(addr, size, MADV_DONTNEED) == 0) { - works = memchr(addr, 'A', size) == NULL; - } else { - /* - * If madvise() does not support MADV_DONTNEED, then we can - * call it anyway, and use it's return code. - */ - works = 1; - } - - if (munmap(addr, size) != 0) { - malloc_write("<jemalloc>: Cannot deallocate memory for " - "MADV_DONTNEED check\n"); - if (opt_abort) { - abort(); - } - } - - return works; -} -#endif - -/******************************************************************************/ -/* - * Function prototypes for static functions that are referenced prior to - * definition. - */ - -static void os_pages_unmap(void *addr, size_t size); - -/******************************************************************************/ - -static void * -os_pages_map(void *addr, size_t size, size_t alignment, bool *commit) { - assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr); - assert(ALIGNMENT_CEILING(size, os_page) == size); - assert(size != 0); - - if (os_overcommits) { - *commit = true; - } - - void *ret; -#ifdef _WIN32 - /* - * If VirtualAlloc can't allocate at the given address when one is - * given, it fails and returns NULL. - */ - ret = VirtualAlloc(addr, size, MEM_RESERVE | (*commit ? MEM_COMMIT : 0), - PAGE_READWRITE); -#else - /* - * We don't use MAP_FIXED here, because it can cause the *replacement* - * of existing mappings, and we only want to create new mappings. - */ - { -#ifdef __NetBSD__ - /* - * On NetBSD PAGE for a platform is defined to the - * maximum page size of all machine architectures - * for that platform, so that we can use the same - * binaries across all machine architectures. - */ - if (alignment > os_page || PAGE > os_page) { - unsigned int a = ilog2(MAX(alignment, PAGE)); - mmap_flags |= MAP_ALIGNED(a); - } -#endif - int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; - - ret = mmap(addr, size, prot, mmap_flags, PAGES_FD_TAG, 0); - } - assert(ret != NULL); - - if (ret == MAP_FAILED) { - ret = NULL; - } else if (addr != NULL && ret != addr) { - /* - * We succeeded in mapping memory, but not in the right place. - */ - os_pages_unmap(ret, size); - ret = NULL; - } -#endif - assert(ret == NULL || (addr == NULL && ret != addr) || (addr != NULL && - ret == addr)); - return ret; -} - -static void * -os_pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size, - bool *commit) { - void *ret = (void *)((uintptr_t)addr + leadsize); - - assert(alloc_size >= leadsize + size); -#ifdef _WIN32 - os_pages_unmap(addr, alloc_size); - void *new_addr = os_pages_map(ret, size, PAGE, commit); - if (new_addr == ret) { - return ret; - } - if (new_addr != NULL) { - os_pages_unmap(new_addr, size); - } - return NULL; -#else - size_t trailsize = alloc_size - leadsize - size; - - if (leadsize != 0) { - os_pages_unmap(addr, leadsize); - } - if (trailsize != 0) { - os_pages_unmap((void *)((uintptr_t)ret + size), trailsize); - } - return ret; -#endif -} - -static void -os_pages_unmap(void *addr, size_t size) { - assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr); - assert(ALIGNMENT_CEILING(size, os_page) == size); - -#ifdef _WIN32 - if (VirtualFree(addr, 0, MEM_RELEASE) == 0) -#else - if (munmap(addr, size) == -1) -#endif - { - char buf[BUFERROR_BUF]; - - buferror(get_errno(), buf, sizeof(buf)); - malloc_printf("<jemalloc>: Error in " -#ifdef _WIN32 - "VirtualFree" -#else - "munmap" -#endif - "(): %s\n", buf); - if (opt_abort) { - abort(); - } - } -} - -static void * -pages_map_slow(size_t size, size_t alignment, bool *commit) { - size_t alloc_size = size + alignment - os_page; - /* Beware size_t wrap-around. */ - if (alloc_size < size) { - return NULL; - } - - void *ret; - do { - void *pages = os_pages_map(NULL, alloc_size, alignment, commit); - if (pages == NULL) { - return NULL; - } - size_t leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment) - - (uintptr_t)pages; - ret = os_pages_trim(pages, alloc_size, leadsize, size, commit); - } while (ret == NULL); - - assert(ret != NULL); - assert(PAGE_ADDR2BASE(ret) == ret); - return ret; -} - -void * -pages_map(void *addr, size_t size, size_t alignment, bool *commit) { - assert(alignment >= PAGE); - assert(ALIGNMENT_ADDR2BASE(addr, alignment) == addr); - -#if defined(__FreeBSD__) && defined(MAP_EXCL) - /* - * FreeBSD has mechanisms both to mmap at specific address without - * touching existing mappings, and to mmap with specific alignment. - */ - { - if (os_overcommits) { - *commit = true; - } - - int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; - int flags = mmap_flags; - - if (addr != NULL) { - flags |= MAP_FIXED | MAP_EXCL; - } else { - unsigned alignment_bits = ffs_zu(alignment); - assert(alignment_bits > 0); - flags |= MAP_ALIGNED(alignment_bits); - } - - void *ret = mmap(addr, size, prot, flags, -1, 0); - if (ret == MAP_FAILED) { - ret = NULL; - } - - return ret; - } -#endif - /* - * Ideally, there would be a way to specify alignment to mmap() (like - * NetBSD has), but in the absence of such a feature, we have to work - * hard to efficiently create aligned mappings. The reliable, but - * slow method is to create a mapping that is over-sized, then trim the - * excess. However, that always results in one or two calls to - * os_pages_unmap(), and it can leave holes in the process's virtual - * memory map if memory grows downward. - * - * Optimistically try mapping precisely the right amount before falling - * back to the slow method, with the expectation that the optimistic - * approach works most of the time. - */ - - void *ret = os_pages_map(addr, size, os_page, commit); - if (ret == NULL || ret == addr) { - return ret; - } - assert(addr == NULL); - if (ALIGNMENT_ADDR2OFFSET(ret, alignment) != 0) { - os_pages_unmap(ret, size); - return pages_map_slow(size, alignment, commit); - } - - assert(PAGE_ADDR2BASE(ret) == ret); - return ret; -} - -void -pages_unmap(void *addr, size_t size) { - assert(PAGE_ADDR2BASE(addr) == addr); - assert(PAGE_CEILING(size) == size); - - os_pages_unmap(addr, size); -} - -static bool -os_pages_commit(void *addr, size_t size, bool commit) { - assert(PAGE_ADDR2BASE(addr) == addr); - assert(PAGE_CEILING(size) == size); - -#ifdef _WIN32 - return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT, - PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT))); -#else - { - int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; - void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED, - PAGES_FD_TAG, 0); - if (result == MAP_FAILED) { - return true; - } - if (result != addr) { - /* - * We succeeded in mapping memory, but not in the right - * place. - */ - os_pages_unmap(result, size); - return true; - } - return false; - } -#endif -} - -static bool -pages_commit_impl(void *addr, size_t size, bool commit) { - if (os_overcommits) { - return true; - } - - return os_pages_commit(addr, size, commit); -} - -bool -pages_commit(void *addr, size_t size) { - return pages_commit_impl(addr, size, true); -} - -bool -pages_decommit(void *addr, size_t size) { - return pages_commit_impl(addr, size, false); -} - -void -pages_mark_guards(void *head, void *tail) { - assert(head != NULL || tail != NULL); - assert(head == NULL || tail == NULL || - (uintptr_t)head < (uintptr_t)tail); -#ifdef JEMALLOC_HAVE_MPROTECT - if (head != NULL) { - mprotect(head, PAGE, PROT_NONE); - } - if (tail != NULL) { - mprotect(tail, PAGE, PROT_NONE); - } -#else - /* Decommit sets to PROT_NONE / MEM_DECOMMIT. */ - if (head != NULL) { - os_pages_commit(head, PAGE, false); - } - if (tail != NULL) { - os_pages_commit(tail, PAGE, false); - } -#endif -} - -void -pages_unmark_guards(void *head, void *tail) { - assert(head != NULL || tail != NULL); - assert(head == NULL || tail == NULL || - (uintptr_t)head < (uintptr_t)tail); -#ifdef JEMALLOC_HAVE_MPROTECT - bool head_and_tail = (head != NULL) && (tail != NULL); - size_t range = head_and_tail ? - (uintptr_t)tail - (uintptr_t)head + PAGE : - SIZE_T_MAX; - /* - * The amount of work that the kernel does in mprotect depends on the - * range argument. SC_LARGE_MINCLASS is an arbitrary threshold chosen - * to prevent kernel from doing too much work that would outweigh the - * savings of performing one less system call. - */ - bool ranged_mprotect = head_and_tail && range <= SC_LARGE_MINCLASS; - if (ranged_mprotect) { - mprotect(head, range, PROT_READ | PROT_WRITE); - } else { - if (head != NULL) { - mprotect(head, PAGE, PROT_READ | PROT_WRITE); - } - if (tail != NULL) { - mprotect(tail, PAGE, PROT_READ | PROT_WRITE); - } - } -#else - if (head != NULL) { - os_pages_commit(head, PAGE, true); - } - if (tail != NULL) { - os_pages_commit(tail, PAGE, true); - } -#endif -} - -bool -pages_purge_lazy(void *addr, size_t size) { - assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr); - assert(PAGE_CEILING(size) == size); - - if (!pages_can_purge_lazy) { - return true; - } - if (!pages_can_purge_lazy_runtime) { - /* - * Built with lazy purge enabled, but detected it was not - * supported on the current system. - */ - return true; - } - -#ifdef _WIN32 - VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE); - return false; -#elif defined(JEMALLOC_PURGE_MADVISE_FREE) - return (madvise(addr, size, -# ifdef MADV_FREE - MADV_FREE -# else - JEMALLOC_MADV_FREE -# endif - ) != 0); -#elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \ - !defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS) - return (madvise(addr, size, MADV_DONTNEED) != 0); -#elif defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED) && \ - !defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS) - return (posix_madvise(addr, size, POSIX_MADV_DONTNEED) != 0); -#else - not_reached(); -#endif -} - -bool -pages_purge_forced(void *addr, size_t size) { - assert(PAGE_ADDR2BASE(addr) == addr); - assert(PAGE_CEILING(size) == size); - - if (!pages_can_purge_forced) { - return true; - } - -#if defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \ - defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS) - return (unlikely(madvise_dont_need_zeros_is_faulty) || - madvise(addr, size, MADV_DONTNEED) != 0); -#elif defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED) && \ - defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS) - return (unlikely(madvise_dont_need_zeros_is_faulty) || - posix_madvise(addr, size, POSIX_MADV_DONTNEED) != 0); -#elif defined(JEMALLOC_MAPS_COALESCE) - /* Try to overlay a new demand-zeroed mapping. */ - return pages_commit(addr, size); -#else - not_reached(); -#endif -} - -static bool -pages_huge_impl(void *addr, size_t size, bool aligned) { - if (aligned) { - assert(HUGEPAGE_ADDR2BASE(addr) == addr); - assert(HUGEPAGE_CEILING(size) == size); - } -#if defined(JEMALLOC_HAVE_MADVISE_HUGE) - return (madvise(addr, size, MADV_HUGEPAGE) != 0); -#elif defined(JEMALLOC_HAVE_MEMCNTL) - struct memcntl_mha m = {0}; - m.mha_cmd = MHA_MAPSIZE_VA; - m.mha_pagesize = HUGEPAGE; - return (memcntl(addr, size, MC_HAT_ADVISE, (caddr_t)&m, 0, 0) == 0); -#else - return true; -#endif -} - -bool -pages_huge(void *addr, size_t size) { - return pages_huge_impl(addr, size, true); -} - -static bool -pages_huge_unaligned(void *addr, size_t size) { - return pages_huge_impl(addr, size, false); -} - -static bool -pages_nohuge_impl(void *addr, size_t size, bool aligned) { - if (aligned) { - assert(HUGEPAGE_ADDR2BASE(addr) == addr); - assert(HUGEPAGE_CEILING(size) == size); - } - -#ifdef JEMALLOC_HAVE_MADVISE_HUGE - return (madvise(addr, size, MADV_NOHUGEPAGE) != 0); -#else - return false; -#endif -} - -bool -pages_nohuge(void *addr, size_t size) { - return pages_nohuge_impl(addr, size, true); -} - -static bool -pages_nohuge_unaligned(void *addr, size_t size) { - return pages_nohuge_impl(addr, size, false); -} - -bool -pages_dontdump(void *addr, size_t size) { - assert(PAGE_ADDR2BASE(addr) == addr); - assert(PAGE_CEILING(size) == size); -#if defined(JEMALLOC_MADVISE_DONTDUMP) - return madvise(addr, size, MADV_DONTDUMP) != 0; -#elif defined(JEMALLOC_MADVISE_NOCORE) - return madvise(addr, size, MADV_NOCORE) != 0; -#else - return false; -#endif -} - -bool -pages_dodump(void *addr, size_t size) { - assert(PAGE_ADDR2BASE(addr) == addr); - assert(PAGE_CEILING(size) == size); -#if defined(JEMALLOC_MADVISE_DONTDUMP) - return madvise(addr, size, MADV_DODUMP) != 0; -#elif defined(JEMALLOC_MADVISE_NOCORE) - return madvise(addr, size, MADV_CORE) != 0; -#else - return false; -#endif -} - - -static size_t -os_page_detect(void) { -#ifdef _WIN32 - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; -#elif defined(__FreeBSD__) - /* - * This returns the value obtained from - * the auxv vector, avoiding a syscall. - */ - return getpagesize(); -#else - long result = sysconf(_SC_PAGESIZE); - if (result == -1) { - return LG_PAGE; - } - return (size_t)result; -#endif -} - -#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT -static bool -os_overcommits_sysctl(void) { - int vm_overcommit; - size_t sz; - - sz = sizeof(vm_overcommit); -#if defined(__FreeBSD__) && defined(VM_OVERCOMMIT) - int mib[2]; - - mib[0] = CTL_VM; - mib[1] = VM_OVERCOMMIT; - if (sysctl(mib, 2, &vm_overcommit, &sz, NULL, 0) != 0) { - return false; /* Error. */ - } -#else - if (sysctlbyname("vm.overcommit", &vm_overcommit, &sz, NULL, 0) != 0) { - return false; /* Error. */ - } -#endif - - return ((vm_overcommit & 0x3) == 0); -} -#endif - -#ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY -/* - * Use syscall(2) rather than {open,read,close}(2) when possible to avoid - * reentry during bootstrapping if another library has interposed system call - * wrappers. - */ -static bool -os_overcommits_proc(void) { - int fd; - char buf[1]; - -#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open) - #if defined(O_CLOEXEC) - fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY | - O_CLOEXEC); - #else - fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY); - if (fd != -1) { - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); - } - #endif -#elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat) - #if defined(O_CLOEXEC) - fd = (int)syscall(SYS_openat, - AT_FDCWD, "/proc/sys/vm/overcommit_memory", O_RDONLY | O_CLOEXEC); - #else - fd = (int)syscall(SYS_openat, - AT_FDCWD, "/proc/sys/vm/overcommit_memory", O_RDONLY); - if (fd != -1) { - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); - } - #endif -#else - #if defined(O_CLOEXEC) - fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY | O_CLOEXEC); - #else - fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY); - if (fd != -1) { - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); - } - #endif -#endif - - if (fd == -1) { - return false; /* Error. */ - } - - ssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf)); -#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close) - syscall(SYS_close, fd); -#else - close(fd); -#endif - - if (nread < 1) { - return false; /* Error. */ - } - /* - * /proc/sys/vm/overcommit_memory meanings: - * 0: Heuristic overcommit. - * 1: Always overcommit. - * 2: Never overcommit. - */ - return (buf[0] == '0' || buf[0] == '1'); -} -#endif - -void -pages_set_thp_state (void *ptr, size_t size) { - if (opt_thp == thp_mode_default || opt_thp == init_system_thp_mode) { - return; - } - assert(opt_thp != thp_mode_not_supported && - init_system_thp_mode != thp_mode_not_supported); - - if (opt_thp == thp_mode_always - && init_system_thp_mode != thp_mode_never) { - assert(init_system_thp_mode == thp_mode_default); - pages_huge_unaligned(ptr, size); - } else if (opt_thp == thp_mode_never) { - assert(init_system_thp_mode == thp_mode_default || - init_system_thp_mode == thp_mode_always); - pages_nohuge_unaligned(ptr, size); - } -} - -static void -init_thp_state(void) { - if (!have_madvise_huge && !have_memcntl) { - if (metadata_thp_enabled() && opt_abort) { - malloc_write("<jemalloc>: no MADV_HUGEPAGE support\n"); - abort(); - } - goto label_error; - } -#if defined(JEMALLOC_HAVE_MADVISE_HUGE) - static const char sys_state_madvise[] = "always [madvise] never\n"; - static const char sys_state_always[] = "[always] madvise never\n"; - static const char sys_state_never[] = "always madvise [never]\n"; - char buf[sizeof(sys_state_madvise)]; - -#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open) - int fd = (int)syscall(SYS_open, - "/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY); -#elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat) - int fd = (int)syscall(SYS_openat, - AT_FDCWD, "/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY); -#else - int fd = open("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY); -#endif - if (fd == -1) { - goto label_error; - } - - ssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf)); -#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close) - syscall(SYS_close, fd); -#else - close(fd); -#endif - - if (nread < 0) { - goto label_error; - } - - if (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) { - init_system_thp_mode = thp_mode_default; - } else if (strncmp(buf, sys_state_always, (size_t)nread) == 0) { - init_system_thp_mode = thp_mode_always; - } else if (strncmp(buf, sys_state_never, (size_t)nread) == 0) { - init_system_thp_mode = thp_mode_never; - } else { - goto label_error; - } - return; -#elif defined(JEMALLOC_HAVE_MEMCNTL) - init_system_thp_mode = thp_mode_default; - return; -#endif -label_error: - opt_thp = init_system_thp_mode = thp_mode_not_supported; -} - -bool -pages_boot(void) { - os_page = os_page_detect(); - if (os_page > PAGE) { - malloc_write("<jemalloc>: Unsupported system page size\n"); - if (opt_abort) { - abort(); - } - return true; - } - -#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS - if (!opt_trust_madvise) { - madvise_dont_need_zeros_is_faulty = !madvise_MADV_DONTNEED_zeroes_pages(); - if (madvise_dont_need_zeros_is_faulty) { - malloc_write("<jemalloc>: MADV_DONTNEED does not work (memset will be used instead)\n"); - malloc_write("<jemalloc>: (This is the expected behaviour if you are running under QEMU)\n"); - } - } else { - /* In case opt_trust_madvise is disable, - * do not do runtime check */ - madvise_dont_need_zeros_is_faulty = 0; - } -#endif - -#ifndef _WIN32 - mmap_flags = MAP_PRIVATE | MAP_ANON; -#endif - -#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT - os_overcommits = os_overcommits_sysctl(); -#elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY) - os_overcommits = os_overcommits_proc(); -# ifdef MAP_NORESERVE - if (os_overcommits) { - mmap_flags |= MAP_NORESERVE; - } -# endif -#elif defined(__NetBSD__) - os_overcommits = true; -#else - os_overcommits = false; -#endif - - init_thp_state(); - -#ifdef __FreeBSD__ - /* - * FreeBSD doesn't need the check; madvise(2) is known to work. - */ -#else - /* Detect lazy purge runtime support. */ - if (pages_can_purge_lazy) { - bool committed = false; - void *madv_free_page = os_pages_map(NULL, PAGE, PAGE, &committed); - if (madv_free_page == NULL) { - return true; - } - assert(pages_can_purge_lazy_runtime); - if (pages_purge_lazy(madv_free_page, PAGE)) { - pages_can_purge_lazy_runtime = false; - } - os_pages_unmap(madv_free_page, PAGE); - } -#endif - - return false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/pai.c b/fluent-bit/lib/jemalloc-5.3.0/src/pai.c deleted file mode 100644 index 45c87729..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/pai.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -size_t -pai_alloc_batch_default(tsdn_t *tsdn, pai_t *self, size_t size, size_t nallocs, - edata_list_active_t *results, bool *deferred_work_generated) { - for (size_t i = 0; i < nallocs; i++) { - bool deferred_by_alloc = false; - edata_t *edata = pai_alloc(tsdn, self, size, PAGE, - /* zero */ false, /* guarded */ false, - /* frequent_reuse */ false, &deferred_by_alloc); - *deferred_work_generated |= deferred_by_alloc; - if (edata == NULL) { - return i; - } - edata_list_active_append(results, edata); - } - return nallocs; -} - -void -pai_dalloc_batch_default(tsdn_t *tsdn, pai_t *self, - edata_list_active_t *list, bool *deferred_work_generated) { - edata_t *edata; - while ((edata = edata_list_active_first(list)) != NULL) { - bool deferred_by_dalloc = false; - edata_list_active_remove(list, edata); - pai_dalloc(tsdn, self, edata, &deferred_by_dalloc); - *deferred_work_generated |= deferred_by_dalloc; - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/peak_event.c b/fluent-bit/lib/jemalloc-5.3.0/src/peak_event.c deleted file mode 100644 index 4093fbcc..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/peak_event.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/peak_event.h" - -#include "jemalloc/internal/activity_callback.h" -#include "jemalloc/internal/peak.h" - -/* - * Update every 64K by default. We're not exposing this as a configuration - * option for now; we don't want to bind ourselves too tightly to any particular - * performance requirements for small values, or guarantee that we'll even be - * able to provide fine-grained accuracy. - */ -#define PEAK_EVENT_WAIT (64 * 1024) - -/* Update the peak with current tsd state. */ -void -peak_event_update(tsd_t *tsd) { - uint64_t alloc = tsd_thread_allocated_get(tsd); - uint64_t dalloc = tsd_thread_deallocated_get(tsd); - peak_t *peak = tsd_peakp_get(tsd); - peak_update(peak, alloc, dalloc); -} - -static void -peak_event_activity_callback(tsd_t *tsd) { - activity_callback_thunk_t *thunk = tsd_activity_callback_thunkp_get( - tsd); - uint64_t alloc = tsd_thread_allocated_get(tsd); - uint64_t dalloc = tsd_thread_deallocated_get(tsd); - if (thunk->callback != NULL) { - thunk->callback(thunk->uctx, alloc, dalloc); - } -} - -/* Set current state to zero. */ -void -peak_event_zero(tsd_t *tsd) { - uint64_t alloc = tsd_thread_allocated_get(tsd); - uint64_t dalloc = tsd_thread_deallocated_get(tsd); - peak_t *peak = tsd_peakp_get(tsd); - peak_set_zero(peak, alloc, dalloc); -} - -uint64_t -peak_event_max(tsd_t *tsd) { - peak_t *peak = tsd_peakp_get(tsd); - return peak_max(peak); -} - -uint64_t -peak_alloc_new_event_wait(tsd_t *tsd) { - return PEAK_EVENT_WAIT; -} - -uint64_t -peak_alloc_postponed_event_wait(tsd_t *tsd) { - return TE_MIN_START_WAIT; -} - -void -peak_alloc_event_handler(tsd_t *tsd, uint64_t elapsed) { - peak_event_update(tsd); - peak_event_activity_callback(tsd); -} - -uint64_t -peak_dalloc_new_event_wait(tsd_t *tsd) { - return PEAK_EVENT_WAIT; -} - -uint64_t -peak_dalloc_postponed_event_wait(tsd_t *tsd) { - return TE_MIN_START_WAIT; -} - -void -peak_dalloc_event_handler(tsd_t *tsd, uint64_t elapsed) { - peak_event_update(tsd); - peak_event_activity_callback(tsd); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/prof.c b/fluent-bit/lib/jemalloc-5.3.0/src/prof.c deleted file mode 100644 index 7a6d5d56..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/prof.c +++ /dev/null @@ -1,789 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/ctl.h" -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/counter.h" -#include "jemalloc/internal/prof_data.h" -#include "jemalloc/internal/prof_log.h" -#include "jemalloc/internal/prof_recent.h" -#include "jemalloc/internal/prof_stats.h" -#include "jemalloc/internal/prof_sys.h" -#include "jemalloc/internal/prof_hook.h" -#include "jemalloc/internal/thread_event.h" - -/* - * This file implements the profiling "APIs" needed by other parts of jemalloc, - * and also manages the relevant "operational" data, mainly options and mutexes; - * the core profiling data structures are encapsulated in prof_data.c. - */ - -/******************************************************************************/ - -/* Data. */ - -bool opt_prof = false; -bool opt_prof_active = true; -bool opt_prof_thread_active_init = true; -size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT; -ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT; -bool opt_prof_gdump = false; -bool opt_prof_final = false; -bool opt_prof_leak = false; -bool opt_prof_leak_error = false; -bool opt_prof_accum = false; -char opt_prof_prefix[PROF_DUMP_FILENAME_LEN]; -bool opt_prof_sys_thread_name = false; -bool opt_prof_unbias = true; - -/* Accessed via prof_sample_event_handler(). */ -static counter_accum_t prof_idump_accumulated; - -/* - * Initialized as opt_prof_active, and accessed via - * prof_active_[gs]et{_unlocked,}(). - */ -bool prof_active_state; -static malloc_mutex_t prof_active_mtx; - -/* - * Initialized as opt_prof_thread_active_init, and accessed via - * prof_thread_active_init_[gs]et(). - */ -static bool prof_thread_active_init; -static malloc_mutex_t prof_thread_active_init_mtx; - -/* - * Initialized as opt_prof_gdump, and accessed via - * prof_gdump_[gs]et{_unlocked,}(). - */ -bool prof_gdump_val; -static malloc_mutex_t prof_gdump_mtx; - -uint64_t prof_interval = 0; - -size_t lg_prof_sample; - -static uint64_t next_thr_uid; -static malloc_mutex_t next_thr_uid_mtx; - -/* Do not dump any profiles until bootstrapping is complete. */ -bool prof_booted = false; - -/* Logically a prof_backtrace_hook_t. */ -atomic_p_t prof_backtrace_hook; - -/* Logically a prof_dump_hook_t. */ -atomic_p_t prof_dump_hook; - -/******************************************************************************/ - -void -prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx) { - cassert(config_prof); - - if (tsd_reentrancy_level_get(tsd) > 0) { - assert((uintptr_t)tctx == (uintptr_t)1U); - return; - } - - if ((uintptr_t)tctx > (uintptr_t)1U) { - malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock); - tctx->prepared = false; - prof_tctx_try_destroy(tsd, tctx); - } -} - -void -prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size, - size_t usize, prof_tctx_t *tctx) { - cassert(config_prof); - - if (opt_prof_sys_thread_name) { - prof_sys_thread_name_fetch(tsd); - } - - edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, - ptr); - prof_info_set(tsd, edata, tctx, size); - - szind_t szind = sz_size2index(usize); - - malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock); - /* - * We need to do these map lookups while holding the lock, to avoid the - * possibility of races with prof_reset calls, which update the map and - * then acquire the lock. This actually still leaves a data race on the - * contents of the unbias map, but we have not yet gone through and - * atomic-ified the prof module, and compilers are not yet causing us - * issues. The key thing is to make sure that, if we read garbage data, - * the prof_reset call is about to mark our tctx as expired before any - * dumping of our corrupted output is attempted. - */ - size_t shifted_unbiased_cnt = prof_shifted_unbiased_cnt[szind]; - size_t unbiased_bytes = prof_unbiased_sz[szind]; - tctx->cnts.curobjs++; - tctx->cnts.curobjs_shifted_unbiased += shifted_unbiased_cnt; - tctx->cnts.curbytes += usize; - tctx->cnts.curbytes_unbiased += unbiased_bytes; - if (opt_prof_accum) { - tctx->cnts.accumobjs++; - tctx->cnts.accumobjs_shifted_unbiased += shifted_unbiased_cnt; - tctx->cnts.accumbytes += usize; - tctx->cnts.accumbytes_unbiased += unbiased_bytes; - } - bool record_recent = prof_recent_alloc_prepare(tsd, tctx); - tctx->prepared = false; - malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock); - if (record_recent) { - assert(tctx == edata_prof_tctx_get(edata)); - prof_recent_alloc(tsd, edata, size, usize); - } - - if (opt_prof_stats) { - prof_stats_inc(tsd, szind, size); - } -} - -void -prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_info_t *prof_info) { - cassert(config_prof); - - assert(prof_info != NULL); - prof_tctx_t *tctx = prof_info->alloc_tctx; - assert((uintptr_t)tctx > (uintptr_t)1U); - - szind_t szind = sz_size2index(usize); - malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock); - - assert(tctx->cnts.curobjs > 0); - assert(tctx->cnts.curbytes >= usize); - /* - * It's not correct to do equivalent asserts for unbiased bytes, because - * of the potential for races with prof.reset calls. The map contents - * should really be atomic, but we have not atomic-ified the prof module - * yet. - */ - tctx->cnts.curobjs--; - tctx->cnts.curobjs_shifted_unbiased -= prof_shifted_unbiased_cnt[szind]; - tctx->cnts.curbytes -= usize; - tctx->cnts.curbytes_unbiased -= prof_unbiased_sz[szind]; - - prof_try_log(tsd, usize, prof_info); - - prof_tctx_try_destroy(tsd, tctx); - - if (opt_prof_stats) { - prof_stats_dec(tsd, szind, prof_info->alloc_size); - } -} - -prof_tctx_t * -prof_tctx_create(tsd_t *tsd) { - if (!tsd_nominal(tsd) || tsd_reentrancy_level_get(tsd) > 0) { - return NULL; - } - - prof_tdata_t *tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return NULL; - } - - prof_bt_t bt; - bt_init(&bt, tdata->vec); - prof_backtrace(tsd, &bt); - return prof_lookup(tsd, &bt); -} - -/* - * The bodies of this function and prof_leakcheck() are compiled out unless heap - * profiling is enabled, so that it is possible to compile jemalloc with - * floating point support completely disabled. Avoiding floating point code is - * important on memory-constrained systems, but it also enables a workaround for - * versions of glibc that don't properly save/restore floating point registers - * during dynamic lazy symbol loading (which internally calls into whatever - * malloc implementation happens to be integrated into the application). Note - * that some compilers (e.g. gcc 4.8) may use floating point registers for fast - * memory moves, so jemalloc must be compiled with such optimizations disabled - * (e.g. - * -mno-sse) in order for the workaround to be complete. - */ -uint64_t -prof_sample_new_event_wait(tsd_t *tsd) { -#ifdef JEMALLOC_PROF - if (lg_prof_sample == 0) { - return TE_MIN_START_WAIT; - } - - /* - * Compute sample interval as a geometrically distributed random - * variable with mean (2^lg_prof_sample). - * - * __ __ - * | log(u) | 1 - * bytes_until_sample = | -------- |, where p = --------------- - * | log(1-p) | lg_prof_sample - * 2 - * - * For more information on the math, see: - * - * Non-Uniform Random Variate Generation - * Luc Devroye - * Springer-Verlag, New York, 1986 - * pp 500 - * (http://luc.devroye.org/rnbookindex.html) - * - * In the actual computation, there's a non-zero probability that our - * pseudo random number generator generates an exact 0, and to avoid - * log(0), we set u to 1.0 in case r is 0. Therefore u effectively is - * uniformly distributed in (0, 1] instead of [0, 1). Further, rather - * than taking the ceiling, we take the floor and then add 1, since - * otherwise bytes_until_sample would be 0 if u is exactly 1.0. - */ - uint64_t r = prng_lg_range_u64(tsd_prng_statep_get(tsd), 53); - double u = (r == 0U) ? 1.0 : (double)r * (1.0/9007199254740992.0L); - return (uint64_t)(log(u) / - log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample)))) - + (uint64_t)1U; -#else - not_reached(); - return TE_MAX_START_WAIT; -#endif -} - -uint64_t -prof_sample_postponed_event_wait(tsd_t *tsd) { - /* - * The postponed wait time for prof sample event is computed as if we - * want a new wait time (i.e. as if the event were triggered). If we - * instead postpone to the immediate next allocation, like how we're - * handling the other events, then we can have sampling bias, if e.g. - * the allocation immediately following a reentrancy always comes from - * the same stack trace. - */ - return prof_sample_new_event_wait(tsd); -} - -void -prof_sample_event_handler(tsd_t *tsd, uint64_t elapsed) { - cassert(config_prof); - assert(elapsed > 0 && elapsed != TE_INVALID_ELAPSED); - if (prof_interval == 0 || !prof_active_get_unlocked()) { - return; - } - if (counter_accum(tsd_tsdn(tsd), &prof_idump_accumulated, elapsed)) { - prof_idump(tsd_tsdn(tsd)); - } -} - -static void -prof_fdump(void) { - tsd_t *tsd; - - cassert(config_prof); - assert(opt_prof_final); - - if (!prof_booted) { - return; - } - tsd = tsd_fetch(); - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_fdump_impl(tsd); -} - -static bool -prof_idump_accum_init(void) { - cassert(config_prof); - - return counter_accum_init(&prof_idump_accumulated, prof_interval); -} - -void -prof_idump(tsdn_t *tsdn) { - tsd_t *tsd; - prof_tdata_t *tdata; - - cassert(config_prof); - - if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) { - return; - } - tsd = tsdn_tsd(tsdn); - if (tsd_reentrancy_level_get(tsd) > 0) { - return; - } - - tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return; - } - if (tdata->enq) { - tdata->enq_idump = true; - return; - } - - prof_idump_impl(tsd); -} - -bool -prof_mdump(tsd_t *tsd, const char *filename) { - cassert(config_prof); - assert(tsd_reentrancy_level_get(tsd) == 0); - - if (!opt_prof || !prof_booted) { - return true; - } - - return prof_mdump_impl(tsd, filename); -} - -void -prof_gdump(tsdn_t *tsdn) { - tsd_t *tsd; - prof_tdata_t *tdata; - - cassert(config_prof); - - if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) { - return; - } - tsd = tsdn_tsd(tsdn); - if (tsd_reentrancy_level_get(tsd) > 0) { - return; - } - - tdata = prof_tdata_get(tsd, false); - if (tdata == NULL) { - return; - } - if (tdata->enq) { - tdata->enq_gdump = true; - return; - } - - prof_gdump_impl(tsd); -} - -static uint64_t -prof_thr_uid_alloc(tsdn_t *tsdn) { - uint64_t thr_uid; - - malloc_mutex_lock(tsdn, &next_thr_uid_mtx); - thr_uid = next_thr_uid; - next_thr_uid++; - malloc_mutex_unlock(tsdn, &next_thr_uid_mtx); - - return thr_uid; -} - -prof_tdata_t * -prof_tdata_init(tsd_t *tsd) { - return prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0, - NULL, prof_thread_active_init_get(tsd_tsdn(tsd))); -} - -prof_tdata_t * -prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) { - uint64_t thr_uid = tdata->thr_uid; - uint64_t thr_discrim = tdata->thr_discrim + 1; - char *thread_name = (tdata->thread_name != NULL) ? - prof_thread_name_alloc(tsd, tdata->thread_name) : NULL; - bool active = tdata->active; - - prof_tdata_detach(tsd, tdata); - return prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name, - active); -} - -void -prof_tdata_cleanup(tsd_t *tsd) { - prof_tdata_t *tdata; - - if (!config_prof) { - return; - } - - tdata = tsd_prof_tdata_get(tsd); - if (tdata != NULL) { - prof_tdata_detach(tsd, tdata); - } -} - -bool -prof_active_get(tsdn_t *tsdn) { - bool prof_active_current; - - prof_active_assert(); - malloc_mutex_lock(tsdn, &prof_active_mtx); - prof_active_current = prof_active_state; - malloc_mutex_unlock(tsdn, &prof_active_mtx); - return prof_active_current; -} - -bool -prof_active_set(tsdn_t *tsdn, bool active) { - bool prof_active_old; - - prof_active_assert(); - malloc_mutex_lock(tsdn, &prof_active_mtx); - prof_active_old = prof_active_state; - prof_active_state = active; - malloc_mutex_unlock(tsdn, &prof_active_mtx); - prof_active_assert(); - return prof_active_old; -} - -const char * -prof_thread_name_get(tsd_t *tsd) { - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t *tdata; - - tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return ""; - } - return (tdata->thread_name != NULL ? tdata->thread_name : ""); -} - -int -prof_thread_name_set(tsd_t *tsd, const char *thread_name) { - if (opt_prof_sys_thread_name) { - return ENOENT; - } else { - return prof_thread_name_set_impl(tsd, thread_name); - } -} - -bool -prof_thread_active_get(tsd_t *tsd) { - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t *tdata; - - tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return false; - } - return tdata->active; -} - -bool -prof_thread_active_set(tsd_t *tsd, bool active) { - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t *tdata; - - tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return true; - } - tdata->active = active; - return false; -} - -bool -prof_thread_active_init_get(tsdn_t *tsdn) { - bool active_init; - - malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx); - active_init = prof_thread_active_init; - malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx); - return active_init; -} - -bool -prof_thread_active_init_set(tsdn_t *tsdn, bool active_init) { - bool active_init_old; - - malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx); - active_init_old = prof_thread_active_init; - prof_thread_active_init = active_init; - malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx); - return active_init_old; -} - -bool -prof_gdump_get(tsdn_t *tsdn) { - bool prof_gdump_current; - - malloc_mutex_lock(tsdn, &prof_gdump_mtx); - prof_gdump_current = prof_gdump_val; - malloc_mutex_unlock(tsdn, &prof_gdump_mtx); - return prof_gdump_current; -} - -bool -prof_gdump_set(tsdn_t *tsdn, bool gdump) { - bool prof_gdump_old; - - malloc_mutex_lock(tsdn, &prof_gdump_mtx); - prof_gdump_old = prof_gdump_val; - prof_gdump_val = gdump; - malloc_mutex_unlock(tsdn, &prof_gdump_mtx); - return prof_gdump_old; -} - -void -prof_backtrace_hook_set(prof_backtrace_hook_t hook) { - atomic_store_p(&prof_backtrace_hook, hook, ATOMIC_RELEASE); -} - -prof_backtrace_hook_t -prof_backtrace_hook_get() { - return (prof_backtrace_hook_t)atomic_load_p(&prof_backtrace_hook, - ATOMIC_ACQUIRE); -} - -void -prof_dump_hook_set(prof_dump_hook_t hook) { - atomic_store_p(&prof_dump_hook, hook, ATOMIC_RELEASE); -} - -prof_dump_hook_t -prof_dump_hook_get() { - return (prof_dump_hook_t)atomic_load_p(&prof_dump_hook, - ATOMIC_ACQUIRE); -} - -void -prof_boot0(void) { - cassert(config_prof); - - memcpy(opt_prof_prefix, PROF_PREFIX_DEFAULT, - sizeof(PROF_PREFIX_DEFAULT)); -} - -void -prof_boot1(void) { - cassert(config_prof); - - /* - * opt_prof must be in its final state before any arenas are - * initialized, so this function must be executed early. - */ - if (opt_prof_leak_error && !opt_prof_leak) { - opt_prof_leak = true; - } - - if (opt_prof_leak && !opt_prof) { - /* - * Enable opt_prof, but in such a way that profiles are never - * automatically dumped. - */ - opt_prof = true; - opt_prof_gdump = false; - } else if (opt_prof) { - if (opt_lg_prof_interval >= 0) { - prof_interval = (((uint64_t)1U) << - opt_lg_prof_interval); - } - } -} - -bool -prof_boot2(tsd_t *tsd, base_t *base) { - cassert(config_prof); - - /* - * Initialize the global mutexes unconditionally to maintain correct - * stats when opt_prof is false. - */ - if (malloc_mutex_init(&prof_active_mtx, "prof_active", - WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&prof_gdump_mtx, "prof_gdump", - WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&prof_thread_active_init_mtx, - "prof_thread_active_init", WITNESS_RANK_PROF_THREAD_ACTIVE_INIT, - malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&bt2gctx_mtx, "prof_bt2gctx", - WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&tdatas_mtx, "prof_tdatas", - WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid", - WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&prof_stats_mtx, "prof_stats", - WITNESS_RANK_PROF_STATS, malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&prof_dump_filename_mtx, - "prof_dump_filename", WITNESS_RANK_PROF_DUMP_FILENAME, - malloc_mutex_rank_exclusive)) { - return true; - } - if (malloc_mutex_init(&prof_dump_mtx, "prof_dump", - WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) { - return true; - } - - if (opt_prof) { - lg_prof_sample = opt_lg_prof_sample; - prof_unbias_map_init(); - prof_active_state = opt_prof_active; - prof_gdump_val = opt_prof_gdump; - prof_thread_active_init = opt_prof_thread_active_init; - - if (prof_data_init(tsd)) { - return true; - } - - next_thr_uid = 0; - if (prof_idump_accum_init()) { - return true; - } - - if (opt_prof_final && opt_prof_prefix[0] != '\0' && - atexit(prof_fdump) != 0) { - malloc_write("<jemalloc>: Error in atexit()\n"); - if (opt_abort) { - abort(); - } - } - - if (prof_log_init(tsd)) { - return true; - } - - if (prof_recent_init()) { - return true; - } - - prof_base = base; - - gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd), base, - PROF_NCTX_LOCKS * sizeof(malloc_mutex_t), CACHELINE); - if (gctx_locks == NULL) { - return true; - } - for (unsigned i = 0; i < PROF_NCTX_LOCKS; i++) { - if (malloc_mutex_init(&gctx_locks[i], "prof_gctx", - WITNESS_RANK_PROF_GCTX, - malloc_mutex_rank_exclusive)) { - return true; - } - } - - tdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd), base, - PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t), CACHELINE); - if (tdata_locks == NULL) { - return true; - } - for (unsigned i = 0; i < PROF_NTDATA_LOCKS; i++) { - if (malloc_mutex_init(&tdata_locks[i], "prof_tdata", - WITNESS_RANK_PROF_TDATA, - malloc_mutex_rank_exclusive)) { - return true; - } - } - - prof_unwind_init(); - prof_hooks_init(); - } - prof_booted = true; - - return false; -} - -void -prof_prefork0(tsdn_t *tsdn) { - if (config_prof && opt_prof) { - unsigned i; - - malloc_mutex_prefork(tsdn, &prof_dump_mtx); - malloc_mutex_prefork(tsdn, &bt2gctx_mtx); - malloc_mutex_prefork(tsdn, &tdatas_mtx); - for (i = 0; i < PROF_NTDATA_LOCKS; i++) { - malloc_mutex_prefork(tsdn, &tdata_locks[i]); - } - malloc_mutex_prefork(tsdn, &log_mtx); - for (i = 0; i < PROF_NCTX_LOCKS; i++) { - malloc_mutex_prefork(tsdn, &gctx_locks[i]); - } - malloc_mutex_prefork(tsdn, &prof_recent_dump_mtx); - } -} - -void -prof_prefork1(tsdn_t *tsdn) { - if (config_prof && opt_prof) { - counter_prefork(tsdn, &prof_idump_accumulated); - malloc_mutex_prefork(tsdn, &prof_active_mtx); - malloc_mutex_prefork(tsdn, &prof_dump_filename_mtx); - malloc_mutex_prefork(tsdn, &prof_gdump_mtx); - malloc_mutex_prefork(tsdn, &prof_recent_alloc_mtx); - malloc_mutex_prefork(tsdn, &prof_stats_mtx); - malloc_mutex_prefork(tsdn, &next_thr_uid_mtx); - malloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx); - } -} - -void -prof_postfork_parent(tsdn_t *tsdn) { - if (config_prof && opt_prof) { - unsigned i; - - malloc_mutex_postfork_parent(tsdn, - &prof_thread_active_init_mtx); - malloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx); - malloc_mutex_postfork_parent(tsdn, &prof_stats_mtx); - malloc_mutex_postfork_parent(tsdn, &prof_recent_alloc_mtx); - malloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx); - malloc_mutex_postfork_parent(tsdn, &prof_dump_filename_mtx); - malloc_mutex_postfork_parent(tsdn, &prof_active_mtx); - counter_postfork_parent(tsdn, &prof_idump_accumulated); - malloc_mutex_postfork_parent(tsdn, &prof_recent_dump_mtx); - for (i = 0; i < PROF_NCTX_LOCKS; i++) { - malloc_mutex_postfork_parent(tsdn, &gctx_locks[i]); - } - malloc_mutex_postfork_parent(tsdn, &log_mtx); - for (i = 0; i < PROF_NTDATA_LOCKS; i++) { - malloc_mutex_postfork_parent(tsdn, &tdata_locks[i]); - } - malloc_mutex_postfork_parent(tsdn, &tdatas_mtx); - malloc_mutex_postfork_parent(tsdn, &bt2gctx_mtx); - malloc_mutex_postfork_parent(tsdn, &prof_dump_mtx); - } -} - -void -prof_postfork_child(tsdn_t *tsdn) { - if (config_prof && opt_prof) { - unsigned i; - - malloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx); - malloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx); - malloc_mutex_postfork_child(tsdn, &prof_stats_mtx); - malloc_mutex_postfork_child(tsdn, &prof_recent_alloc_mtx); - malloc_mutex_postfork_child(tsdn, &prof_gdump_mtx); - malloc_mutex_postfork_child(tsdn, &prof_dump_filename_mtx); - malloc_mutex_postfork_child(tsdn, &prof_active_mtx); - counter_postfork_child(tsdn, &prof_idump_accumulated); - malloc_mutex_postfork_child(tsdn, &prof_recent_dump_mtx); - for (i = 0; i < PROF_NCTX_LOCKS; i++) { - malloc_mutex_postfork_child(tsdn, &gctx_locks[i]); - } - malloc_mutex_postfork_child(tsdn, &log_mtx); - for (i = 0; i < PROF_NTDATA_LOCKS; i++) { - malloc_mutex_postfork_child(tsdn, &tdata_locks[i]); - } - malloc_mutex_postfork_child(tsdn, &tdatas_mtx); - malloc_mutex_postfork_child(tsdn, &bt2gctx_mtx); - malloc_mutex_postfork_child(tsdn, &prof_dump_mtx); - } -} - -/******************************************************************************/ diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/prof_data.c b/fluent-bit/lib/jemalloc-5.3.0/src/prof_data.c deleted file mode 100644 index bfa55be1..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/prof_data.c +++ /dev/null @@ -1,1447 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/ckh.h" -#include "jemalloc/internal/hash.h" -#include "jemalloc/internal/malloc_io.h" -#include "jemalloc/internal/prof_data.h" - -/* - * This file defines and manages the core profiling data structures. - * - * Conceptually, profiling data can be imagined as a table with three columns: - * thread, stack trace, and current allocation size. (When prof_accum is on, - * there's one additional column which is the cumulative allocation size.) - * - * Implementation wise, each thread maintains a hash recording the stack trace - * to allocation size correspondences, which are basically the individual rows - * in the table. In addition, two global "indices" are built to make data - * aggregation efficient (for dumping): bt2gctx and tdatas, which are basically - * the "grouped by stack trace" and "grouped by thread" views of the same table, - * respectively. Note that the allocation size is only aggregated to the two - * indices at dumping time, so as to optimize for performance. - */ - -/******************************************************************************/ - -malloc_mutex_t bt2gctx_mtx; -malloc_mutex_t tdatas_mtx; -malloc_mutex_t prof_dump_mtx; - -/* - * Table of mutexes that are shared among gctx's. These are leaf locks, so - * there is no problem with using them for more than one gctx at the same time. - * The primary motivation for this sharing though is that gctx's are ephemeral, - * and destroying mutexes causes complications for systems that allocate when - * creating/destroying mutexes. - */ -malloc_mutex_t *gctx_locks; -static atomic_u_t cum_gctxs; /* Atomic counter. */ - -/* - * Table of mutexes that are shared among tdata's. No operations require - * holding multiple tdata locks, so there is no problem with using them for more - * than one tdata at the same time, even though a gctx lock may be acquired - * while holding a tdata lock. - */ -malloc_mutex_t *tdata_locks; - -/* - * Global hash of (prof_bt_t *)-->(prof_gctx_t *). This is the master data - * structure that knows about all backtraces currently captured. - */ -static ckh_t bt2gctx; - -/* - * Tree of all extant prof_tdata_t structures, regardless of state, - * {attached,detached,expired}. - */ -static prof_tdata_tree_t tdatas; - -size_t prof_unbiased_sz[PROF_SC_NSIZES]; -size_t prof_shifted_unbiased_cnt[PROF_SC_NSIZES]; - -/******************************************************************************/ -/* Red-black trees. */ - -static int -prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) { - uint64_t a_thr_uid = a->thr_uid; - uint64_t b_thr_uid = b->thr_uid; - int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid); - if (ret == 0) { - uint64_t a_thr_discrim = a->thr_discrim; - uint64_t b_thr_discrim = b->thr_discrim; - ret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim < - b_thr_discrim); - if (ret == 0) { - uint64_t a_tctx_uid = a->tctx_uid; - uint64_t b_tctx_uid = b->tctx_uid; - ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid < - b_tctx_uid); - } - } - return ret; -} - -rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t, - tctx_link, prof_tctx_comp) - -static int -prof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) { - unsigned a_len = a->bt.len; - unsigned b_len = b->bt.len; - unsigned comp_len = (a_len < b_len) ? a_len : b_len; - int ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *)); - if (ret == 0) { - ret = (a_len > b_len) - (a_len < b_len); - } - return ret; -} - -rb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link, - prof_gctx_comp) - -static int -prof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) { - int ret; - uint64_t a_uid = a->thr_uid; - uint64_t b_uid = b->thr_uid; - - ret = ((a_uid > b_uid) - (a_uid < b_uid)); - if (ret == 0) { - uint64_t a_discrim = a->thr_discrim; - uint64_t b_discrim = b->thr_discrim; - - ret = ((a_discrim > b_discrim) - (a_discrim < b_discrim)); - } - return ret; -} - -rb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link, - prof_tdata_comp) - -/******************************************************************************/ - -static malloc_mutex_t * -prof_gctx_mutex_choose(void) { - unsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED); - - return &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS]; -} - -static malloc_mutex_t * -prof_tdata_mutex_choose(uint64_t thr_uid) { - return &tdata_locks[thr_uid % PROF_NTDATA_LOCKS]; -} - -bool -prof_data_init(tsd_t *tsd) { - tdata_tree_new(&tdatas); - return ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, - prof_bt_hash, prof_bt_keycomp); -} - -static void -prof_enter(tsd_t *tsd, prof_tdata_t *tdata) { - cassert(config_prof); - assert(tdata == prof_tdata_get(tsd, false)); - - if (tdata != NULL) { - assert(!tdata->enq); - tdata->enq = true; - } - - malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx); -} - -static void -prof_leave(tsd_t *tsd, prof_tdata_t *tdata) { - cassert(config_prof); - assert(tdata == prof_tdata_get(tsd, false)); - - malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx); - - if (tdata != NULL) { - bool idump, gdump; - - assert(tdata->enq); - tdata->enq = false; - idump = tdata->enq_idump; - tdata->enq_idump = false; - gdump = tdata->enq_gdump; - tdata->enq_gdump = false; - - if (idump) { - prof_idump(tsd_tsdn(tsd)); - } - if (gdump) { - prof_gdump(tsd_tsdn(tsd)); - } - } -} - -static prof_gctx_t * -prof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) { - /* - * Create a single allocation that has space for vec of length bt->len. - */ - size_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *)); - prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size, - sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true), - true); - if (gctx == NULL) { - return NULL; - } - gctx->lock = prof_gctx_mutex_choose(); - /* - * Set nlimbo to 1, in order to avoid a race condition with - * prof_tctx_destroy()/prof_gctx_try_destroy(). - */ - gctx->nlimbo = 1; - tctx_tree_new(&gctx->tctxs); - /* Duplicate bt. */ - memcpy(gctx->vec, bt->vec, bt->len * sizeof(void *)); - gctx->bt.vec = gctx->vec; - gctx->bt.len = bt->len; - return gctx; -} - -static void -prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, - prof_gctx_t *gctx) { - cassert(config_prof); - - /* - * Check that gctx is still unused by any thread cache before destroying - * it. prof_lookup() increments gctx->nlimbo in order to avoid a race - * condition with this function, as does prof_tctx_destroy() in order to - * avoid a race between the main body of prof_tctx_destroy() and entry - * into this function. - */ - prof_enter(tsd, tdata_self); - malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock); - assert(gctx->nlimbo != 0); - if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) { - /* Remove gctx from bt2gctx. */ - if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) { - not_reached(); - } - prof_leave(tsd, tdata_self); - /* Destroy gctx. */ - malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); - idalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true); - } else { - /* - * Compensate for increment in prof_tctx_destroy() or - * prof_lookup(). - */ - gctx->nlimbo--; - malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); - prof_leave(tsd, tdata_self); - } -} - -static bool -prof_gctx_should_destroy(prof_gctx_t *gctx) { - if (opt_prof_accum) { - return false; - } - if (!tctx_tree_empty(&gctx->tctxs)) { - return false; - } - if (gctx->nlimbo != 0) { - return false; - } - return true; -} - -static bool -prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata, - void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) { - union { - prof_gctx_t *p; - void *v; - } gctx, tgctx; - union { - prof_bt_t *p; - void *v; - } btkey; - bool new_gctx; - - prof_enter(tsd, tdata); - if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) { - /* bt has never been seen before. Insert it. */ - prof_leave(tsd, tdata); - tgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt); - if (tgctx.v == NULL) { - return true; - } - prof_enter(tsd, tdata); - if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) { - gctx.p = tgctx.p; - btkey.p = &gctx.p->bt; - if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) { - /* OOM. */ - prof_leave(tsd, tdata); - idalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL, - true, true); - return true; - } - new_gctx = true; - } else { - new_gctx = false; - } - } else { - tgctx.v = NULL; - new_gctx = false; - } - - if (!new_gctx) { - /* - * Increment nlimbo, in order to avoid a race condition with - * prof_tctx_destroy()/prof_gctx_try_destroy(). - */ - malloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock); - gctx.p->nlimbo++; - malloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock); - new_gctx = false; - - if (tgctx.v != NULL) { - /* Lost race to insert. */ - idalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true, - true); - } - } - prof_leave(tsd, tdata); - - *p_btkey = btkey.v; - *p_gctx = gctx.p; - *p_new_gctx = new_gctx; - return false; -} - -prof_tctx_t * -prof_lookup(tsd_t *tsd, prof_bt_t *bt) { - union { - prof_tctx_t *p; - void *v; - } ret; - prof_tdata_t *tdata; - bool not_found; - - cassert(config_prof); - - tdata = prof_tdata_get(tsd, false); - assert(tdata != NULL); - - malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock); - not_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v); - if (!not_found) { /* Note double negative! */ - ret.p->prepared = true; - } - malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock); - if (not_found) { - void *btkey; - prof_gctx_t *gctx; - bool new_gctx, error; - - /* - * This thread's cache lacks bt. Look for it in the global - * cache. - */ - if (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx, - &new_gctx)) { - return NULL; - } - - /* Link a prof_tctx_t into gctx for this thread. */ - ret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t), - sz_size2index(sizeof(prof_tctx_t)), false, NULL, true, - arena_ichoose(tsd, NULL), true); - if (ret.p == NULL) { - if (new_gctx) { - prof_gctx_try_destroy(tsd, tdata, gctx); - } - return NULL; - } - ret.p->tdata = tdata; - ret.p->thr_uid = tdata->thr_uid; - ret.p->thr_discrim = tdata->thr_discrim; - ret.p->recent_count = 0; - memset(&ret.p->cnts, 0, sizeof(prof_cnt_t)); - ret.p->gctx = gctx; - ret.p->tctx_uid = tdata->tctx_uid_next++; - ret.p->prepared = true; - ret.p->state = prof_tctx_state_initializing; - malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock); - error = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v); - malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock); - if (error) { - if (new_gctx) { - prof_gctx_try_destroy(tsd, tdata, gctx); - } - idalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true); - return NULL; - } - malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock); - ret.p->state = prof_tctx_state_nominal; - tctx_tree_insert(&gctx->tctxs, ret.p); - gctx->nlimbo--; - malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); - } - - return ret.p; -} - -/* Used in unit tests. */ -static prof_tdata_t * -prof_tdata_count_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata, - void *arg) { - size_t *tdata_count = (size_t *)arg; - - (*tdata_count)++; - - return NULL; -} - -/* Used in unit tests. */ -size_t -prof_tdata_count(void) { - size_t tdata_count = 0; - tsdn_t *tsdn; - - tsdn = tsdn_fetch(); - malloc_mutex_lock(tsdn, &tdatas_mtx); - tdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter, - (void *)&tdata_count); - malloc_mutex_unlock(tsdn, &tdatas_mtx); - - return tdata_count; -} - -/* Used in unit tests. */ -size_t -prof_bt_count(void) { - size_t bt_count; - tsd_t *tsd; - prof_tdata_t *tdata; - - tsd = tsd_fetch(); - tdata = prof_tdata_get(tsd, false); - if (tdata == NULL) { - return 0; - } - - malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx); - bt_count = ckh_count(&bt2gctx); - malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx); - - return bt_count; -} - -char * -prof_thread_name_alloc(tsd_t *tsd, const char *thread_name) { - char *ret; - size_t size; - - if (thread_name == NULL) { - return NULL; - } - - size = strlen(thread_name) + 1; - if (size == 1) { - return ""; - } - - ret = iallocztm(tsd_tsdn(tsd), size, sz_size2index(size), false, NULL, - true, arena_get(TSDN_NULL, 0, true), true); - if (ret == NULL) { - return NULL; - } - memcpy(ret, thread_name, size); - return ret; -} - -int -prof_thread_name_set_impl(tsd_t *tsd, const char *thread_name) { - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t *tdata; - unsigned i; - char *s; - - tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return EAGAIN; - } - - /* Validate input. */ - if (thread_name == NULL) { - return EFAULT; - } - for (i = 0; thread_name[i] != '\0'; i++) { - char c = thread_name[i]; - if (!isgraph(c) && !isblank(c)) { - return EFAULT; - } - } - - s = prof_thread_name_alloc(tsd, thread_name); - if (s == NULL) { - return EAGAIN; - } - - if (tdata->thread_name != NULL) { - idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true, - true); - tdata->thread_name = NULL; - } - if (strlen(s) > 0) { - tdata->thread_name = s; - } - return 0; -} - -JEMALLOC_FORMAT_PRINTF(3, 4) -static void -prof_dump_printf(write_cb_t *prof_dump_write, void *cbopaque, - const char *format, ...) { - va_list ap; - char buf[PROF_PRINTF_BUFSIZE]; - - va_start(ap, format); - malloc_vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - prof_dump_write(cbopaque, buf); -} - -/* - * Casting a double to a uint64_t may not necessarily be in range; this can be - * UB. I don't think this is practically possible with the cur counters, but - * plausibly could be with the accum counters. - */ -#ifdef JEMALLOC_PROF -static uint64_t -prof_double_uint64_cast(double d) { - /* - * Note: UINT64_MAX + 1 is exactly representable as a double on all - * reasonable platforms (certainly those we'll support). Writing this - * as !(a < b) instead of (a >= b) means that we're NaN-safe. - */ - double rounded = round(d); - if (!(rounded < (double)UINT64_MAX)) { - return UINT64_MAX; - } - return (uint64_t)rounded; -} -#endif - -void prof_unbias_map_init() { - /* See the comment in prof_sample_new_event_wait */ -#ifdef JEMALLOC_PROF - for (szind_t i = 0; i < SC_NSIZES; i++) { - double sz = (double)sz_index2size(i); - double rate = (double)(ZU(1) << lg_prof_sample); - double div_val = 1.0 - exp(-sz / rate); - double unbiased_sz = sz / div_val; - /* - * The "true" right value for the unbiased count is - * 1.0/(1 - exp(-sz/rate)). The problem is, we keep the counts - * as integers (for a variety of reasons -- rounding errors - * could trigger asserts, and not all libcs can properly handle - * floating point arithmetic during malloc calls inside libc). - * Rounding to an integer, though, can lead to rounding errors - * of over 30% for sizes close to the sampling rate. So - * instead, we multiply by a constant, dividing the maximum - * possible roundoff error by that constant. To avoid overflow - * in summing up size_t values, the largest safe constant we can - * pick is the size of the smallest allocation. - */ - double cnt_shift = (double)(ZU(1) << SC_LG_TINY_MIN); - double shifted_unbiased_cnt = cnt_shift / div_val; - prof_unbiased_sz[i] = (size_t)round(unbiased_sz); - prof_shifted_unbiased_cnt[i] = (size_t)round( - shifted_unbiased_cnt); - } -#else - unreachable(); -#endif -} - -/* - * The unbiasing story is long. The jeprof unbiasing logic was copied from - * pprof. Both shared an issue: they unbiased using the average size of the - * allocations at a particular stack trace. This can work out OK if allocations - * are mostly of the same size given some stack, but not otherwise. We now - * internally track what the unbiased results ought to be. We can't just report - * them as they are though; they'll still go through the jeprof unbiasing - * process. Instead, we figure out what values we can feed *into* jeprof's - * unbiasing mechanism that will lead to getting the right values out. - * - * It'll unbias count and aggregate size as: - * - * c_out = c_in * 1/(1-exp(-s_in/c_in/R) - * s_out = s_in * 1/(1-exp(-s_in/c_in/R) - * - * We want to solve for the values of c_in and s_in that will - * give the c_out and s_out that we've computed internally. - * - * Let's do a change of variables (both to make the math easier and to make it - * easier to write): - * x = s_in / c_in - * y = s_in - * k = 1/R. - * - * Then - * c_out = y/x * 1/(1-exp(-k*x)) - * s_out = y * 1/(1-exp(-k*x)) - * - * The first equation gives: - * y = x * c_out * (1-exp(-k*x)) - * The second gives: - * y = s_out * (1-exp(-k*x)) - * So we have - * x = s_out / c_out. - * And all the other values fall out from that. - * - * This is all a fair bit of work. The thing we get out of it is that we don't - * break backwards compatibility with jeprof (and the various tools that have - * copied its unbiasing logic). Eventually, we anticipate a v3 heap profile - * dump format based on JSON, at which point I think much of this logic can get - * cleaned up (since we'll be taking a compatibility break there anyways). - */ -static void -prof_do_unbias(uint64_t c_out_shifted_i, uint64_t s_out_i, uint64_t *r_c_in, - uint64_t *r_s_in) { -#ifdef JEMALLOC_PROF - if (c_out_shifted_i == 0 || s_out_i == 0) { - *r_c_in = 0; - *r_s_in = 0; - return; - } - /* - * See the note in prof_unbias_map_init() to see why we take c_out in a - * shifted form. - */ - double c_out = (double)c_out_shifted_i - / (double)(ZU(1) << SC_LG_TINY_MIN); - double s_out = (double)s_out_i; - double R = (double)(ZU(1) << lg_prof_sample); - - double x = s_out / c_out; - double y = s_out * (1.0 - exp(-x / R)); - - double c_in = y / x; - double s_in = y; - - *r_c_in = prof_double_uint64_cast(c_in); - *r_s_in = prof_double_uint64_cast(s_in); -#else - unreachable(); -#endif -} - -static void -prof_dump_print_cnts(write_cb_t *prof_dump_write, void *cbopaque, - const prof_cnt_t *cnts) { - uint64_t curobjs; - uint64_t curbytes; - uint64_t accumobjs; - uint64_t accumbytes; - if (opt_prof_unbias) { - prof_do_unbias(cnts->curobjs_shifted_unbiased, - cnts->curbytes_unbiased, &curobjs, &curbytes); - prof_do_unbias(cnts->accumobjs_shifted_unbiased, - cnts->accumbytes_unbiased, &accumobjs, &accumbytes); - } else { - curobjs = cnts->curobjs; - curbytes = cnts->curbytes; - accumobjs = cnts->accumobjs; - accumbytes = cnts->accumbytes; - } - prof_dump_printf(prof_dump_write, cbopaque, - "%"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]", - curobjs, curbytes, accumobjs, accumbytes); -} - -static void -prof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) { - malloc_mutex_assert_owner(tsdn, tctx->tdata->lock); - - malloc_mutex_lock(tsdn, tctx->gctx->lock); - - switch (tctx->state) { - case prof_tctx_state_initializing: - malloc_mutex_unlock(tsdn, tctx->gctx->lock); - return; - case prof_tctx_state_nominal: - tctx->state = prof_tctx_state_dumping; - malloc_mutex_unlock(tsdn, tctx->gctx->lock); - - memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t)); - - tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs; - tdata->cnt_summed.curobjs_shifted_unbiased - += tctx->dump_cnts.curobjs_shifted_unbiased; - tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes; - tdata->cnt_summed.curbytes_unbiased - += tctx->dump_cnts.curbytes_unbiased; - if (opt_prof_accum) { - tdata->cnt_summed.accumobjs += - tctx->dump_cnts.accumobjs; - tdata->cnt_summed.accumobjs_shifted_unbiased += - tctx->dump_cnts.accumobjs_shifted_unbiased; - tdata->cnt_summed.accumbytes += - tctx->dump_cnts.accumbytes; - tdata->cnt_summed.accumbytes_unbiased += - tctx->dump_cnts.accumbytes_unbiased; - } - break; - case prof_tctx_state_dumping: - case prof_tctx_state_purgatory: - not_reached(); - } -} - -static void -prof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) { - malloc_mutex_assert_owner(tsdn, gctx->lock); - - gctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs; - gctx->cnt_summed.curobjs_shifted_unbiased - += tctx->dump_cnts.curobjs_shifted_unbiased; - gctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes; - gctx->cnt_summed.curbytes_unbiased += tctx->dump_cnts.curbytes_unbiased; - if (opt_prof_accum) { - gctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs; - gctx->cnt_summed.accumobjs_shifted_unbiased - += tctx->dump_cnts.accumobjs_shifted_unbiased; - gctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes; - gctx->cnt_summed.accumbytes_unbiased - += tctx->dump_cnts.accumbytes_unbiased; - } -} - -static prof_tctx_t * -prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) { - tsdn_t *tsdn = (tsdn_t *)arg; - - malloc_mutex_assert_owner(tsdn, tctx->gctx->lock); - - switch (tctx->state) { - case prof_tctx_state_nominal: - /* New since dumping started; ignore. */ - break; - case prof_tctx_state_dumping: - case prof_tctx_state_purgatory: - prof_tctx_merge_gctx(tsdn, tctx, tctx->gctx); - break; - default: - not_reached(); - } - - return NULL; -} - -typedef struct prof_dump_iter_arg_s prof_dump_iter_arg_t; -struct prof_dump_iter_arg_s { - tsdn_t *tsdn; - write_cb_t *prof_dump_write; - void *cbopaque; -}; - -static prof_tctx_t * -prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) { - prof_dump_iter_arg_t *arg = (prof_dump_iter_arg_t *)opaque; - malloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock); - - switch (tctx->state) { - case prof_tctx_state_initializing: - case prof_tctx_state_nominal: - /* Not captured by this dump. */ - break; - case prof_tctx_state_dumping: - case prof_tctx_state_purgatory: - prof_dump_printf(arg->prof_dump_write, arg->cbopaque, - " t%"FMTu64": ", tctx->thr_uid); - prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque, - &tctx->dump_cnts); - arg->prof_dump_write(arg->cbopaque, "\n"); - break; - default: - not_reached(); - } - return NULL; -} - -static prof_tctx_t * -prof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) { - tsdn_t *tsdn = (tsdn_t *)arg; - prof_tctx_t *ret; - - malloc_mutex_assert_owner(tsdn, tctx->gctx->lock); - - switch (tctx->state) { - case prof_tctx_state_nominal: - /* New since dumping started; ignore. */ - break; - case prof_tctx_state_dumping: - tctx->state = prof_tctx_state_nominal; - break; - case prof_tctx_state_purgatory: - ret = tctx; - goto label_return; - default: - not_reached(); - } - - ret = NULL; -label_return: - return ret; -} - -static void -prof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) { - cassert(config_prof); - - malloc_mutex_lock(tsdn, gctx->lock); - - /* - * Increment nlimbo so that gctx won't go away before dump. - * Additionally, link gctx into the dump list so that it is included in - * prof_dump()'s second pass. - */ - gctx->nlimbo++; - gctx_tree_insert(gctxs, gctx); - - memset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t)); - - malloc_mutex_unlock(tsdn, gctx->lock); -} - -typedef struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg_t; -struct prof_gctx_merge_iter_arg_s { - tsdn_t *tsdn; - size_t *leak_ngctx; -}; - -static prof_gctx_t * -prof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) { - prof_gctx_merge_iter_arg_t *arg = (prof_gctx_merge_iter_arg_t *)opaque; - - malloc_mutex_lock(arg->tsdn, gctx->lock); - tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter, - (void *)arg->tsdn); - if (gctx->cnt_summed.curobjs != 0) { - (*arg->leak_ngctx)++; - } - malloc_mutex_unlock(arg->tsdn, gctx->lock); - - return NULL; -} - -static void -prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) { - prof_tdata_t *tdata = prof_tdata_get(tsd, false); - prof_gctx_t *gctx; - - /* - * Standard tree iteration won't work here, because as soon as we - * decrement gctx->nlimbo and unlock gctx, another thread can - * concurrently destroy it, which will corrupt the tree. Therefore, - * tear down the tree one node at a time during iteration. - */ - while ((gctx = gctx_tree_first(gctxs)) != NULL) { - gctx_tree_remove(gctxs, gctx); - malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock); - { - prof_tctx_t *next; - - next = NULL; - do { - prof_tctx_t *to_destroy = - tctx_tree_iter(&gctx->tctxs, next, - prof_tctx_finish_iter, - (void *)tsd_tsdn(tsd)); - if (to_destroy != NULL) { - next = tctx_tree_next(&gctx->tctxs, - to_destroy); - tctx_tree_remove(&gctx->tctxs, - to_destroy); - idalloctm(tsd_tsdn(tsd), to_destroy, - NULL, NULL, true, true); - } else { - next = NULL; - } - } while (next != NULL); - } - gctx->nlimbo--; - if (prof_gctx_should_destroy(gctx)) { - gctx->nlimbo++; - malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); - prof_gctx_try_destroy(tsd, tdata, gctx); - } else { - malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); - } - } -} - -typedef struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg_t; -struct prof_tdata_merge_iter_arg_s { - tsdn_t *tsdn; - prof_cnt_t *cnt_all; -}; - -static prof_tdata_t * -prof_tdata_merge_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata, - void *opaque) { - prof_tdata_merge_iter_arg_t *arg = - (prof_tdata_merge_iter_arg_t *)opaque; - - malloc_mutex_lock(arg->tsdn, tdata->lock); - if (!tdata->expired) { - size_t tabind; - union { - prof_tctx_t *p; - void *v; - } tctx; - - tdata->dumping = true; - memset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t)); - for (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL, - &tctx.v);) { - prof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata); - } - - arg->cnt_all->curobjs += tdata->cnt_summed.curobjs; - arg->cnt_all->curobjs_shifted_unbiased - += tdata->cnt_summed.curobjs_shifted_unbiased; - arg->cnt_all->curbytes += tdata->cnt_summed.curbytes; - arg->cnt_all->curbytes_unbiased - += tdata->cnt_summed.curbytes_unbiased; - if (opt_prof_accum) { - arg->cnt_all->accumobjs += tdata->cnt_summed.accumobjs; - arg->cnt_all->accumobjs_shifted_unbiased - += tdata->cnt_summed.accumobjs_shifted_unbiased; - arg->cnt_all->accumbytes += - tdata->cnt_summed.accumbytes; - arg->cnt_all->accumbytes_unbiased += - tdata->cnt_summed.accumbytes_unbiased; - } - } else { - tdata->dumping = false; - } - malloc_mutex_unlock(arg->tsdn, tdata->lock); - - return NULL; -} - -static prof_tdata_t * -prof_tdata_dump_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata, - void *opaque) { - if (!tdata->dumping) { - return NULL; - } - - prof_dump_iter_arg_t *arg = (prof_dump_iter_arg_t *)opaque; - prof_dump_printf(arg->prof_dump_write, arg->cbopaque, " t%"FMTu64": ", - tdata->thr_uid); - prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque, - &tdata->cnt_summed); - if (tdata->thread_name != NULL) { - arg->prof_dump_write(arg->cbopaque, " "); - arg->prof_dump_write(arg->cbopaque, tdata->thread_name); - } - arg->prof_dump_write(arg->cbopaque, "\n"); - return NULL; -} - -static void -prof_dump_header(prof_dump_iter_arg_t *arg, const prof_cnt_t *cnt_all) { - prof_dump_printf(arg->prof_dump_write, arg->cbopaque, - "heap_v2/%"FMTu64"\n t*: ", ((uint64_t)1U << lg_prof_sample)); - prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque, cnt_all); - arg->prof_dump_write(arg->cbopaque, "\n"); - - malloc_mutex_lock(arg->tsdn, &tdatas_mtx); - tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter, arg); - malloc_mutex_unlock(arg->tsdn, &tdatas_mtx); -} - -static void -prof_dump_gctx(prof_dump_iter_arg_t *arg, prof_gctx_t *gctx, - const prof_bt_t *bt, prof_gctx_tree_t *gctxs) { - cassert(config_prof); - malloc_mutex_assert_owner(arg->tsdn, gctx->lock); - - /* Avoid dumping such gctx's that have no useful data. */ - if ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) || - (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) { - assert(gctx->cnt_summed.curobjs == 0); - assert(gctx->cnt_summed.curbytes == 0); - /* - * These asserts would not be correct -- see the comment on races - * in prof.c - * assert(gctx->cnt_summed.curobjs_unbiased == 0); - * assert(gctx->cnt_summed.curbytes_unbiased == 0); - */ - assert(gctx->cnt_summed.accumobjs == 0); - assert(gctx->cnt_summed.accumobjs_shifted_unbiased == 0); - assert(gctx->cnt_summed.accumbytes == 0); - assert(gctx->cnt_summed.accumbytes_unbiased == 0); - return; - } - - arg->prof_dump_write(arg->cbopaque, "@"); - for (unsigned i = 0; i < bt->len; i++) { - prof_dump_printf(arg->prof_dump_write, arg->cbopaque, - " %#"FMTxPTR, (uintptr_t)bt->vec[i]); - } - - arg->prof_dump_write(arg->cbopaque, "\n t*: "); - prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque, - &gctx->cnt_summed); - arg->prof_dump_write(arg->cbopaque, "\n"); - - tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter, arg); -} - -/* - * See prof_sample_new_event_wait() comment for why the body of this function - * is conditionally compiled. - */ -static void -prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx) { -#ifdef JEMALLOC_PROF - /* - * Scaling is equivalent AdjustSamples() in jeprof, but the result may - * differ slightly from what jeprof reports, because here we scale the - * summary values, whereas jeprof scales each context individually and - * reports the sums of the scaled values. - */ - if (cnt_all->curbytes != 0) { - double sample_period = (double)((uint64_t)1 << lg_prof_sample); - double ratio = (((double)cnt_all->curbytes) / - (double)cnt_all->curobjs) / sample_period; - double scale_factor = 1.0 / (1.0 - exp(-ratio)); - uint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes) - * scale_factor); - uint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) * - scale_factor); - - malloc_printf("<jemalloc>: Leak approximation summary: ~%"FMTu64 - " byte%s, ~%"FMTu64" object%s, >= %zu context%s\n", - curbytes, (curbytes != 1) ? "s" : "", curobjs, (curobjs != - 1) ? "s" : "", leak_ngctx, (leak_ngctx != 1) ? "s" : ""); - malloc_printf( - "<jemalloc>: Run jeprof on dump output for leak detail\n"); - if (opt_prof_leak_error) { - malloc_printf( - "<jemalloc>: Exiting with error code because memory" - " leaks were detected\n"); - /* - * Use _exit() with underscore to avoid calling atexit() - * and entering endless cycle. - */ - _exit(1); - } - } -#endif -} - -static prof_gctx_t * -prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) { - prof_dump_iter_arg_t *arg = (prof_dump_iter_arg_t *)opaque; - malloc_mutex_lock(arg->tsdn, gctx->lock); - prof_dump_gctx(arg, gctx, &gctx->bt, gctxs); - malloc_mutex_unlock(arg->tsdn, gctx->lock); - return NULL; -} - -static void -prof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata, prof_cnt_t *cnt_all, - size_t *leak_ngctx, prof_gctx_tree_t *gctxs) { - size_t tabind; - union { - prof_gctx_t *p; - void *v; - } gctx; - - prof_enter(tsd, tdata); - - /* - * Put gctx's in limbo and clear their counters in preparation for - * summing. - */ - gctx_tree_new(gctxs); - for (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) { - prof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs); - } - - /* - * Iterate over tdatas, and for the non-expired ones snapshot their tctx - * stats and merge them into the associated gctx's. - */ - memset(cnt_all, 0, sizeof(prof_cnt_t)); - prof_tdata_merge_iter_arg_t prof_tdata_merge_iter_arg = {tsd_tsdn(tsd), - cnt_all}; - malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx); - tdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter, - &prof_tdata_merge_iter_arg); - malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx); - - /* Merge tctx stats into gctx's. */ - *leak_ngctx = 0; - prof_gctx_merge_iter_arg_t prof_gctx_merge_iter_arg = {tsd_tsdn(tsd), - leak_ngctx}; - gctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter, - &prof_gctx_merge_iter_arg); - - prof_leave(tsd, tdata); -} - -void -prof_dump_impl(tsd_t *tsd, write_cb_t *prof_dump_write, void *cbopaque, - prof_tdata_t *tdata, bool leakcheck) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_dump_mtx); - prof_cnt_t cnt_all; - size_t leak_ngctx; - prof_gctx_tree_t gctxs; - prof_dump_prep(tsd, tdata, &cnt_all, &leak_ngctx, &gctxs); - prof_dump_iter_arg_t prof_dump_iter_arg = {tsd_tsdn(tsd), - prof_dump_write, cbopaque}; - prof_dump_header(&prof_dump_iter_arg, &cnt_all); - gctx_tree_iter(&gctxs, NULL, prof_gctx_dump_iter, &prof_dump_iter_arg); - prof_gctx_finish(tsd, &gctxs); - if (leakcheck) { - prof_leakcheck(&cnt_all, leak_ngctx); - } -} - -/* Used in unit tests. */ -void -prof_cnt_all(prof_cnt_t *cnt_all) { - tsd_t *tsd = tsd_fetch(); - prof_tdata_t *tdata = prof_tdata_get(tsd, false); - if (tdata == NULL) { - memset(cnt_all, 0, sizeof(prof_cnt_t)); - } else { - size_t leak_ngctx; - prof_gctx_tree_t gctxs; - prof_dump_prep(tsd, tdata, cnt_all, &leak_ngctx, &gctxs); - prof_gctx_finish(tsd, &gctxs); - } -} - -void -prof_bt_hash(const void *key, size_t r_hash[2]) { - prof_bt_t *bt = (prof_bt_t *)key; - - cassert(config_prof); - - hash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash); -} - -bool -prof_bt_keycomp(const void *k1, const void *k2) { - const prof_bt_t *bt1 = (prof_bt_t *)k1; - const prof_bt_t *bt2 = (prof_bt_t *)k2; - - cassert(config_prof); - - if (bt1->len != bt2->len) { - return false; - } - return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0); -} - -prof_tdata_t * -prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim, - char *thread_name, bool active) { - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t *tdata; - - cassert(config_prof); - - /* Initialize an empty cache for this thread. */ - tdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t), - sz_size2index(sizeof(prof_tdata_t)), false, NULL, true, - arena_get(TSDN_NULL, 0, true), true); - if (tdata == NULL) { - return NULL; - } - - tdata->lock = prof_tdata_mutex_choose(thr_uid); - tdata->thr_uid = thr_uid; - tdata->thr_discrim = thr_discrim; - tdata->thread_name = thread_name; - tdata->attached = true; - tdata->expired = false; - tdata->tctx_uid_next = 0; - - if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash, - prof_bt_keycomp)) { - idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true); - return NULL; - } - - tdata->enq = false; - tdata->enq_idump = false; - tdata->enq_gdump = false; - - tdata->dumping = false; - tdata->active = active; - - malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx); - tdata_tree_insert(&tdatas, tdata); - malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx); - - return tdata; -} - -static bool -prof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) { - if (tdata->attached && !even_if_attached) { - return false; - } - if (ckh_count(&tdata->bt2tctx) != 0) { - return false; - } - return true; -} - -static bool -prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata, - bool even_if_attached) { - malloc_mutex_assert_owner(tsdn, tdata->lock); - - return prof_tdata_should_destroy_unlocked(tdata, even_if_attached); -} - -static void -prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata, - bool even_if_attached) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx); - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tdata->lock); - - tdata_tree_remove(&tdatas, tdata); - - assert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached)); - - if (tdata->thread_name != NULL) { - idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true, - true); - } - ckh_delete(tsd, &tdata->bt2tctx); - idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true); -} - -static void -prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) { - malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx); - prof_tdata_destroy_locked(tsd, tdata, even_if_attached); - malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx); -} - -void -prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) { - bool destroy_tdata; - - malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock); - if (tdata->attached) { - destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata, - true); - /* - * Only detach if !destroy_tdata, because detaching would allow - * another thread to win the race to destroy tdata. - */ - if (!destroy_tdata) { - tdata->attached = false; - } - tsd_prof_tdata_set(tsd, NULL); - } else { - destroy_tdata = false; - } - malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock); - if (destroy_tdata) { - prof_tdata_destroy(tsd, tdata, true); - } -} - -static bool -prof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) { - bool destroy_tdata; - - malloc_mutex_lock(tsdn, tdata->lock); - if (!tdata->expired) { - tdata->expired = true; - destroy_tdata = prof_tdata_should_destroy(tsdn, tdata, false); - } else { - destroy_tdata = false; - } - malloc_mutex_unlock(tsdn, tdata->lock); - - return destroy_tdata; -} - -static prof_tdata_t * -prof_tdata_reset_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata, - void *arg) { - tsdn_t *tsdn = (tsdn_t *)arg; - - return (prof_tdata_expire(tsdn, tdata) ? tdata : NULL); -} - -void -prof_reset(tsd_t *tsd, size_t lg_sample) { - prof_tdata_t *next; - - assert(lg_sample < (sizeof(uint64_t) << 3)); - - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx); - malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx); - - lg_prof_sample = lg_sample; - prof_unbias_map_init(); - - next = NULL; - do { - prof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next, - prof_tdata_reset_iter, (void *)tsd); - if (to_destroy != NULL) { - next = tdata_tree_next(&tdatas, to_destroy); - prof_tdata_destroy_locked(tsd, to_destroy, false); - } else { - next = NULL; - } - } while (next != NULL); - - malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx); -} - -static bool -prof_tctx_should_destroy(tsd_t *tsd, prof_tctx_t *tctx) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); - - if (opt_prof_accum) { - return false; - } - if (tctx->cnts.curobjs != 0) { - return false; - } - if (tctx->prepared) { - return false; - } - if (tctx->recent_count != 0) { - return false; - } - return true; -} - -static void -prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); - - assert(tctx->cnts.curobjs == 0); - assert(tctx->cnts.curbytes == 0); - /* - * These asserts are not correct -- see the comment about races in - * prof.c - * - * assert(tctx->cnts.curobjs_shifted_unbiased == 0); - * assert(tctx->cnts.curbytes_unbiased == 0); - */ - assert(!opt_prof_accum); - assert(tctx->cnts.accumobjs == 0); - assert(tctx->cnts.accumbytes == 0); - /* - * These ones are, since accumbyte counts never go down. Either - * prof_accum is off (in which case these should never have changed from - * their initial value of zero), or it's on (in which case we shouldn't - * be destroying this tctx). - */ - assert(tctx->cnts.accumobjs_shifted_unbiased == 0); - assert(tctx->cnts.accumbytes_unbiased == 0); - - prof_gctx_t *gctx = tctx->gctx; - - { - prof_tdata_t *tdata = tctx->tdata; - tctx->tdata = NULL; - ckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL); - bool destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), - tdata, false); - malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock); - if (destroy_tdata) { - prof_tdata_destroy(tsd, tdata, false); - } - } - - bool destroy_tctx, destroy_gctx; - - malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock); - switch (tctx->state) { - case prof_tctx_state_nominal: - tctx_tree_remove(&gctx->tctxs, tctx); - destroy_tctx = true; - if (prof_gctx_should_destroy(gctx)) { - /* - * Increment gctx->nlimbo in order to keep another - * thread from winning the race to destroy gctx while - * this one has gctx->lock dropped. Without this, it - * would be possible for another thread to: - * - * 1) Sample an allocation associated with gctx. - * 2) Deallocate the sampled object. - * 3) Successfully prof_gctx_try_destroy(gctx). - * - * The result would be that gctx no longer exists by the - * time this thread accesses it in - * prof_gctx_try_destroy(). - */ - gctx->nlimbo++; - destroy_gctx = true; - } else { - destroy_gctx = false; - } - break; - case prof_tctx_state_dumping: - /* - * A dumping thread needs tctx to remain valid until dumping - * has finished. Change state such that the dumping thread will - * complete destruction during a late dump iteration phase. - */ - tctx->state = prof_tctx_state_purgatory; - destroy_tctx = false; - destroy_gctx = false; - break; - default: - not_reached(); - destroy_tctx = false; - destroy_gctx = false; - } - malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); - if (destroy_gctx) { - prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx); - } - if (destroy_tctx) { - idalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true); - } -} - -void -prof_tctx_try_destroy(tsd_t *tsd, prof_tctx_t *tctx) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); - if (prof_tctx_should_destroy(tsd, tctx)) { - /* tctx->tdata->lock will be released in prof_tctx_destroy(). */ - prof_tctx_destroy(tsd, tctx); - } else { - malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock); - } -} - -/******************************************************************************/ diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/prof_log.c b/fluent-bit/lib/jemalloc-5.3.0/src/prof_log.c deleted file mode 100644 index 0632c3b3..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/prof_log.c +++ /dev/null @@ -1,717 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/buf_writer.h" -#include "jemalloc/internal/ckh.h" -#include "jemalloc/internal/emitter.h" -#include "jemalloc/internal/hash.h" -#include "jemalloc/internal/malloc_io.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/prof_data.h" -#include "jemalloc/internal/prof_log.h" -#include "jemalloc/internal/prof_sys.h" - -bool opt_prof_log = false; -typedef enum prof_logging_state_e prof_logging_state_t; -enum prof_logging_state_e { - prof_logging_state_stopped, - prof_logging_state_started, - prof_logging_state_dumping -}; - -/* - * - stopped: log_start never called, or previous log_stop has completed. - * - started: log_start called, log_stop not called yet. Allocations are logged. - * - dumping: log_stop called but not finished; samples are not logged anymore. - */ -prof_logging_state_t prof_logging_state = prof_logging_state_stopped; - -/* Used in unit tests. */ -static bool prof_log_dummy = false; - -/* Incremented for every log file that is output. */ -static uint64_t log_seq = 0; -static char log_filename[ - /* Minimize memory bloat for non-prof builds. */ -#ifdef JEMALLOC_PROF - PATH_MAX + -#endif - 1]; - -/* Timestamp for most recent call to log_start(). */ -static nstime_t log_start_timestamp; - -/* Increment these when adding to the log_bt and log_thr linked lists. */ -static size_t log_bt_index = 0; -static size_t log_thr_index = 0; - -/* Linked list node definitions. These are only used in this file. */ -typedef struct prof_bt_node_s prof_bt_node_t; - -struct prof_bt_node_s { - prof_bt_node_t *next; - size_t index; - prof_bt_t bt; - /* Variable size backtrace vector pointed to by bt. */ - void *vec[1]; -}; - -typedef struct prof_thr_node_s prof_thr_node_t; - -struct prof_thr_node_s { - prof_thr_node_t *next; - size_t index; - uint64_t thr_uid; - /* Variable size based on thr_name_sz. */ - char name[1]; -}; - -typedef struct prof_alloc_node_s prof_alloc_node_t; - -/* This is output when logging sampled allocations. */ -struct prof_alloc_node_s { - prof_alloc_node_t *next; - /* Indices into an array of thread data. */ - size_t alloc_thr_ind; - size_t free_thr_ind; - - /* Indices into an array of backtraces. */ - size_t alloc_bt_ind; - size_t free_bt_ind; - - uint64_t alloc_time_ns; - uint64_t free_time_ns; - - size_t usize; -}; - -/* - * Created on the first call to prof_try_log and deleted on prof_log_stop. - * These are the backtraces and threads that have already been logged by an - * allocation. - */ -static bool log_tables_initialized = false; -static ckh_t log_bt_node_set; -static ckh_t log_thr_node_set; - -/* Store linked lists for logged data. */ -static prof_bt_node_t *log_bt_first = NULL; -static prof_bt_node_t *log_bt_last = NULL; -static prof_thr_node_t *log_thr_first = NULL; -static prof_thr_node_t *log_thr_last = NULL; -static prof_alloc_node_t *log_alloc_first = NULL; -static prof_alloc_node_t *log_alloc_last = NULL; - -/* Protects the prof_logging_state and any log_{...} variable. */ -malloc_mutex_t log_mtx; - -/******************************************************************************/ -/* - * Function prototypes for static functions that are referenced prior to - * definition. - */ - -/* Hashtable functions for log_bt_node_set and log_thr_node_set. */ -static void prof_thr_node_hash(const void *key, size_t r_hash[2]); -static bool prof_thr_node_keycomp(const void *k1, const void *k2); -static void prof_bt_node_hash(const void *key, size_t r_hash[2]); -static bool prof_bt_node_keycomp(const void *k1, const void *k2); - -/******************************************************************************/ - -static size_t -prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) { - assert(prof_logging_state == prof_logging_state_started); - malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx); - - prof_bt_node_t dummy_node; - dummy_node.bt = *bt; - prof_bt_node_t *node; - - /* See if this backtrace is already cached in the table. */ - if (ckh_search(&log_bt_node_set, (void *)(&dummy_node), - (void **)(&node), NULL)) { - size_t sz = offsetof(prof_bt_node_t, vec) + - (bt->len * sizeof(void *)); - prof_bt_node_t *new_node = (prof_bt_node_t *) - iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, - true, arena_get(TSDN_NULL, 0, true), true); - if (log_bt_first == NULL) { - log_bt_first = new_node; - log_bt_last = new_node; - } else { - log_bt_last->next = new_node; - log_bt_last = new_node; - } - - new_node->next = NULL; - new_node->index = log_bt_index; - /* - * Copy the backtrace: bt is inside a tdata or gctx, which - * might die before prof_log_stop is called. - */ - new_node->bt.len = bt->len; - memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *)); - new_node->bt.vec = new_node->vec; - - log_bt_index++; - ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL); - return new_node->index; - } else { - return node->index; - } -} - -static size_t -prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) { - assert(prof_logging_state == prof_logging_state_started); - malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx); - - prof_thr_node_t dummy_node; - dummy_node.thr_uid = thr_uid; - prof_thr_node_t *node; - - /* See if this thread is already cached in the table. */ - if (ckh_search(&log_thr_node_set, (void *)(&dummy_node), - (void **)(&node), NULL)) { - size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1; - prof_thr_node_t *new_node = (prof_thr_node_t *) - iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, - true, arena_get(TSDN_NULL, 0, true), true); - if (log_thr_first == NULL) { - log_thr_first = new_node; - log_thr_last = new_node; - } else { - log_thr_last->next = new_node; - log_thr_last = new_node; - } - - new_node->next = NULL; - new_node->index = log_thr_index; - new_node->thr_uid = thr_uid; - strcpy(new_node->name, name); - - log_thr_index++; - ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL); - return new_node->index; - } else { - return node->index; - } -} - -JEMALLOC_COLD -void -prof_try_log(tsd_t *tsd, size_t usize, prof_info_t *prof_info) { - cassert(config_prof); - prof_tctx_t *tctx = prof_info->alloc_tctx; - malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); - - prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false); - if (cons_tdata == NULL) { - /* - * We decide not to log these allocations. cons_tdata will be - * NULL only when the current thread is in a weird state (e.g. - * it's being destroyed). - */ - return; - } - - malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx); - - if (prof_logging_state != prof_logging_state_started) { - goto label_done; - } - - if (!log_tables_initialized) { - bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS, - prof_bt_node_hash, prof_bt_node_keycomp); - bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS, - prof_thr_node_hash, prof_thr_node_keycomp); - if (err1 || err2) { - goto label_done; - } - log_tables_initialized = true; - } - - nstime_t alloc_time = prof_info->alloc_time; - nstime_t free_time; - nstime_prof_init_update(&free_time); - - size_t sz = sizeof(prof_alloc_node_t); - prof_alloc_node_t *new_node = (prof_alloc_node_t *) - iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true, - arena_get(TSDN_NULL, 0, true), true); - - const char *prod_thr_name = (tctx->tdata->thread_name == NULL)? - "" : tctx->tdata->thread_name; - const char *cons_thr_name = prof_thread_name_get(tsd); - - prof_bt_t bt; - /* Initialize the backtrace, using the buffer in tdata to store it. */ - bt_init(&bt, cons_tdata->vec); - prof_backtrace(tsd, &bt); - prof_bt_t *cons_bt = &bt; - - /* We haven't destroyed tctx yet, so gctx should be good to read. */ - prof_bt_t *prod_bt = &tctx->gctx->bt; - - new_node->next = NULL; - new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid, - prod_thr_name); - new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid, - cons_thr_name); - new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt); - new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt); - new_node->alloc_time_ns = nstime_ns(&alloc_time); - new_node->free_time_ns = nstime_ns(&free_time); - new_node->usize = usize; - - if (log_alloc_first == NULL) { - log_alloc_first = new_node; - log_alloc_last = new_node; - } else { - log_alloc_last->next = new_node; - log_alloc_last = new_node; - } - -label_done: - malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx); -} - -static void -prof_bt_node_hash(const void *key, size_t r_hash[2]) { - const prof_bt_node_t *bt_node = (prof_bt_node_t *)key; - prof_bt_hash((void *)(&bt_node->bt), r_hash); -} - -static bool -prof_bt_node_keycomp(const void *k1, const void *k2) { - const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1; - const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2; - return prof_bt_keycomp((void *)(&bt_node1->bt), - (void *)(&bt_node2->bt)); -} - -static void -prof_thr_node_hash(const void *key, size_t r_hash[2]) { - const prof_thr_node_t *thr_node = (prof_thr_node_t *)key; - hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash); -} - -static bool -prof_thr_node_keycomp(const void *k1, const void *k2) { - const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1; - const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2; - return thr_node1->thr_uid == thr_node2->thr_uid; -} - -/* Used in unit tests. */ -size_t -prof_log_bt_count(void) { - cassert(config_prof); - size_t cnt = 0; - prof_bt_node_t *node = log_bt_first; - while (node != NULL) { - cnt++; - node = node->next; - } - return cnt; -} - -/* Used in unit tests. */ -size_t -prof_log_alloc_count(void) { - cassert(config_prof); - size_t cnt = 0; - prof_alloc_node_t *node = log_alloc_first; - while (node != NULL) { - cnt++; - node = node->next; - } - return cnt; -} - -/* Used in unit tests. */ -size_t -prof_log_thr_count(void) { - cassert(config_prof); - size_t cnt = 0; - prof_thr_node_t *node = log_thr_first; - while (node != NULL) { - cnt++; - node = node->next; - } - return cnt; -} - -/* Used in unit tests. */ -bool -prof_log_is_logging(void) { - cassert(config_prof); - return prof_logging_state == prof_logging_state_started; -} - -/* Used in unit tests. */ -bool -prof_log_rep_check(void) { - cassert(config_prof); - if (prof_logging_state == prof_logging_state_stopped - && log_tables_initialized) { - return true; - } - - if (log_bt_last != NULL && log_bt_last->next != NULL) { - return true; - } - if (log_thr_last != NULL && log_thr_last->next != NULL) { - return true; - } - if (log_alloc_last != NULL && log_alloc_last->next != NULL) { - return true; - } - - size_t bt_count = prof_log_bt_count(); - size_t thr_count = prof_log_thr_count(); - size_t alloc_count = prof_log_alloc_count(); - - - if (prof_logging_state == prof_logging_state_stopped) { - if (bt_count != 0 || thr_count != 0 || alloc_count || 0) { - return true; - } - } - - prof_alloc_node_t *node = log_alloc_first; - while (node != NULL) { - if (node->alloc_bt_ind >= bt_count) { - return true; - } - if (node->free_bt_ind >= bt_count) { - return true; - } - if (node->alloc_thr_ind >= thr_count) { - return true; - } - if (node->free_thr_ind >= thr_count) { - return true; - } - if (node->alloc_time_ns > node->free_time_ns) { - return true; - } - node = node->next; - } - - return false; -} - -/* Used in unit tests. */ -void -prof_log_dummy_set(bool new_value) { - cassert(config_prof); - prof_log_dummy = new_value; -} - -/* Used as an atexit function to stop logging on exit. */ -static void -prof_log_stop_final(void) { - tsd_t *tsd = tsd_fetch(); - prof_log_stop(tsd_tsdn(tsd)); -} - -JEMALLOC_COLD -bool -prof_log_start(tsdn_t *tsdn, const char *filename) { - cassert(config_prof); - - if (!opt_prof) { - return true; - } - - bool ret = false; - - malloc_mutex_lock(tsdn, &log_mtx); - - static bool prof_log_atexit_called = false; - if (!prof_log_atexit_called) { - prof_log_atexit_called = true; - if (atexit(prof_log_stop_final) != 0) { - malloc_write("<jemalloc>: Error in atexit() " - "for logging\n"); - if (opt_abort) { - abort(); - } - ret = true; - goto label_done; - } - } - - if (prof_logging_state != prof_logging_state_stopped) { - ret = true; - } else if (filename == NULL) { - /* Make default name. */ - prof_get_default_filename(tsdn, log_filename, log_seq); - log_seq++; - prof_logging_state = prof_logging_state_started; - } else if (strlen(filename) >= PROF_DUMP_FILENAME_LEN) { - ret = true; - } else { - strcpy(log_filename, filename); - prof_logging_state = prof_logging_state_started; - } - - if (!ret) { - nstime_prof_init_update(&log_start_timestamp); - } -label_done: - malloc_mutex_unlock(tsdn, &log_mtx); - - return ret; -} - -struct prof_emitter_cb_arg_s { - int fd; - ssize_t ret; -}; - -static void -prof_emitter_write_cb(void *opaque, const char *to_write) { - struct prof_emitter_cb_arg_s *arg = - (struct prof_emitter_cb_arg_s *)opaque; - size_t bytes = strlen(to_write); - if (prof_log_dummy) { - return; - } - arg->ret = malloc_write_fd(arg->fd, to_write, bytes); -} - -/* - * prof_log_emit_{...} goes through the appropriate linked list, emitting each - * node to the json and deallocating it. - */ -static void -prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) { - emitter_json_array_kv_begin(emitter, "threads"); - prof_thr_node_t *thr_node = log_thr_first; - prof_thr_node_t *thr_old_node; - while (thr_node != NULL) { - emitter_json_object_begin(emitter); - - emitter_json_kv(emitter, "thr_uid", emitter_type_uint64, - &thr_node->thr_uid); - - char *thr_name = thr_node->name; - - emitter_json_kv(emitter, "thr_name", emitter_type_string, - &thr_name); - - emitter_json_object_end(emitter); - thr_old_node = thr_node; - thr_node = thr_node->next; - idalloctm(tsd_tsdn(tsd), thr_old_node, NULL, NULL, true, true); - } - emitter_json_array_end(emitter); -} - -static void -prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) { - emitter_json_array_kv_begin(emitter, "stack_traces"); - prof_bt_node_t *bt_node = log_bt_first; - prof_bt_node_t *bt_old_node; - /* - * Calculate how many hex digits we need: twice number of bytes, two for - * "0x", and then one more for terminating '\0'. - */ - char buf[2 * sizeof(intptr_t) + 3]; - size_t buf_sz = sizeof(buf); - while (bt_node != NULL) { - emitter_json_array_begin(emitter); - size_t i; - for (i = 0; i < bt_node->bt.len; i++) { - malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]); - char *trace_str = buf; - emitter_json_value(emitter, emitter_type_string, - &trace_str); - } - emitter_json_array_end(emitter); - - bt_old_node = bt_node; - bt_node = bt_node->next; - idalloctm(tsd_tsdn(tsd), bt_old_node, NULL, NULL, true, true); - } - emitter_json_array_end(emitter); -} - -static void -prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) { - emitter_json_array_kv_begin(emitter, "allocations"); - prof_alloc_node_t *alloc_node = log_alloc_first; - prof_alloc_node_t *alloc_old_node; - while (alloc_node != NULL) { - emitter_json_object_begin(emitter); - - emitter_json_kv(emitter, "alloc_thread", emitter_type_size, - &alloc_node->alloc_thr_ind); - - emitter_json_kv(emitter, "free_thread", emitter_type_size, - &alloc_node->free_thr_ind); - - emitter_json_kv(emitter, "alloc_trace", emitter_type_size, - &alloc_node->alloc_bt_ind); - - emitter_json_kv(emitter, "free_trace", emitter_type_size, - &alloc_node->free_bt_ind); - - emitter_json_kv(emitter, "alloc_timestamp", - emitter_type_uint64, &alloc_node->alloc_time_ns); - - emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64, - &alloc_node->free_time_ns); - - emitter_json_kv(emitter, "usize", emitter_type_uint64, - &alloc_node->usize); - - emitter_json_object_end(emitter); - - alloc_old_node = alloc_node; - alloc_node = alloc_node->next; - idalloctm(tsd_tsdn(tsd), alloc_old_node, NULL, NULL, true, - true); - } - emitter_json_array_end(emitter); -} - -static void -prof_log_emit_metadata(emitter_t *emitter) { - emitter_json_object_kv_begin(emitter, "info"); - - nstime_t now; - - nstime_prof_init_update(&now); - uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp); - emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns); - - char *vers = JEMALLOC_VERSION; - emitter_json_kv(emitter, "version", - emitter_type_string, &vers); - - emitter_json_kv(emitter, "lg_sample_rate", - emitter_type_int, &lg_prof_sample); - - const char *res_type = prof_time_res_mode_names[opt_prof_time_res]; - emitter_json_kv(emitter, "prof_time_resolution", emitter_type_string, - &res_type); - - int pid = prof_getpid(); - emitter_json_kv(emitter, "pid", emitter_type_int, &pid); - - emitter_json_object_end(emitter); -} - -#define PROF_LOG_STOP_BUFSIZE PROF_DUMP_BUFSIZE -JEMALLOC_COLD -bool -prof_log_stop(tsdn_t *tsdn) { - cassert(config_prof); - if (!opt_prof || !prof_booted) { - return true; - } - - tsd_t *tsd = tsdn_tsd(tsdn); - malloc_mutex_lock(tsdn, &log_mtx); - - if (prof_logging_state != prof_logging_state_started) { - malloc_mutex_unlock(tsdn, &log_mtx); - return true; - } - - /* - * Set the state to dumping. We'll set it to stopped when we're done. - * Since other threads won't be able to start/stop/log when the state is - * dumping, we don't have to hold the lock during the whole method. - */ - prof_logging_state = prof_logging_state_dumping; - malloc_mutex_unlock(tsdn, &log_mtx); - - - emitter_t emitter; - - /* Create a file. */ - - int fd; - if (prof_log_dummy) { - fd = 0; - } else { - fd = creat(log_filename, 0644); - } - - if (fd == -1) { - malloc_printf("<jemalloc>: creat() for log file \"%s\" " - " failed with %d\n", log_filename, errno); - if (opt_abort) { - abort(); - } - return true; - } - - struct prof_emitter_cb_arg_s arg; - arg.fd = fd; - - buf_writer_t buf_writer; - buf_writer_init(tsdn, &buf_writer, prof_emitter_write_cb, &arg, NULL, - PROF_LOG_STOP_BUFSIZE); - emitter_init(&emitter, emitter_output_json_compact, buf_writer_cb, - &buf_writer); - - emitter_begin(&emitter); - prof_log_emit_metadata(&emitter); - prof_log_emit_threads(tsd, &emitter); - prof_log_emit_traces(tsd, &emitter); - prof_log_emit_allocs(tsd, &emitter); - emitter_end(&emitter); - - buf_writer_terminate(tsdn, &buf_writer); - - /* Reset global state. */ - if (log_tables_initialized) { - ckh_delete(tsd, &log_bt_node_set); - ckh_delete(tsd, &log_thr_node_set); - } - log_tables_initialized = false; - log_bt_index = 0; - log_thr_index = 0; - log_bt_first = NULL; - log_bt_last = NULL; - log_thr_first = NULL; - log_thr_last = NULL; - log_alloc_first = NULL; - log_alloc_last = NULL; - - malloc_mutex_lock(tsdn, &log_mtx); - prof_logging_state = prof_logging_state_stopped; - malloc_mutex_unlock(tsdn, &log_mtx); - - if (prof_log_dummy) { - return false; - } - return close(fd) || arg.ret == -1; -} -#undef PROF_LOG_STOP_BUFSIZE - -JEMALLOC_COLD -bool -prof_log_init(tsd_t *tsd) { - cassert(config_prof); - if (malloc_mutex_init(&log_mtx, "prof_log", - WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) { - return true; - } - - if (opt_prof_log) { - prof_log_start(tsd_tsdn(tsd), NULL); - } - - return false; -} - -/******************************************************************************/ diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/prof_recent.c b/fluent-bit/lib/jemalloc-5.3.0/src/prof_recent.c deleted file mode 100644 index 834a9446..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/prof_recent.c +++ /dev/null @@ -1,600 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/buf_writer.h" -#include "jemalloc/internal/emitter.h" -#include "jemalloc/internal/prof_data.h" -#include "jemalloc/internal/prof_recent.h" - -ssize_t opt_prof_recent_alloc_max = PROF_RECENT_ALLOC_MAX_DEFAULT; -malloc_mutex_t prof_recent_alloc_mtx; /* Protects the fields below */ -static atomic_zd_t prof_recent_alloc_max; -static ssize_t prof_recent_alloc_count = 0; -prof_recent_list_t prof_recent_alloc_list; - -malloc_mutex_t prof_recent_dump_mtx; /* Protects dumping. */ - -static void -prof_recent_alloc_max_init() { - atomic_store_zd(&prof_recent_alloc_max, opt_prof_recent_alloc_max, - ATOMIC_RELAXED); -} - -static inline ssize_t -prof_recent_alloc_max_get_no_lock() { - return atomic_load_zd(&prof_recent_alloc_max, ATOMIC_RELAXED); -} - -static inline ssize_t -prof_recent_alloc_max_get(tsd_t *tsd) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - return prof_recent_alloc_max_get_no_lock(); -} - -static inline ssize_t -prof_recent_alloc_max_update(tsd_t *tsd, ssize_t max) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - ssize_t old_max = prof_recent_alloc_max_get(tsd); - atomic_store_zd(&prof_recent_alloc_max, max, ATOMIC_RELAXED); - return old_max; -} - -static prof_recent_t * -prof_recent_allocate_node(tsdn_t *tsdn) { - return (prof_recent_t *)iallocztm(tsdn, sizeof(prof_recent_t), - sz_size2index(sizeof(prof_recent_t)), false, NULL, true, - arena_get(tsdn, 0, false), true); -} - -static void -prof_recent_free_node(tsdn_t *tsdn, prof_recent_t *node) { - assert(node != NULL); - assert(isalloc(tsdn, node) == sz_s2u(sizeof(prof_recent_t))); - idalloctm(tsdn, node, NULL, NULL, true, true); -} - -static inline void -increment_recent_count(tsd_t *tsd, prof_tctx_t *tctx) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); - ++tctx->recent_count; - assert(tctx->recent_count > 0); -} - -bool -prof_recent_alloc_prepare(tsd_t *tsd, prof_tctx_t *tctx) { - cassert(config_prof); - assert(opt_prof && prof_booted); - malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - - /* - * Check whether last-N mode is turned on without trying to acquire the - * lock, so as to optimize for the following two scenarios: - * (1) Last-N mode is switched off; - * (2) Dumping, during which last-N mode is temporarily turned off so - * as not to block sampled allocations. - */ - if (prof_recent_alloc_max_get_no_lock() == 0) { - return false; - } - - /* - * Increment recent_count to hold the tctx so that it won't be gone - * even after tctx->tdata->lock is released. This acts as a - * "placeholder"; the real recording of the allocation requires a lock - * on prof_recent_alloc_mtx and is done in prof_recent_alloc (when - * tctx->tdata->lock has been released). - */ - increment_recent_count(tsd, tctx); - return true; -} - -static void -decrement_recent_count(tsd_t *tsd, prof_tctx_t *tctx) { - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - assert(tctx != NULL); - malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock); - assert(tctx->recent_count > 0); - --tctx->recent_count; - prof_tctx_try_destroy(tsd, tctx); -} - -static inline edata_t * -prof_recent_alloc_edata_get_no_lock(const prof_recent_t *n) { - return (edata_t *)atomic_load_p(&n->alloc_edata, ATOMIC_ACQUIRE); -} - -edata_t * -prof_recent_alloc_edata_get_no_lock_test(const prof_recent_t *n) { - cassert(config_prof); - return prof_recent_alloc_edata_get_no_lock(n); -} - -static inline edata_t * -prof_recent_alloc_edata_get(tsd_t *tsd, const prof_recent_t *n) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - return prof_recent_alloc_edata_get_no_lock(n); -} - -static void -prof_recent_alloc_edata_set(tsd_t *tsd, prof_recent_t *n, edata_t *edata) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - atomic_store_p(&n->alloc_edata, edata, ATOMIC_RELEASE); -} - -void -edata_prof_recent_alloc_init(edata_t *edata) { - cassert(config_prof); - edata_prof_recent_alloc_set_dont_call_directly(edata, NULL); -} - -static inline prof_recent_t * -edata_prof_recent_alloc_get_no_lock(const edata_t *edata) { - cassert(config_prof); - return edata_prof_recent_alloc_get_dont_call_directly(edata); -} - -prof_recent_t * -edata_prof_recent_alloc_get_no_lock_test(const edata_t *edata) { - cassert(config_prof); - return edata_prof_recent_alloc_get_no_lock(edata); -} - -static inline prof_recent_t * -edata_prof_recent_alloc_get(tsd_t *tsd, const edata_t *edata) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_t *recent_alloc = - edata_prof_recent_alloc_get_no_lock(edata); - assert(recent_alloc == NULL || - prof_recent_alloc_edata_get(tsd, recent_alloc) == edata); - return recent_alloc; -} - -static prof_recent_t * -edata_prof_recent_alloc_update_internal(tsd_t *tsd, edata_t *edata, - prof_recent_t *recent_alloc) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_t *old_recent_alloc = - edata_prof_recent_alloc_get(tsd, edata); - edata_prof_recent_alloc_set_dont_call_directly(edata, recent_alloc); - return old_recent_alloc; -} - -static void -edata_prof_recent_alloc_set(tsd_t *tsd, edata_t *edata, - prof_recent_t *recent_alloc) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - assert(recent_alloc != NULL); - prof_recent_t *old_recent_alloc = - edata_prof_recent_alloc_update_internal(tsd, edata, recent_alloc); - assert(old_recent_alloc == NULL); - prof_recent_alloc_edata_set(tsd, recent_alloc, edata); -} - -static void -edata_prof_recent_alloc_reset(tsd_t *tsd, edata_t *edata, - prof_recent_t *recent_alloc) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - assert(recent_alloc != NULL); - prof_recent_t *old_recent_alloc = - edata_prof_recent_alloc_update_internal(tsd, edata, NULL); - assert(old_recent_alloc == recent_alloc); - assert(edata == prof_recent_alloc_edata_get(tsd, recent_alloc)); - prof_recent_alloc_edata_set(tsd, recent_alloc, NULL); -} - -/* - * This function should be called right before an allocation is released, so - * that the associated recent allocation record can contain the following - * information: - * (1) The allocation is released; - * (2) The time of the deallocation; and - * (3) The prof_tctx associated with the deallocation. - */ -void -prof_recent_alloc_reset(tsd_t *tsd, edata_t *edata) { - cassert(config_prof); - /* - * Check whether the recent allocation record still exists without - * trying to acquire the lock. - */ - if (edata_prof_recent_alloc_get_no_lock(edata) == NULL) { - return; - } - - prof_tctx_t *dalloc_tctx = prof_tctx_create(tsd); - /* - * In case dalloc_tctx is NULL, e.g. due to OOM, we will not record the - * deallocation time / tctx, which is handled later, after we check - * again when holding the lock. - */ - - if (dalloc_tctx != NULL) { - malloc_mutex_lock(tsd_tsdn(tsd), dalloc_tctx->tdata->lock); - increment_recent_count(tsd, dalloc_tctx); - dalloc_tctx->prepared = false; - malloc_mutex_unlock(tsd_tsdn(tsd), dalloc_tctx->tdata->lock); - } - - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - /* Check again after acquiring the lock. */ - prof_recent_t *recent = edata_prof_recent_alloc_get(tsd, edata); - if (recent != NULL) { - assert(nstime_equals_zero(&recent->dalloc_time)); - assert(recent->dalloc_tctx == NULL); - if (dalloc_tctx != NULL) { - nstime_prof_update(&recent->dalloc_time); - recent->dalloc_tctx = dalloc_tctx; - dalloc_tctx = NULL; - } - edata_prof_recent_alloc_reset(tsd, edata, recent); - } - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - - if (dalloc_tctx != NULL) { - /* We lost the rase - the allocation record was just gone. */ - decrement_recent_count(tsd, dalloc_tctx); - } -} - -static void -prof_recent_alloc_evict_edata(tsd_t *tsd, prof_recent_t *recent_alloc) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - edata_t *edata = prof_recent_alloc_edata_get(tsd, recent_alloc); - if (edata != NULL) { - edata_prof_recent_alloc_reset(tsd, edata, recent_alloc); - } -} - -static bool -prof_recent_alloc_is_empty(tsd_t *tsd) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - if (ql_empty(&prof_recent_alloc_list)) { - assert(prof_recent_alloc_count == 0); - return true; - } else { - assert(prof_recent_alloc_count > 0); - return false; - } -} - -static void -prof_recent_alloc_assert_count(tsd_t *tsd) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - if (!config_debug) { - return; - } - ssize_t count = 0; - prof_recent_t *n; - ql_foreach(n, &prof_recent_alloc_list, link) { - ++count; - } - assert(count == prof_recent_alloc_count); - assert(prof_recent_alloc_max_get(tsd) == -1 || - count <= prof_recent_alloc_max_get(tsd)); -} - -void -prof_recent_alloc(tsd_t *tsd, edata_t *edata, size_t size, size_t usize) { - cassert(config_prof); - assert(edata != NULL); - prof_tctx_t *tctx = edata_prof_tctx_get(edata); - - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_alloc_assert_count(tsd); - - /* - * Reserve a new prof_recent_t node if needed. If needed, we release - * the prof_recent_alloc_mtx lock and allocate. Then, rather than - * immediately checking for OOM, we regain the lock and try to make use - * of the reserve node if needed. There are six scenarios: - * - * \ now | no need | need but OOMed | need and allocated - * later \ | | | - * ------------------------------------------------------------ - * no need | (1) | (2) | (3) - * ------------------------------------------------------------ - * need | (4) | (5) | (6) - * - * First, "(4)" never happens, because we don't release the lock in the - * middle if there's no need for a new node; in such cases "(1)" always - * takes place, which is trivial. - * - * Out of the remaining four scenarios, "(6)" is the common case and is - * trivial. "(5)" is also trivial, in which case we'll rollback the - * effect of prof_recent_alloc_prepare() as expected. - * - * "(2)" / "(3)" occurs when the need for a new node is gone after we - * regain the lock. If the new node is successfully allocated, i.e. in - * the case of "(3)", we'll release it in the end; otherwise, i.e. in - * the case of "(2)", we do nothing - we're lucky that the OOM ends up - * doing no harm at all. - * - * Therefore, the only performance cost of the "release lock" -> - * "allocate" -> "regain lock" design is the "(3)" case, but it happens - * very rarely, so the cost is relatively small compared to the gain of - * not having to have the lock order of prof_recent_alloc_mtx above all - * the allocation locks. - */ - prof_recent_t *reserve = NULL; - if (prof_recent_alloc_max_get(tsd) == -1 || - prof_recent_alloc_count < prof_recent_alloc_max_get(tsd)) { - assert(prof_recent_alloc_max_get(tsd) != 0); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - reserve = prof_recent_allocate_node(tsd_tsdn(tsd)); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_alloc_assert_count(tsd); - } - - if (prof_recent_alloc_max_get(tsd) == 0) { - assert(prof_recent_alloc_is_empty(tsd)); - goto label_rollback; - } - - prof_tctx_t *old_alloc_tctx, *old_dalloc_tctx; - if (prof_recent_alloc_count == prof_recent_alloc_max_get(tsd)) { - /* If upper limit is reached, rotate the head. */ - assert(prof_recent_alloc_max_get(tsd) != -1); - assert(!prof_recent_alloc_is_empty(tsd)); - prof_recent_t *head = ql_first(&prof_recent_alloc_list); - old_alloc_tctx = head->alloc_tctx; - assert(old_alloc_tctx != NULL); - old_dalloc_tctx = head->dalloc_tctx; - prof_recent_alloc_evict_edata(tsd, head); - ql_rotate(&prof_recent_alloc_list, link); - } else { - /* Otherwise make use of the new node. */ - assert(prof_recent_alloc_max_get(tsd) == -1 || - prof_recent_alloc_count < prof_recent_alloc_max_get(tsd)); - if (reserve == NULL) { - goto label_rollback; - } - ql_elm_new(reserve, link); - ql_tail_insert(&prof_recent_alloc_list, reserve, link); - reserve = NULL; - old_alloc_tctx = NULL; - old_dalloc_tctx = NULL; - ++prof_recent_alloc_count; - } - - /* Fill content into the tail node. */ - prof_recent_t *tail = ql_last(&prof_recent_alloc_list, link); - assert(tail != NULL); - tail->size = size; - tail->usize = usize; - nstime_copy(&tail->alloc_time, edata_prof_alloc_time_get(edata)); - tail->alloc_tctx = tctx; - nstime_init_zero(&tail->dalloc_time); - tail->dalloc_tctx = NULL; - edata_prof_recent_alloc_set(tsd, edata, tail); - - assert(!prof_recent_alloc_is_empty(tsd)); - prof_recent_alloc_assert_count(tsd); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - - if (reserve != NULL) { - prof_recent_free_node(tsd_tsdn(tsd), reserve); - } - - /* - * Asynchronously handle the tctx of the old node, so that there's no - * simultaneous holdings of prof_recent_alloc_mtx and tdata->lock. - * In the worst case this may delay the tctx release but it's better - * than holding prof_recent_alloc_mtx for longer. - */ - if (old_alloc_tctx != NULL) { - decrement_recent_count(tsd, old_alloc_tctx); - } - if (old_dalloc_tctx != NULL) { - decrement_recent_count(tsd, old_dalloc_tctx); - } - return; - -label_rollback: - assert(edata_prof_recent_alloc_get(tsd, edata) == NULL); - prof_recent_alloc_assert_count(tsd); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - if (reserve != NULL) { - prof_recent_free_node(tsd_tsdn(tsd), reserve); - } - decrement_recent_count(tsd, tctx); -} - -ssize_t -prof_recent_alloc_max_ctl_read() { - cassert(config_prof); - /* Don't bother to acquire the lock. */ - return prof_recent_alloc_max_get_no_lock(); -} - -static void -prof_recent_alloc_restore_locked(tsd_t *tsd, prof_recent_list_t *to_delete) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - ssize_t max = prof_recent_alloc_max_get(tsd); - if (max == -1 || prof_recent_alloc_count <= max) { - /* Easy case - no need to alter the list. */ - ql_new(to_delete); - prof_recent_alloc_assert_count(tsd); - return; - } - - prof_recent_t *node; - ql_foreach(node, &prof_recent_alloc_list, link) { - if (prof_recent_alloc_count == max) { - break; - } - prof_recent_alloc_evict_edata(tsd, node); - --prof_recent_alloc_count; - } - assert(prof_recent_alloc_count == max); - - ql_move(to_delete, &prof_recent_alloc_list); - if (max == 0) { - assert(node == NULL); - } else { - assert(node != NULL); - ql_split(to_delete, node, &prof_recent_alloc_list, link); - } - assert(!ql_empty(to_delete)); - prof_recent_alloc_assert_count(tsd); -} - -static void -prof_recent_alloc_async_cleanup(tsd_t *tsd, prof_recent_list_t *to_delete) { - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_dump_mtx); - malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - while (!ql_empty(to_delete)) { - prof_recent_t *node = ql_first(to_delete); - ql_remove(to_delete, node, link); - decrement_recent_count(tsd, node->alloc_tctx); - if (node->dalloc_tctx != NULL) { - decrement_recent_count(tsd, node->dalloc_tctx); - } - prof_recent_free_node(tsd_tsdn(tsd), node); - } -} - -ssize_t -prof_recent_alloc_max_ctl_write(tsd_t *tsd, ssize_t max) { - cassert(config_prof); - assert(max >= -1); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_alloc_assert_count(tsd); - const ssize_t old_max = prof_recent_alloc_max_update(tsd, max); - prof_recent_list_t to_delete; - prof_recent_alloc_restore_locked(tsd, &to_delete); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_alloc_async_cleanup(tsd, &to_delete); - return old_max; -} - -static void -prof_recent_alloc_dump_bt(emitter_t *emitter, prof_tctx_t *tctx) { - char bt_buf[2 * sizeof(intptr_t) + 3]; - char *s = bt_buf; - assert(tctx != NULL); - prof_bt_t *bt = &tctx->gctx->bt; - for (size_t i = 0; i < bt->len; ++i) { - malloc_snprintf(bt_buf, sizeof(bt_buf), "%p", bt->vec[i]); - emitter_json_value(emitter, emitter_type_string, &s); - } -} - -static void -prof_recent_alloc_dump_node(emitter_t *emitter, prof_recent_t *node) { - emitter_json_object_begin(emitter); - - emitter_json_kv(emitter, "size", emitter_type_size, &node->size); - emitter_json_kv(emitter, "usize", emitter_type_size, &node->usize); - bool released = prof_recent_alloc_edata_get_no_lock(node) == NULL; - emitter_json_kv(emitter, "released", emitter_type_bool, &released); - - emitter_json_kv(emitter, "alloc_thread_uid", emitter_type_uint64, - &node->alloc_tctx->thr_uid); - prof_tdata_t *alloc_tdata = node->alloc_tctx->tdata; - assert(alloc_tdata != NULL); - if (alloc_tdata->thread_name != NULL) { - emitter_json_kv(emitter, "alloc_thread_name", - emitter_type_string, &alloc_tdata->thread_name); - } - uint64_t alloc_time_ns = nstime_ns(&node->alloc_time); - emitter_json_kv(emitter, "alloc_time", emitter_type_uint64, - &alloc_time_ns); - emitter_json_array_kv_begin(emitter, "alloc_trace"); - prof_recent_alloc_dump_bt(emitter, node->alloc_tctx); - emitter_json_array_end(emitter); - - if (released && node->dalloc_tctx != NULL) { - emitter_json_kv(emitter, "dalloc_thread_uid", - emitter_type_uint64, &node->dalloc_tctx->thr_uid); - prof_tdata_t *dalloc_tdata = node->dalloc_tctx->tdata; - assert(dalloc_tdata != NULL); - if (dalloc_tdata->thread_name != NULL) { - emitter_json_kv(emitter, "dalloc_thread_name", - emitter_type_string, &dalloc_tdata->thread_name); - } - assert(!nstime_equals_zero(&node->dalloc_time)); - uint64_t dalloc_time_ns = nstime_ns(&node->dalloc_time); - emitter_json_kv(emitter, "dalloc_time", emitter_type_uint64, - &dalloc_time_ns); - emitter_json_array_kv_begin(emitter, "dalloc_trace"); - prof_recent_alloc_dump_bt(emitter, node->dalloc_tctx); - emitter_json_array_end(emitter); - } - - emitter_json_object_end(emitter); -} - -#define PROF_RECENT_PRINT_BUFSIZE 65536 -JEMALLOC_COLD -void -prof_recent_alloc_dump(tsd_t *tsd, write_cb_t *write_cb, void *cbopaque) { - cassert(config_prof); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_dump_mtx); - buf_writer_t buf_writer; - buf_writer_init(tsd_tsdn(tsd), &buf_writer, write_cb, cbopaque, NULL, - PROF_RECENT_PRINT_BUFSIZE); - emitter_t emitter; - emitter_init(&emitter, emitter_output_json_compact, buf_writer_cb, - &buf_writer); - prof_recent_list_t temp_list; - - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_alloc_assert_count(tsd); - ssize_t dump_max = prof_recent_alloc_max_get(tsd); - ql_move(&temp_list, &prof_recent_alloc_list); - ssize_t dump_count = prof_recent_alloc_count; - prof_recent_alloc_count = 0; - prof_recent_alloc_assert_count(tsd); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - - emitter_begin(&emitter); - uint64_t sample_interval = (uint64_t)1U << lg_prof_sample; - emitter_json_kv(&emitter, "sample_interval", emitter_type_uint64, - &sample_interval); - emitter_json_kv(&emitter, "recent_alloc_max", emitter_type_ssize, - &dump_max); - emitter_json_array_kv_begin(&emitter, "recent_alloc"); - prof_recent_t *node; - ql_foreach(node, &temp_list, link) { - prof_recent_alloc_dump_node(&emitter, node); - } - emitter_json_array_end(&emitter); - emitter_end(&emitter); - - malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - prof_recent_alloc_assert_count(tsd); - ql_concat(&temp_list, &prof_recent_alloc_list, link); - ql_move(&prof_recent_alloc_list, &temp_list); - prof_recent_alloc_count += dump_count; - prof_recent_alloc_restore_locked(tsd, &temp_list); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx); - - buf_writer_terminate(tsd_tsdn(tsd), &buf_writer); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_dump_mtx); - - prof_recent_alloc_async_cleanup(tsd, &temp_list); -} -#undef PROF_RECENT_PRINT_BUFSIZE - -bool -prof_recent_init() { - cassert(config_prof); - prof_recent_alloc_max_init(); - - if (malloc_mutex_init(&prof_recent_alloc_mtx, "prof_recent_alloc", - WITNESS_RANK_PROF_RECENT_ALLOC, malloc_mutex_rank_exclusive)) { - return true; - } - - if (malloc_mutex_init(&prof_recent_dump_mtx, "prof_recent_dump", - WITNESS_RANK_PROF_RECENT_DUMP, malloc_mutex_rank_exclusive)) { - return true; - } - - ql_new(&prof_recent_alloc_list); - - return false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/prof_stats.c b/fluent-bit/lib/jemalloc-5.3.0/src/prof_stats.c deleted file mode 100644 index 5d1a506b..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/prof_stats.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/prof_stats.h" - -bool opt_prof_stats = false; -malloc_mutex_t prof_stats_mtx; -static prof_stats_t prof_stats_live[PROF_SC_NSIZES]; -static prof_stats_t prof_stats_accum[PROF_SC_NSIZES]; - -static void -prof_stats_enter(tsd_t *tsd, szind_t ind) { - assert(opt_prof && opt_prof_stats); - assert(ind < SC_NSIZES); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_stats_mtx); -} - -static void -prof_stats_leave(tsd_t *tsd) { - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_stats_mtx); -} - -void -prof_stats_inc(tsd_t *tsd, szind_t ind, size_t size) { - cassert(config_prof); - prof_stats_enter(tsd, ind); - prof_stats_live[ind].req_sum += size; - prof_stats_live[ind].count++; - prof_stats_accum[ind].req_sum += size; - prof_stats_accum[ind].count++; - prof_stats_leave(tsd); -} - -void -prof_stats_dec(tsd_t *tsd, szind_t ind, size_t size) { - cassert(config_prof); - prof_stats_enter(tsd, ind); - prof_stats_live[ind].req_sum -= size; - prof_stats_live[ind].count--; - prof_stats_leave(tsd); -} - -void -prof_stats_get_live(tsd_t *tsd, szind_t ind, prof_stats_t *stats) { - cassert(config_prof); - prof_stats_enter(tsd, ind); - memcpy(stats, &prof_stats_live[ind], sizeof(prof_stats_t)); - prof_stats_leave(tsd); -} - -void -prof_stats_get_accum(tsd_t *tsd, szind_t ind, prof_stats_t *stats) { - cassert(config_prof); - prof_stats_enter(tsd, ind); - memcpy(stats, &prof_stats_accum[ind], sizeof(prof_stats_t)); - prof_stats_leave(tsd); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/prof_sys.c b/fluent-bit/lib/jemalloc-5.3.0/src/prof_sys.c deleted file mode 100644 index b5f1f5b2..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/prof_sys.c +++ /dev/null @@ -1,669 +0,0 @@ -#define JEMALLOC_PROF_SYS_C_ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/buf_writer.h" -#include "jemalloc/internal/ctl.h" -#include "jemalloc/internal/prof_data.h" -#include "jemalloc/internal/prof_sys.h" - -#ifdef JEMALLOC_PROF_LIBUNWIND -#define UNW_LOCAL_ONLY -#include <libunwind.h> -#endif - -#ifdef JEMALLOC_PROF_LIBGCC -/* - * We have a circular dependency -- jemalloc_internal.h tells us if we should - * use libgcc's unwinding functionality, but after we've included that, we've - * already hooked _Unwind_Backtrace. We'll temporarily disable hooking. - */ -#undef _Unwind_Backtrace -#include <unwind.h> -#define _Unwind_Backtrace JEMALLOC_TEST_HOOK(_Unwind_Backtrace, test_hooks_libc_hook) -#endif - -/******************************************************************************/ - -malloc_mutex_t prof_dump_filename_mtx; - -bool prof_do_mock = false; - -static uint64_t prof_dump_seq; -static uint64_t prof_dump_iseq; -static uint64_t prof_dump_mseq; -static uint64_t prof_dump_useq; - -static char *prof_prefix = NULL; - -/* The fallback allocator profiling functionality will use. */ -base_t *prof_base; - -void -bt_init(prof_bt_t *bt, void **vec) { - cassert(config_prof); - - bt->vec = vec; - bt->len = 0; -} - -#ifdef JEMALLOC_PROF_LIBUNWIND -static void -prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { - int nframes; - - cassert(config_prof); - assert(*len == 0); - assert(vec != NULL); - assert(max_len == PROF_BT_MAX); - - nframes = unw_backtrace(vec, PROF_BT_MAX); - if (nframes <= 0) { - return; - } - *len = nframes; -} -#elif (defined(JEMALLOC_PROF_LIBGCC)) -static _Unwind_Reason_Code -prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) { - cassert(config_prof); - - return _URC_NO_REASON; -} - -static _Unwind_Reason_Code -prof_unwind_callback(struct _Unwind_Context *context, void *arg) { - prof_unwind_data_t *data = (prof_unwind_data_t *)arg; - void *ip; - - cassert(config_prof); - - ip = (void *)_Unwind_GetIP(context); - if (ip == NULL) { - return _URC_END_OF_STACK; - } - data->vec[*data->len] = ip; - (*data->len)++; - if (*data->len == data->max) { - return _URC_END_OF_STACK; - } - - return _URC_NO_REASON; -} - -static void -prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { - prof_unwind_data_t data = {vec, len, max_len}; - - cassert(config_prof); - assert(vec != NULL); - assert(max_len == PROF_BT_MAX); - - _Unwind_Backtrace(prof_unwind_callback, &data); -} -#elif (defined(JEMALLOC_PROF_GCC)) -static void -prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { -#define BT_FRAME(i) \ - if ((i) < max_len) { \ - void *p; \ - if (__builtin_frame_address(i) == 0) { \ - return; \ - } \ - p = __builtin_return_address(i); \ - if (p == NULL) { \ - return; \ - } \ - vec[(i)] = p; \ - *len = (i) + 1; \ - } else { \ - return; \ - } - - cassert(config_prof); - assert(vec != NULL); - assert(max_len == PROF_BT_MAX); - - BT_FRAME(0) - BT_FRAME(1) - BT_FRAME(2) - BT_FRAME(3) - BT_FRAME(4) - BT_FRAME(5) - BT_FRAME(6) - BT_FRAME(7) - BT_FRAME(8) - BT_FRAME(9) - - BT_FRAME(10) - BT_FRAME(11) - BT_FRAME(12) - BT_FRAME(13) - BT_FRAME(14) - BT_FRAME(15) - BT_FRAME(16) - BT_FRAME(17) - BT_FRAME(18) - BT_FRAME(19) - - BT_FRAME(20) - BT_FRAME(21) - BT_FRAME(22) - BT_FRAME(23) - BT_FRAME(24) - BT_FRAME(25) - BT_FRAME(26) - BT_FRAME(27) - BT_FRAME(28) - BT_FRAME(29) - - BT_FRAME(30) - BT_FRAME(31) - BT_FRAME(32) - BT_FRAME(33) - BT_FRAME(34) - BT_FRAME(35) - BT_FRAME(36) - BT_FRAME(37) - BT_FRAME(38) - BT_FRAME(39) - - BT_FRAME(40) - BT_FRAME(41) - BT_FRAME(42) - BT_FRAME(43) - BT_FRAME(44) - BT_FRAME(45) - BT_FRAME(46) - BT_FRAME(47) - BT_FRAME(48) - BT_FRAME(49) - - BT_FRAME(50) - BT_FRAME(51) - BT_FRAME(52) - BT_FRAME(53) - BT_FRAME(54) - BT_FRAME(55) - BT_FRAME(56) - BT_FRAME(57) - BT_FRAME(58) - BT_FRAME(59) - - BT_FRAME(60) - BT_FRAME(61) - BT_FRAME(62) - BT_FRAME(63) - BT_FRAME(64) - BT_FRAME(65) - BT_FRAME(66) - BT_FRAME(67) - BT_FRAME(68) - BT_FRAME(69) - - BT_FRAME(70) - BT_FRAME(71) - BT_FRAME(72) - BT_FRAME(73) - BT_FRAME(74) - BT_FRAME(75) - BT_FRAME(76) - BT_FRAME(77) - BT_FRAME(78) - BT_FRAME(79) - - BT_FRAME(80) - BT_FRAME(81) - BT_FRAME(82) - BT_FRAME(83) - BT_FRAME(84) - BT_FRAME(85) - BT_FRAME(86) - BT_FRAME(87) - BT_FRAME(88) - BT_FRAME(89) - - BT_FRAME(90) - BT_FRAME(91) - BT_FRAME(92) - BT_FRAME(93) - BT_FRAME(94) - BT_FRAME(95) - BT_FRAME(96) - BT_FRAME(97) - BT_FRAME(98) - BT_FRAME(99) - - BT_FRAME(100) - BT_FRAME(101) - BT_FRAME(102) - BT_FRAME(103) - BT_FRAME(104) - BT_FRAME(105) - BT_FRAME(106) - BT_FRAME(107) - BT_FRAME(108) - BT_FRAME(109) - - BT_FRAME(110) - BT_FRAME(111) - BT_FRAME(112) - BT_FRAME(113) - BT_FRAME(114) - BT_FRAME(115) - BT_FRAME(116) - BT_FRAME(117) - BT_FRAME(118) - BT_FRAME(119) - - BT_FRAME(120) - BT_FRAME(121) - BT_FRAME(122) - BT_FRAME(123) - BT_FRAME(124) - BT_FRAME(125) - BT_FRAME(126) - BT_FRAME(127) -#undef BT_FRAME -} -#else -static void -prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { - cassert(config_prof); - not_reached(); -} -#endif - -void -prof_backtrace(tsd_t *tsd, prof_bt_t *bt) { - cassert(config_prof); - prof_backtrace_hook_t prof_backtrace_hook = prof_backtrace_hook_get(); - assert(prof_backtrace_hook != NULL); - - pre_reentrancy(tsd, NULL); - prof_backtrace_hook(bt->vec, &bt->len, PROF_BT_MAX); - post_reentrancy(tsd); -} - -void -prof_hooks_init() { - prof_backtrace_hook_set(&prof_backtrace_impl); - prof_dump_hook_set(NULL); -} - -void -prof_unwind_init() { -#ifdef JEMALLOC_PROF_LIBGCC - /* - * Cause the backtracing machinery to allocate its internal - * state before enabling profiling. - */ - _Unwind_Backtrace(prof_unwind_init_callback, NULL); -#endif -} - -static int -prof_sys_thread_name_read_impl(char *buf, size_t limit) { -#if defined(JEMALLOC_HAVE_PTHREAD_GETNAME_NP) - return pthread_getname_np(pthread_self(), buf, limit); -#elif defined(JEMALLOC_HAVE_PTHREAD_GET_NAME_NP) - pthread_get_name_np(pthread_self(), buf, limit); - return 0; -#else - return ENOSYS; -#endif -} -prof_sys_thread_name_read_t *JET_MUTABLE prof_sys_thread_name_read = - prof_sys_thread_name_read_impl; - -void -prof_sys_thread_name_fetch(tsd_t *tsd) { -#define THREAD_NAME_MAX_LEN 16 - char buf[THREAD_NAME_MAX_LEN]; - if (!prof_sys_thread_name_read(buf, THREAD_NAME_MAX_LEN)) { - prof_thread_name_set_impl(tsd, buf); - } -#undef THREAD_NAME_MAX_LEN -} - -int -prof_getpid(void) { -#ifdef _WIN32 - return GetCurrentProcessId(); -#else - return getpid(); -#endif -} - -/* - * This buffer is rather large for stack allocation, so use a single buffer for - * all profile dumps; protected by prof_dump_mtx. - */ -static char prof_dump_buf[PROF_DUMP_BUFSIZE]; - -typedef struct prof_dump_arg_s prof_dump_arg_t; -struct prof_dump_arg_s { - /* - * Whether error should be handled locally: if true, then we print out - * error message as well as abort (if opt_abort is true) when an error - * occurred, and we also report the error back to the caller in the end; - * if false, then we only report the error back to the caller in the - * end. - */ - const bool handle_error_locally; - /* - * Whether there has been an error in the dumping process, which could - * have happened either in file opening or in file writing. When an - * error has already occurred, we will stop further writing to the file. - */ - bool error; - /* File descriptor of the dump file. */ - int prof_dump_fd; -}; - -static void -prof_dump_check_possible_error(prof_dump_arg_t *arg, bool err_cond, - const char *format, ...) { - assert(!arg->error); - if (!err_cond) { - return; - } - - arg->error = true; - if (!arg->handle_error_locally) { - return; - } - - va_list ap; - char buf[PROF_PRINTF_BUFSIZE]; - va_start(ap, format); - malloc_vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - malloc_write(buf); - - if (opt_abort) { - abort(); - } -} - -static int -prof_dump_open_file_impl(const char *filename, int mode) { - return creat(filename, mode); -} -prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file = - prof_dump_open_file_impl; - -static void -prof_dump_open(prof_dump_arg_t *arg, const char *filename) { - arg->prof_dump_fd = prof_dump_open_file(filename, 0644); - prof_dump_check_possible_error(arg, arg->prof_dump_fd == -1, - "<jemalloc>: failed to open \"%s\"\n", filename); -} - -prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file = malloc_write_fd; - -static void -prof_dump_flush(void *opaque, const char *s) { - cassert(config_prof); - prof_dump_arg_t *arg = (prof_dump_arg_t *)opaque; - if (!arg->error) { - ssize_t err = prof_dump_write_file(arg->prof_dump_fd, s, - strlen(s)); - prof_dump_check_possible_error(arg, err == -1, - "<jemalloc>: failed to write during heap profile flush\n"); - } -} - -static void -prof_dump_close(prof_dump_arg_t *arg) { - if (arg->prof_dump_fd != -1) { - close(arg->prof_dump_fd); - } -} - -#ifndef _WIN32 -JEMALLOC_FORMAT_PRINTF(1, 2) -static int -prof_open_maps_internal(const char *format, ...) { - int mfd; - va_list ap; - char filename[PATH_MAX + 1]; - - va_start(ap, format); - malloc_vsnprintf(filename, sizeof(filename), format, ap); - va_end(ap); - -#if defined(O_CLOEXEC) - mfd = open(filename, O_RDONLY | O_CLOEXEC); -#else - mfd = open(filename, O_RDONLY); - if (mfd != -1) { - fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC); - } -#endif - - return mfd; -} -#endif - -static int -prof_dump_open_maps_impl() { - int mfd; - - cassert(config_prof); -#if defined(__FreeBSD__) || defined(__DragonFly__) - mfd = prof_open_maps_internal("/proc/curproc/map"); -#elif defined(_WIN32) - mfd = -1; // Not implemented -#else - int pid = prof_getpid(); - - mfd = prof_open_maps_internal("/proc/%d/task/%d/maps", pid, pid); - if (mfd == -1) { - mfd = prof_open_maps_internal("/proc/%d/maps", pid); - } -#endif - return mfd; -} -prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps = - prof_dump_open_maps_impl; - -static ssize_t -prof_dump_read_maps_cb(void *read_cbopaque, void *buf, size_t limit) { - int mfd = *(int *)read_cbopaque; - assert(mfd != -1); - return malloc_read_fd(mfd, buf, limit); -} - -static void -prof_dump_maps(buf_writer_t *buf_writer) { - int mfd = prof_dump_open_maps(); - if (mfd == -1) { - return; - } - - buf_writer_cb(buf_writer, "\nMAPPED_LIBRARIES:\n"); - buf_writer_pipe(buf_writer, prof_dump_read_maps_cb, &mfd); - close(mfd); -} - -static bool -prof_dump(tsd_t *tsd, bool propagate_err, const char *filename, - bool leakcheck) { - cassert(config_prof); - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t * tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return true; - } - - prof_dump_arg_t arg = {/* handle_error_locally */ !propagate_err, - /* error */ false, /* prof_dump_fd */ -1}; - - pre_reentrancy(tsd, NULL); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx); - - prof_dump_open(&arg, filename); - buf_writer_t buf_writer; - bool err = buf_writer_init(tsd_tsdn(tsd), &buf_writer, prof_dump_flush, - &arg, prof_dump_buf, PROF_DUMP_BUFSIZE); - assert(!err); - prof_dump_impl(tsd, buf_writer_cb, &buf_writer, tdata, leakcheck); - prof_dump_maps(&buf_writer); - buf_writer_terminate(tsd_tsdn(tsd), &buf_writer); - prof_dump_close(&arg); - - prof_dump_hook_t dump_hook = prof_dump_hook_get(); - if (dump_hook != NULL) { - dump_hook(filename); - } - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx); - post_reentrancy(tsd); - - return arg.error; -} - -/* - * If profiling is off, then PROF_DUMP_FILENAME_LEN is 1, so we'll end up - * calling strncpy with a size of 0, which triggers a -Wstringop-truncation - * warning (strncpy can never actually be called in this case, since we bail out - * much earlier when config_prof is false). This function works around the - * warning to let us leave the warning on. - */ -static inline void -prof_strncpy(char *UNUSED dest, const char *UNUSED src, size_t UNUSED size) { - cassert(config_prof); -#ifdef JEMALLOC_PROF - strncpy(dest, src, size); -#endif -} - -static const char * -prof_prefix_get(tsdn_t* tsdn) { - malloc_mutex_assert_owner(tsdn, &prof_dump_filename_mtx); - - return prof_prefix == NULL ? opt_prof_prefix : prof_prefix; -} - -static bool -prof_prefix_is_empty(tsdn_t *tsdn) { - malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); - bool ret = (prof_prefix_get(tsdn)[0] == '\0'); - malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); - return ret; -} - -#define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1) -#define VSEQ_INVALID UINT64_C(0xffffffffffffffff) -static void -prof_dump_filename(tsd_t *tsd, char *filename, char v, uint64_t vseq) { - cassert(config_prof); - - assert(tsd_reentrancy_level_get(tsd) == 0); - const char *prefix = prof_prefix_get(tsd_tsdn(tsd)); - - if (vseq != VSEQ_INVALID) { - /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */ - malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, - "%s.%d.%"FMTu64".%c%"FMTu64".heap", prefix, prof_getpid(), - prof_dump_seq, v, vseq); - } else { - /* "<prefix>.<pid>.<seq>.<v>.heap" */ - malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, - "%s.%d.%"FMTu64".%c.heap", prefix, prof_getpid(), - prof_dump_seq, v); - } - prof_dump_seq++; -} - -void -prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind) { - malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); - malloc_snprintf(filename, PROF_DUMP_FILENAME_LEN, - "%s.%d.%"FMTu64".json", prof_prefix_get(tsdn), prof_getpid(), ind); - malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); -} - -void -prof_fdump_impl(tsd_t *tsd) { - char filename[DUMP_FILENAME_BUFSIZE]; - - assert(!prof_prefix_is_empty(tsd_tsdn(tsd))); - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump_filename(tsd, filename, 'f', VSEQ_INVALID); - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump(tsd, false, filename, opt_prof_leak); -} - -bool -prof_prefix_set(tsdn_t *tsdn, const char *prefix) { - cassert(config_prof); - ctl_mtx_assert_held(tsdn); - malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); - if (prof_prefix == NULL) { - malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); - /* Everything is still guarded by ctl_mtx. */ - char *buffer = base_alloc(tsdn, prof_base, - PROF_DUMP_FILENAME_LEN, QUANTUM); - if (buffer == NULL) { - return true; - } - malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); - prof_prefix = buffer; - } - assert(prof_prefix != NULL); - - prof_strncpy(prof_prefix, prefix, PROF_DUMP_FILENAME_LEN - 1); - prof_prefix[PROF_DUMP_FILENAME_LEN - 1] = '\0'; - malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); - - return false; -} - -void -prof_idump_impl(tsd_t *tsd) { - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') { - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - return; - } - char filename[PATH_MAX + 1]; - prof_dump_filename(tsd, filename, 'i', prof_dump_iseq); - prof_dump_iseq++; - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump(tsd, false, filename, false); -} - -bool -prof_mdump_impl(tsd_t *tsd, const char *filename) { - char filename_buf[DUMP_FILENAME_BUFSIZE]; - if (filename == NULL) { - /* No filename specified, so automatically generate one. */ - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') { - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - return true; - } - prof_dump_filename(tsd, filename_buf, 'm', prof_dump_mseq); - prof_dump_mseq++; - malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - filename = filename_buf; - } - return prof_dump(tsd, true, filename, false); -} - -void -prof_gdump_impl(tsd_t *tsd) { - tsdn_t *tsdn = tsd_tsdn(tsd); - malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); - if (prof_prefix_get(tsdn)[0] == '\0') { - malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); - return; - } - char filename[DUMP_FILENAME_BUFSIZE]; - prof_dump_filename(tsd, filename, 'u', prof_dump_useq); - prof_dump_useq++; - malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); - prof_dump(tsd, false, filename, false); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/psset.c b/fluent-bit/lib/jemalloc-5.3.0/src/psset.c deleted file mode 100644 index 9a8f054f..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/psset.c +++ /dev/null @@ -1,385 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/psset.h" - -#include "jemalloc/internal/fb.h" - -void -psset_init(psset_t *psset) { - for (unsigned i = 0; i < PSSET_NPSIZES; i++) { - hpdata_age_heap_new(&psset->pageslabs[i]); - } - fb_init(psset->pageslab_bitmap, PSSET_NPSIZES); - memset(&psset->merged_stats, 0, sizeof(psset->merged_stats)); - memset(&psset->stats, 0, sizeof(psset->stats)); - hpdata_empty_list_init(&psset->empty); - for (int i = 0; i < PSSET_NPURGE_LISTS; i++) { - hpdata_purge_list_init(&psset->to_purge[i]); - } - fb_init(psset->purge_bitmap, PSSET_NPURGE_LISTS); - hpdata_hugify_list_init(&psset->to_hugify); -} - -static void -psset_bin_stats_accum(psset_bin_stats_t *dst, psset_bin_stats_t *src) { - dst->npageslabs += src->npageslabs; - dst->nactive += src->nactive; - dst->ndirty += src->ndirty; -} - -void -psset_stats_accum(psset_stats_t *dst, psset_stats_t *src) { - psset_bin_stats_accum(&dst->full_slabs[0], &src->full_slabs[0]); - psset_bin_stats_accum(&dst->full_slabs[1], &src->full_slabs[1]); - psset_bin_stats_accum(&dst->empty_slabs[0], &src->empty_slabs[0]); - psset_bin_stats_accum(&dst->empty_slabs[1], &src->empty_slabs[1]); - for (pszind_t i = 0; i < PSSET_NPSIZES; i++) { - psset_bin_stats_accum(&dst->nonfull_slabs[i][0], - &src->nonfull_slabs[i][0]); - psset_bin_stats_accum(&dst->nonfull_slabs[i][1], - &src->nonfull_slabs[i][1]); - } -} - -/* - * The stats maintenance strategy is to remove a pageslab's contribution to the - * stats when we call psset_update_begin, and re-add it (to a potentially new - * bin) when we call psset_update_end. - */ -JEMALLOC_ALWAYS_INLINE void -psset_bin_stats_insert_remove(psset_t *psset, psset_bin_stats_t *binstats, - hpdata_t *ps, bool insert) { - size_t mul = insert ? (size_t)1 : (size_t)-1; - size_t huge_idx = (size_t)hpdata_huge_get(ps); - - binstats[huge_idx].npageslabs += mul * 1; - binstats[huge_idx].nactive += mul * hpdata_nactive_get(ps); - binstats[huge_idx].ndirty += mul * hpdata_ndirty_get(ps); - - psset->merged_stats.npageslabs += mul * 1; - psset->merged_stats.nactive += mul * hpdata_nactive_get(ps); - psset->merged_stats.ndirty += mul * hpdata_ndirty_get(ps); - - if (config_debug) { - psset_bin_stats_t check_stats = {0}; - for (size_t huge = 0; huge <= 1; huge++) { - psset_bin_stats_accum(&check_stats, - &psset->stats.full_slabs[huge]); - psset_bin_stats_accum(&check_stats, - &psset->stats.empty_slabs[huge]); - for (pszind_t pind = 0; pind < PSSET_NPSIZES; pind++) { - psset_bin_stats_accum(&check_stats, - &psset->stats.nonfull_slabs[pind][huge]); - } - } - assert(psset->merged_stats.npageslabs - == check_stats.npageslabs); - assert(psset->merged_stats.nactive == check_stats.nactive); - assert(psset->merged_stats.ndirty == check_stats.ndirty); - } -} - -static void -psset_bin_stats_insert(psset_t *psset, psset_bin_stats_t *binstats, - hpdata_t *ps) { - psset_bin_stats_insert_remove(psset, binstats, ps, true); -} - -static void -psset_bin_stats_remove(psset_t *psset, psset_bin_stats_t *binstats, - hpdata_t *ps) { - psset_bin_stats_insert_remove(psset, binstats, ps, false); -} - -static void -psset_hpdata_heap_remove(psset_t *psset, pszind_t pind, hpdata_t *ps) { - hpdata_age_heap_remove(&psset->pageslabs[pind], ps); - if (hpdata_age_heap_empty(&psset->pageslabs[pind])) { - fb_unset(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind); - } -} - -static void -psset_hpdata_heap_insert(psset_t *psset, pszind_t pind, hpdata_t *ps) { - if (hpdata_age_heap_empty(&psset->pageslabs[pind])) { - fb_set(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind); - } - hpdata_age_heap_insert(&psset->pageslabs[pind], ps); -} - -static void -psset_stats_insert(psset_t* psset, hpdata_t *ps) { - if (hpdata_empty(ps)) { - psset_bin_stats_insert(psset, psset->stats.empty_slabs, ps); - } else if (hpdata_full(ps)) { - psset_bin_stats_insert(psset, psset->stats.full_slabs, ps); - } else { - size_t longest_free_range = hpdata_longest_free_range_get(ps); - - pszind_t pind = sz_psz2ind(sz_psz_quantize_floor( - longest_free_range << LG_PAGE)); - assert(pind < PSSET_NPSIZES); - - psset_bin_stats_insert(psset, psset->stats.nonfull_slabs[pind], - ps); - } -} - -static void -psset_stats_remove(psset_t *psset, hpdata_t *ps) { - if (hpdata_empty(ps)) { - psset_bin_stats_remove(psset, psset->stats.empty_slabs, ps); - } else if (hpdata_full(ps)) { - psset_bin_stats_remove(psset, psset->stats.full_slabs, ps); - } else { - size_t longest_free_range = hpdata_longest_free_range_get(ps); - - pszind_t pind = sz_psz2ind(sz_psz_quantize_floor( - longest_free_range << LG_PAGE)); - assert(pind < PSSET_NPSIZES); - - psset_bin_stats_remove(psset, psset->stats.nonfull_slabs[pind], - ps); - } -} - -/* - * Put ps into some container so that it can be found during future allocation - * requests. - */ -static void -psset_alloc_container_insert(psset_t *psset, hpdata_t *ps) { - assert(!hpdata_in_psset_alloc_container_get(ps)); - hpdata_in_psset_alloc_container_set(ps, true); - if (hpdata_empty(ps)) { - /* - * This prepend, paired with popping the head in psset_fit, - * means we implement LIFO ordering for the empty slabs set, - * which seems reasonable. - */ - hpdata_empty_list_prepend(&psset->empty, ps); - } else if (hpdata_full(ps)) { - /* - * We don't need to keep track of the full slabs; we're never - * going to return them from a psset_pick_alloc call. - */ - } else { - size_t longest_free_range = hpdata_longest_free_range_get(ps); - - pszind_t pind = sz_psz2ind(sz_psz_quantize_floor( - longest_free_range << LG_PAGE)); - assert(pind < PSSET_NPSIZES); - - psset_hpdata_heap_insert(psset, pind, ps); - } -} - -/* Remove ps from those collections. */ -static void -psset_alloc_container_remove(psset_t *psset, hpdata_t *ps) { - assert(hpdata_in_psset_alloc_container_get(ps)); - hpdata_in_psset_alloc_container_set(ps, false); - - if (hpdata_empty(ps)) { - hpdata_empty_list_remove(&psset->empty, ps); - } else if (hpdata_full(ps)) { - /* Same as above -- do nothing in this case. */ - } else { - size_t longest_free_range = hpdata_longest_free_range_get(ps); - - pszind_t pind = sz_psz2ind(sz_psz_quantize_floor( - longest_free_range << LG_PAGE)); - assert(pind < PSSET_NPSIZES); - - psset_hpdata_heap_remove(psset, pind, ps); - } -} - -static size_t -psset_purge_list_ind(hpdata_t *ps) { - size_t ndirty = hpdata_ndirty_get(ps); - /* Shouldn't have something with no dirty pages purgeable. */ - assert(ndirty > 0); - /* - * Higher indices correspond to lists we'd like to purge earlier; make - * the two highest indices correspond to empty lists, which we attempt - * to purge before purging any non-empty list. This has two advantages: - * - Empty page slabs are the least likely to get reused (we'll only - * pick them for an allocation if we have no other choice). - * - Empty page slabs can purge every dirty page they contain in a - * single call, which is not usually the case. - * - * We purge hugeified empty slabs before nonhugeified ones, on the basis - * that they are fully dirty, while nonhugified slabs might not be, so - * we free up more pages more easily. - */ - if (hpdata_nactive_get(ps) == 0) { - if (hpdata_huge_get(ps)) { - return PSSET_NPURGE_LISTS - 1; - } else { - return PSSET_NPURGE_LISTS - 2; - } - } - - pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(ndirty << LG_PAGE)); - /* - * For non-empty slabs, we may reuse them again. Prefer purging - * non-hugeified slabs before hugeified ones then, among pages of - * similar dirtiness. We still get some benefit from the hugification. - */ - return (size_t)pind * 2 + (hpdata_huge_get(ps) ? 0 : 1); -} - -static void -psset_maybe_remove_purge_list(psset_t *psset, hpdata_t *ps) { - /* - * Remove the hpdata from its purge list (if it's in one). Even if it's - * going to stay in the same one, by appending it during - * psset_update_end, we move it to the end of its queue, so that we - * purge LRU within a given dirtiness bucket. - */ - if (hpdata_purge_allowed_get(ps)) { - size_t ind = psset_purge_list_ind(ps); - hpdata_purge_list_t *purge_list = &psset->to_purge[ind]; - hpdata_purge_list_remove(purge_list, ps); - if (hpdata_purge_list_empty(purge_list)) { - fb_unset(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind); - } - } -} - -static void -psset_maybe_insert_purge_list(psset_t *psset, hpdata_t *ps) { - if (hpdata_purge_allowed_get(ps)) { - size_t ind = psset_purge_list_ind(ps); - hpdata_purge_list_t *purge_list = &psset->to_purge[ind]; - if (hpdata_purge_list_empty(purge_list)) { - fb_set(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind); - } - hpdata_purge_list_append(purge_list, ps); - } - -} - -void -psset_update_begin(psset_t *psset, hpdata_t *ps) { - hpdata_assert_consistent(ps); - assert(hpdata_in_psset_get(ps)); - hpdata_updating_set(ps, true); - psset_stats_remove(psset, ps); - if (hpdata_in_psset_alloc_container_get(ps)) { - /* - * Some metadata updates can break alloc container invariants - * (e.g. the longest free range determines the hpdata_heap_t the - * pageslab lives in). - */ - assert(hpdata_alloc_allowed_get(ps)); - psset_alloc_container_remove(psset, ps); - } - psset_maybe_remove_purge_list(psset, ps); - /* - * We don't update presence in the hugify list; we try to keep it FIFO, - * even in the presence of other metadata updates. We'll update - * presence at the end of the metadata update if necessary. - */ -} - -void -psset_update_end(psset_t *psset, hpdata_t *ps) { - assert(hpdata_in_psset_get(ps)); - hpdata_updating_set(ps, false); - psset_stats_insert(psset, ps); - - /* - * The update begin should have removed ps from whatever alloc container - * it was in. - */ - assert(!hpdata_in_psset_alloc_container_get(ps)); - if (hpdata_alloc_allowed_get(ps)) { - psset_alloc_container_insert(psset, ps); - } - psset_maybe_insert_purge_list(psset, ps); - - if (hpdata_hugify_allowed_get(ps) - && !hpdata_in_psset_hugify_container_get(ps)) { - hpdata_in_psset_hugify_container_set(ps, true); - hpdata_hugify_list_append(&psset->to_hugify, ps); - } else if (!hpdata_hugify_allowed_get(ps) - && hpdata_in_psset_hugify_container_get(ps)) { - hpdata_in_psset_hugify_container_set(ps, false); - hpdata_hugify_list_remove(&psset->to_hugify, ps); - } - hpdata_assert_consistent(ps); -} - -hpdata_t * -psset_pick_alloc(psset_t *psset, size_t size) { - assert((size & PAGE_MASK) == 0); - assert(size <= HUGEPAGE); - - pszind_t min_pind = sz_psz2ind(sz_psz_quantize_ceil(size)); - pszind_t pind = (pszind_t)fb_ffs(psset->pageslab_bitmap, PSSET_NPSIZES, - (size_t)min_pind); - if (pind == PSSET_NPSIZES) { - return hpdata_empty_list_first(&psset->empty); - } - hpdata_t *ps = hpdata_age_heap_first(&psset->pageslabs[pind]); - if (ps == NULL) { - return NULL; - } - - hpdata_assert_consistent(ps); - - return ps; -} - -hpdata_t * -psset_pick_purge(psset_t *psset) { - ssize_t ind_ssz = fb_fls(psset->purge_bitmap, PSSET_NPURGE_LISTS, - PSSET_NPURGE_LISTS - 1); - if (ind_ssz < 0) { - return NULL; - } - pszind_t ind = (pszind_t)ind_ssz; - assert(ind < PSSET_NPURGE_LISTS); - hpdata_t *ps = hpdata_purge_list_first(&psset->to_purge[ind]); - assert(ps != NULL); - return ps; -} - -hpdata_t * -psset_pick_hugify(psset_t *psset) { - return hpdata_hugify_list_first(&psset->to_hugify); -} - -void -psset_insert(psset_t *psset, hpdata_t *ps) { - hpdata_in_psset_set(ps, true); - - psset_stats_insert(psset, ps); - if (hpdata_alloc_allowed_get(ps)) { - psset_alloc_container_insert(psset, ps); - } - psset_maybe_insert_purge_list(psset, ps); - - if (hpdata_hugify_allowed_get(ps)) { - hpdata_in_psset_hugify_container_set(ps, true); - hpdata_hugify_list_append(&psset->to_hugify, ps); - } -} - -void -psset_remove(psset_t *psset, hpdata_t *ps) { - hpdata_in_psset_set(ps, false); - - psset_stats_remove(psset, ps); - if (hpdata_in_psset_alloc_container_get(ps)) { - psset_alloc_container_remove(psset, ps); - } - psset_maybe_remove_purge_list(psset, ps); - if (hpdata_in_psset_hugify_container_get(ps)) { - hpdata_in_psset_hugify_container_set(ps, false); - hpdata_hugify_list_remove(&psset->to_hugify, ps); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/rtree.c b/fluent-bit/lib/jemalloc-5.3.0/src/rtree.c deleted file mode 100644 index 6496b5af..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/rtree.c +++ /dev/null @@ -1,261 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/mutex.h" - -/* - * Only the most significant bits of keys passed to rtree_{read,write}() are - * used. - */ -bool -rtree_new(rtree_t *rtree, base_t *base, bool zeroed) { -#ifdef JEMALLOC_JET - if (!zeroed) { - memset(rtree, 0, sizeof(rtree_t)); /* Clear root. */ - } -#else - assert(zeroed); -#endif - rtree->base = base; - - if (malloc_mutex_init(&rtree->init_lock, "rtree", WITNESS_RANK_RTREE, - malloc_mutex_rank_exclusive)) { - return true; - } - - return false; -} - -static rtree_node_elm_t * -rtree_node_alloc(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) { - return (rtree_node_elm_t *)base_alloc(tsdn, rtree->base, - nelms * sizeof(rtree_node_elm_t), CACHELINE); -} - -static rtree_leaf_elm_t * -rtree_leaf_alloc(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) { - return (rtree_leaf_elm_t *)base_alloc(tsdn, rtree->base, - nelms * sizeof(rtree_leaf_elm_t), CACHELINE); -} - -static rtree_node_elm_t * -rtree_node_init(tsdn_t *tsdn, rtree_t *rtree, unsigned level, - atomic_p_t *elmp) { - malloc_mutex_lock(tsdn, &rtree->init_lock); - /* - * If *elmp is non-null, then it was initialized with the init lock - * held, so we can get by with 'relaxed' here. - */ - rtree_node_elm_t *node = atomic_load_p(elmp, ATOMIC_RELAXED); - if (node == NULL) { - node = rtree_node_alloc(tsdn, rtree, ZU(1) << - rtree_levels[level].bits); - if (node == NULL) { - malloc_mutex_unlock(tsdn, &rtree->init_lock); - return NULL; - } - /* - * Even though we hold the lock, a later reader might not; we - * need release semantics. - */ - atomic_store_p(elmp, node, ATOMIC_RELEASE); - } - malloc_mutex_unlock(tsdn, &rtree->init_lock); - - return node; -} - -static rtree_leaf_elm_t * -rtree_leaf_init(tsdn_t *tsdn, rtree_t *rtree, atomic_p_t *elmp) { - malloc_mutex_lock(tsdn, &rtree->init_lock); - /* - * If *elmp is non-null, then it was initialized with the init lock - * held, so we can get by with 'relaxed' here. - */ - rtree_leaf_elm_t *leaf = atomic_load_p(elmp, ATOMIC_RELAXED); - if (leaf == NULL) { - leaf = rtree_leaf_alloc(tsdn, rtree, ZU(1) << - rtree_levels[RTREE_HEIGHT-1].bits); - if (leaf == NULL) { - malloc_mutex_unlock(tsdn, &rtree->init_lock); - return NULL; - } - /* - * Even though we hold the lock, a later reader might not; we - * need release semantics. - */ - atomic_store_p(elmp, leaf, ATOMIC_RELEASE); - } - malloc_mutex_unlock(tsdn, &rtree->init_lock); - - return leaf; -} - -static bool -rtree_node_valid(rtree_node_elm_t *node) { - return ((uintptr_t)node != (uintptr_t)0); -} - -static bool -rtree_leaf_valid(rtree_leaf_elm_t *leaf) { - return ((uintptr_t)leaf != (uintptr_t)0); -} - -static rtree_node_elm_t * -rtree_child_node_tryread(rtree_node_elm_t *elm, bool dependent) { - rtree_node_elm_t *node; - - if (dependent) { - node = (rtree_node_elm_t *)atomic_load_p(&elm->child, - ATOMIC_RELAXED); - } else { - node = (rtree_node_elm_t *)atomic_load_p(&elm->child, - ATOMIC_ACQUIRE); - } - - assert(!dependent || node != NULL); - return node; -} - -static rtree_node_elm_t * -rtree_child_node_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm, - unsigned level, bool dependent) { - rtree_node_elm_t *node; - - node = rtree_child_node_tryread(elm, dependent); - if (!dependent && unlikely(!rtree_node_valid(node))) { - node = rtree_node_init(tsdn, rtree, level + 1, &elm->child); - } - assert(!dependent || node != NULL); - return node; -} - -static rtree_leaf_elm_t * -rtree_child_leaf_tryread(rtree_node_elm_t *elm, bool dependent) { - rtree_leaf_elm_t *leaf; - - if (dependent) { - leaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child, - ATOMIC_RELAXED); - } else { - leaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child, - ATOMIC_ACQUIRE); - } - - assert(!dependent || leaf != NULL); - return leaf; -} - -static rtree_leaf_elm_t * -rtree_child_leaf_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm, - unsigned level, bool dependent) { - rtree_leaf_elm_t *leaf; - - leaf = rtree_child_leaf_tryread(elm, dependent); - if (!dependent && unlikely(!rtree_leaf_valid(leaf))) { - leaf = rtree_leaf_init(tsdn, rtree, &elm->child); - } - assert(!dependent || leaf != NULL); - return leaf; -} - -rtree_leaf_elm_t * -rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, - uintptr_t key, bool dependent, bool init_missing) { - rtree_node_elm_t *node; - rtree_leaf_elm_t *leaf; -#if RTREE_HEIGHT > 1 - node = rtree->root; -#else - leaf = rtree->root; -#endif - - if (config_debug) { - uintptr_t leafkey = rtree_leafkey(key); - for (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) { - assert(rtree_ctx->cache[i].leafkey != leafkey); - } - for (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) { - assert(rtree_ctx->l2_cache[i].leafkey != leafkey); - } - } - -#define RTREE_GET_CHILD(level) { \ - assert(level < RTREE_HEIGHT-1); \ - if (level != 0 && !dependent && \ - unlikely(!rtree_node_valid(node))) { \ - return NULL; \ - } \ - uintptr_t subkey = rtree_subkey(key, level); \ - if (level + 2 < RTREE_HEIGHT) { \ - node = init_missing ? \ - rtree_child_node_read(tsdn, rtree, \ - &node[subkey], level, dependent) : \ - rtree_child_node_tryread(&node[subkey], \ - dependent); \ - } else { \ - leaf = init_missing ? \ - rtree_child_leaf_read(tsdn, rtree, \ - &node[subkey], level, dependent) : \ - rtree_child_leaf_tryread(&node[subkey], \ - dependent); \ - } \ - } - /* - * Cache replacement upon hard lookup (i.e. L1 & L2 rtree cache miss): - * (1) evict last entry in L2 cache; (2) move the collision slot from L1 - * cache down to L2; and 3) fill L1. - */ -#define RTREE_GET_LEAF(level) { \ - assert(level == RTREE_HEIGHT-1); \ - if (!dependent && unlikely(!rtree_leaf_valid(leaf))) { \ - return NULL; \ - } \ - if (RTREE_CTX_NCACHE_L2 > 1) { \ - memmove(&rtree_ctx->l2_cache[1], \ - &rtree_ctx->l2_cache[0], \ - sizeof(rtree_ctx_cache_elm_t) * \ - (RTREE_CTX_NCACHE_L2 - 1)); \ - } \ - size_t slot = rtree_cache_direct_map(key); \ - rtree_ctx->l2_cache[0].leafkey = \ - rtree_ctx->cache[slot].leafkey; \ - rtree_ctx->l2_cache[0].leaf = \ - rtree_ctx->cache[slot].leaf; \ - uintptr_t leafkey = rtree_leafkey(key); \ - rtree_ctx->cache[slot].leafkey = leafkey; \ - rtree_ctx->cache[slot].leaf = leaf; \ - uintptr_t subkey = rtree_subkey(key, level); \ - return &leaf[subkey]; \ - } - if (RTREE_HEIGHT > 1) { - RTREE_GET_CHILD(0) - } - if (RTREE_HEIGHT > 2) { - RTREE_GET_CHILD(1) - } - if (RTREE_HEIGHT > 3) { - for (unsigned i = 2; i < RTREE_HEIGHT-1; i++) { - RTREE_GET_CHILD(i) - } - } - RTREE_GET_LEAF(RTREE_HEIGHT-1) -#undef RTREE_GET_CHILD -#undef RTREE_GET_LEAF - not_reached(); -} - -void -rtree_ctx_data_init(rtree_ctx_t *ctx) { - for (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) { - rtree_ctx_cache_elm_t *cache = &ctx->cache[i]; - cache->leafkey = RTREE_LEAFKEY_INVALID; - cache->leaf = NULL; - } - for (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) { - rtree_ctx_cache_elm_t *cache = &ctx->l2_cache[i]; - cache->leafkey = RTREE_LEAFKEY_INVALID; - cache->leaf = NULL; - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/safety_check.c b/fluent-bit/lib/jemalloc-5.3.0/src/safety_check.c deleted file mode 100644 index 209fdda9..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/safety_check.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -static safety_check_abort_hook_t safety_check_abort; - -void safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr, - size_t true_size, size_t input_size) { - char *src = current_dealloc ? "the current pointer being freed" : - "in thread cache, possibly from previous deallocations"; - - safety_check_fail("<jemalloc>: size mismatch detected (true size %zu " - "vs input size %zu), likely caused by application sized " - "deallocation bugs (source address: %p, %s). Suggest building with " - "--enable-debug or address sanitizer for debugging. Abort.\n", - true_size, input_size, ptr, src); -} - -void safety_check_set_abort(safety_check_abort_hook_t abort_fn) { - safety_check_abort = abort_fn; -} - -void safety_check_fail(const char *format, ...) { - char buf[MALLOC_PRINTF_BUFSIZE]; - - va_list ap; - va_start(ap, format); - malloc_vsnprintf(buf, MALLOC_PRINTF_BUFSIZE, format, ap); - va_end(ap); - - if (safety_check_abort == NULL) { - malloc_write(buf); - abort(); - } else { - safety_check_abort(buf); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/san.c b/fluent-bit/lib/jemalloc-5.3.0/src/san.c deleted file mode 100644 index 6e512911..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/san.c +++ /dev/null @@ -1,208 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/ehooks.h" -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/tsd.h" - -/* The sanitizer options. */ -size_t opt_san_guard_large = SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT; -size_t opt_san_guard_small = SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT; - -/* Aligned (-1 is off) ptrs will be junked & stashed on dealloc. */ -ssize_t opt_lg_san_uaf_align = SAN_LG_UAF_ALIGN_DEFAULT; - -/* - * Initialized in san_init(). When disabled, the mask is set to (uintptr_t)-1 - * to always fail the nonfast_align check. - */ -uintptr_t san_cache_bin_nonfast_mask = SAN_CACHE_BIN_NONFAST_MASK_DEFAULT; - -static inline void -san_find_guarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2, - uintptr_t *addr, size_t size, bool left, bool right) { - assert(!edata_guarded_get(edata)); - assert(size % PAGE == 0); - *addr = (uintptr_t)edata_base_get(edata); - if (left) { - *guard1 = *addr; - *addr += SAN_PAGE_GUARD; - } else { - *guard1 = 0; - } - - if (right) { - *guard2 = *addr + size; - } else { - *guard2 = 0; - } -} - -static inline void -san_find_unguarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2, - uintptr_t *addr, size_t size, bool left, bool right) { - assert(edata_guarded_get(edata)); - assert(size % PAGE == 0); - *addr = (uintptr_t)edata_base_get(edata); - if (right) { - *guard2 = *addr + size; - } else { - *guard2 = 0; - } - - if (left) { - *guard1 = *addr - SAN_PAGE_GUARD; - assert(*guard1 != 0); - *addr = *guard1; - } else { - *guard1 = 0; - } -} - -void -san_guard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, emap_t *emap, - bool left, bool right, bool remap) { - assert(left || right); - if (remap) { - emap_deregister_boundary(tsdn, emap, edata); - } - - size_t size_with_guards = edata_size_get(edata); - size_t usize = (left && right) - ? san_two_side_unguarded_sz(size_with_guards) - : san_one_side_unguarded_sz(size_with_guards); - - uintptr_t guard1, guard2, addr; - san_find_guarded_addr(edata, &guard1, &guard2, &addr, usize, left, - right); - - assert(edata_state_get(edata) == extent_state_active); - ehooks_guard(tsdn, ehooks, (void *)guard1, (void *)guard2); - - /* Update the guarded addr and usable size of the edata. */ - edata_size_set(edata, usize); - edata_addr_set(edata, (void *)addr); - edata_guarded_set(edata, true); - - if (remap) { - emap_register_boundary(tsdn, emap, edata, SC_NSIZES, - /* slab */ false); - } -} - -static void -san_unguard_pages_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - emap_t *emap, bool left, bool right, bool remap) { - assert(left || right); - /* Remove the inner boundary which no longer exists. */ - if (remap) { - assert(edata_state_get(edata) == extent_state_active); - emap_deregister_boundary(tsdn, emap, edata); - } else { - assert(edata_state_get(edata) == extent_state_retained); - } - - size_t size = edata_size_get(edata); - size_t size_with_guards = (left && right) - ? san_two_side_guarded_sz(size) - : san_one_side_guarded_sz(size); - - uintptr_t guard1, guard2, addr; - san_find_unguarded_addr(edata, &guard1, &guard2, &addr, size, left, - right); - - ehooks_unguard(tsdn, ehooks, (void *)guard1, (void *)guard2); - - /* Update the true addr and usable size of the edata. */ - edata_size_set(edata, size_with_guards); - edata_addr_set(edata, (void *)addr); - edata_guarded_set(edata, false); - - /* - * Then re-register the outer boundary including the guards, if - * requested. - */ - if (remap) { - emap_register_boundary(tsdn, emap, edata, SC_NSIZES, - /* slab */ false); - } -} - -void -san_unguard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - emap_t *emap, bool left, bool right) { - san_unguard_pages_impl(tsdn, ehooks, edata, emap, left, right, - /* remap */ true); -} - -void -san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, - emap_t *emap) { - emap_assert_not_mapped(tsdn, emap, edata); - /* - * We don't want to touch the emap of about to be destroyed extents, as - * they have been unmapped upon eviction from the retained ecache. Also, - * we unguard the extents to the right, because retained extents only - * own their right guard page per san_bump_alloc's logic. - */ - san_unguard_pages_impl(tsdn, ehooks, edata, emap, /* left */ false, - /* right */ true, /* remap */ false); -} - -static bool -san_stashed_corrupted(void *ptr, size_t size) { - if (san_junk_ptr_should_slow()) { - for (size_t i = 0; i < size; i++) { - if (((char *)ptr)[i] != (char)uaf_detect_junk) { - return true; - } - } - return false; - } - - void *first, *mid, *last; - san_junk_ptr_locations(ptr, size, &first, &mid, &last); - if (*(uintptr_t *)first != uaf_detect_junk || - *(uintptr_t *)mid != uaf_detect_junk || - *(uintptr_t *)last != uaf_detect_junk) { - return true; - } - - return false; -} - -void -san_check_stashed_ptrs(void **ptrs, size_t nstashed, size_t usize) { - /* - * Verify that the junked-filled & stashed pointers remain unchanged, to - * detect write-after-free. - */ - for (size_t n = 0; n < nstashed; n++) { - void *stashed = ptrs[n]; - assert(stashed != NULL); - assert(cache_bin_nonfast_aligned(stashed)); - if (unlikely(san_stashed_corrupted(stashed, usize))) { - safety_check_fail("<jemalloc>: Write-after-free " - "detected on deallocated pointer %p (size %zu).\n", - stashed, usize); - } - } -} - -void -tsd_san_init(tsd_t *tsd) { - *tsd_san_extents_until_guard_smallp_get(tsd) = opt_san_guard_small; - *tsd_san_extents_until_guard_largep_get(tsd) = opt_san_guard_large; -} - -void -san_init(ssize_t lg_san_uaf_align) { - assert(lg_san_uaf_align == -1 || lg_san_uaf_align >= LG_PAGE); - if (lg_san_uaf_align == -1) { - san_cache_bin_nonfast_mask = (uintptr_t)-1; - return; - } - - san_cache_bin_nonfast_mask = ((uintptr_t)1 << lg_san_uaf_align) - 1; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/san_bump.c b/fluent-bit/lib/jemalloc-5.3.0/src/san_bump.c deleted file mode 100644 index 88897455..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/san_bump.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/san_bump.h" -#include "jemalloc/internal/pac.h" -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/ehooks.h" -#include "jemalloc/internal/edata_cache.h" - -static bool -san_bump_grow_locked(tsdn_t *tsdn, san_bump_alloc_t *sba, pac_t *pac, - ehooks_t *ehooks, size_t size); - -edata_t * -san_bump_alloc(tsdn_t *tsdn, san_bump_alloc_t* sba, pac_t *pac, - ehooks_t *ehooks, size_t size, bool zero) { - assert(san_bump_enabled()); - - edata_t* to_destroy; - size_t guarded_size = san_one_side_guarded_sz(size); - - malloc_mutex_lock(tsdn, &sba->mtx); - - if (sba->curr_reg == NULL || - edata_size_get(sba->curr_reg) < guarded_size) { - /* - * If the current region can't accommodate the allocation, - * try replacing it with a larger one and destroy current if the - * replacement succeeds. - */ - to_destroy = sba->curr_reg; - bool err = san_bump_grow_locked(tsdn, sba, pac, ehooks, - guarded_size); - if (err) { - goto label_err; - } - } else { - to_destroy = NULL; - } - assert(guarded_size <= edata_size_get(sba->curr_reg)); - size_t trail_size = edata_size_get(sba->curr_reg) - guarded_size; - - edata_t* edata; - if (trail_size != 0) { - edata_t* curr_reg_trail = extent_split_wrapper(tsdn, pac, - ehooks, sba->curr_reg, guarded_size, trail_size, - /* holding_core_locks */ true); - if (curr_reg_trail == NULL) { - goto label_err; - } - edata = sba->curr_reg; - sba->curr_reg = curr_reg_trail; - } else { - edata = sba->curr_reg; - sba->curr_reg = NULL; - } - - malloc_mutex_unlock(tsdn, &sba->mtx); - - assert(!edata_guarded_get(edata)); - assert(sba->curr_reg == NULL || !edata_guarded_get(sba->curr_reg)); - assert(to_destroy == NULL || !edata_guarded_get(to_destroy)); - - if (to_destroy != NULL) { - extent_destroy_wrapper(tsdn, pac, ehooks, to_destroy); - } - - san_guard_pages(tsdn, ehooks, edata, pac->emap, /* left */ false, - /* right */ true, /* remap */ true); - - if (extent_commit_zero(tsdn, ehooks, edata, /* commit */ true, zero, - /* growing_retained */ false)) { - extent_record(tsdn, pac, ehooks, &pac->ecache_retained, - edata); - return NULL; - } - - if (config_prof) { - extent_gdump_add(tsdn, edata); - } - - return edata; -label_err: - malloc_mutex_unlock(tsdn, &sba->mtx); - return NULL; -} - -static bool -san_bump_grow_locked(tsdn_t *tsdn, san_bump_alloc_t *sba, pac_t *pac, - ehooks_t *ehooks, size_t size) { - malloc_mutex_assert_owner(tsdn, &sba->mtx); - - bool committed = false, zeroed = false; - size_t alloc_size = size > SBA_RETAINED_ALLOC_SIZE ? size : - SBA_RETAINED_ALLOC_SIZE; - assert((alloc_size & PAGE_MASK) == 0); - sba->curr_reg = extent_alloc_wrapper(tsdn, pac, ehooks, NULL, - alloc_size, PAGE, zeroed, &committed, - /* growing_retained */ true); - if (sba->curr_reg == NULL) { - return true; - } - return false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/sc.c b/fluent-bit/lib/jemalloc-5.3.0/src/sc.c deleted file mode 100644 index e4a94d89..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/sc.c +++ /dev/null @@ -1,306 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/bit_util.h" -#include "jemalloc/internal/bitmap.h" -#include "jemalloc/internal/pages.h" -#include "jemalloc/internal/sc.h" - -/* - * This module computes the size classes used to satisfy allocations. The logic - * here was ported more or less line-by-line from a shell script, and because of - * that is not the most idiomatic C. Eventually we should fix this, but for now - * at least the damage is compartmentalized to this file. - */ - -size_t -reg_size_compute(int lg_base, int lg_delta, int ndelta) { - return (ZU(1) << lg_base) + (ZU(ndelta) << lg_delta); -} - -/* Returns the number of pages in the slab. */ -static int -slab_size(int lg_page, int lg_base, int lg_delta, int ndelta) { - size_t page = (ZU(1) << lg_page); - size_t reg_size = reg_size_compute(lg_base, lg_delta, ndelta); - - size_t try_slab_size = page; - size_t try_nregs = try_slab_size / reg_size; - size_t perfect_slab_size = 0; - bool perfect = false; - /* - * This loop continues until we find the least common multiple of the - * page size and size class size. Size classes are all of the form - * base + ndelta * delta == (ndelta + base/ndelta) * delta, which is - * (ndelta + ngroup) * delta. The way we choose slabbing strategies - * means that delta is at most the page size and ndelta < ngroup. So - * the loop executes for at most 2 * ngroup - 1 iterations, which is - * also the bound on the number of pages in a slab chosen by default. - * With the current default settings, this is at most 7. - */ - while (!perfect) { - perfect_slab_size = try_slab_size; - size_t perfect_nregs = try_nregs; - try_slab_size += page; - try_nregs = try_slab_size / reg_size; - if (perfect_slab_size == perfect_nregs * reg_size) { - perfect = true; - } - } - return (int)(perfect_slab_size / page); -} - -static void -size_class( - /* Output. */ - sc_t *sc, - /* Configuration decisions. */ - int lg_max_lookup, int lg_page, int lg_ngroup, - /* Inputs specific to the size class. */ - int index, int lg_base, int lg_delta, int ndelta) { - sc->index = index; - sc->lg_base = lg_base; - sc->lg_delta = lg_delta; - sc->ndelta = ndelta; - size_t size = reg_size_compute(lg_base, lg_delta, ndelta); - sc->psz = (size % (ZU(1) << lg_page) == 0); - if (index == 0) { - assert(!sc->psz); - } - if (size < (ZU(1) << (lg_page + lg_ngroup))) { - sc->bin = true; - sc->pgs = slab_size(lg_page, lg_base, lg_delta, ndelta); - } else { - sc->bin = false; - sc->pgs = 0; - } - if (size <= (ZU(1) << lg_max_lookup)) { - sc->lg_delta_lookup = lg_delta; - } else { - sc->lg_delta_lookup = 0; - } -} - -static void -size_classes( - /* Output. */ - sc_data_t *sc_data, - /* Determined by the system. */ - size_t lg_ptr_size, int lg_quantum, - /* Configuration decisions. */ - int lg_tiny_min, int lg_max_lookup, int lg_page, int lg_ngroup) { - int ptr_bits = (1 << lg_ptr_size) * 8; - int ngroup = (1 << lg_ngroup); - int ntiny = 0; - int nlbins = 0; - int lg_tiny_maxclass = (unsigned)-1; - int nbins = 0; - int npsizes = 0; - - int index = 0; - - int ndelta = 0; - int lg_base = lg_tiny_min; - int lg_delta = lg_base; - - /* Outputs that we update as we go. */ - size_t lookup_maxclass = 0; - size_t small_maxclass = 0; - int lg_large_minclass = 0; - size_t large_maxclass = 0; - - /* Tiny size classes. */ - while (lg_base < lg_quantum) { - sc_t *sc = &sc_data->sc[index]; - size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index, - lg_base, lg_delta, ndelta); - if (sc->lg_delta_lookup != 0) { - nlbins = index + 1; - } - if (sc->psz) { - npsizes++; - } - if (sc->bin) { - nbins++; - } - ntiny++; - /* Final written value is correct. */ - lg_tiny_maxclass = lg_base; - index++; - lg_delta = lg_base; - lg_base++; - } - - /* First non-tiny (pseudo) group. */ - if (ntiny != 0) { - sc_t *sc = &sc_data->sc[index]; - /* - * See the note in sc.h; the first non-tiny size class has an - * unusual encoding. - */ - lg_base--; - ndelta = 1; - size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index, - lg_base, lg_delta, ndelta); - index++; - lg_base++; - lg_delta++; - if (sc->psz) { - npsizes++; - } - if (sc->bin) { - nbins++; - } - } - while (ndelta < ngroup) { - sc_t *sc = &sc_data->sc[index]; - size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index, - lg_base, lg_delta, ndelta); - index++; - ndelta++; - if (sc->psz) { - npsizes++; - } - if (sc->bin) { - nbins++; - } - } - - /* All remaining groups. */ - lg_base = lg_base + lg_ngroup; - while (lg_base < ptr_bits - 1) { - ndelta = 1; - int ndelta_limit; - if (lg_base == ptr_bits - 2) { - ndelta_limit = ngroup - 1; - } else { - ndelta_limit = ngroup; - } - while (ndelta <= ndelta_limit) { - sc_t *sc = &sc_data->sc[index]; - size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index, - lg_base, lg_delta, ndelta); - if (sc->lg_delta_lookup != 0) { - nlbins = index + 1; - /* Final written value is correct. */ - lookup_maxclass = (ZU(1) << lg_base) - + (ZU(ndelta) << lg_delta); - } - if (sc->psz) { - npsizes++; - } - if (sc->bin) { - nbins++; - /* Final written value is correct. */ - small_maxclass = (ZU(1) << lg_base) - + (ZU(ndelta) << lg_delta); - if (lg_ngroup > 0) { - lg_large_minclass = lg_base + 1; - } else { - lg_large_minclass = lg_base + 2; - } - } - large_maxclass = (ZU(1) << lg_base) - + (ZU(ndelta) << lg_delta); - index++; - ndelta++; - } - lg_base++; - lg_delta++; - } - /* Additional outputs. */ - int nsizes = index; - unsigned lg_ceil_nsizes = lg_ceil(nsizes); - - /* Fill in the output data. */ - sc_data->ntiny = ntiny; - sc_data->nlbins = nlbins; - sc_data->nbins = nbins; - sc_data->nsizes = nsizes; - sc_data->lg_ceil_nsizes = lg_ceil_nsizes; - sc_data->npsizes = npsizes; - sc_data->lg_tiny_maxclass = lg_tiny_maxclass; - sc_data->lookup_maxclass = lookup_maxclass; - sc_data->small_maxclass = small_maxclass; - sc_data->lg_large_minclass = lg_large_minclass; - sc_data->large_minclass = (ZU(1) << lg_large_minclass); - sc_data->large_maxclass = large_maxclass; - - /* - * We compute these values in two ways: - * - Incrementally, as above. - * - In macros, in sc.h. - * The computation is easier when done incrementally, but putting it in - * a constant makes it available to the fast paths without having to - * touch the extra global cacheline. We assert, however, that the two - * computations are equivalent. - */ - assert(sc_data->npsizes == SC_NPSIZES); - assert(sc_data->lg_tiny_maxclass == SC_LG_TINY_MAXCLASS); - assert(sc_data->small_maxclass == SC_SMALL_MAXCLASS); - assert(sc_data->large_minclass == SC_LARGE_MINCLASS); - assert(sc_data->lg_large_minclass == SC_LG_LARGE_MINCLASS); - assert(sc_data->large_maxclass == SC_LARGE_MAXCLASS); - - /* - * In the allocation fastpath, we want to assume that we can - * unconditionally subtract the requested allocation size from - * a ssize_t, and detect passing through 0 correctly. This - * results in optimal generated code. For this to work, the - * maximum allocation size must be less than SSIZE_MAX. - */ - assert(SC_LARGE_MAXCLASS < SSIZE_MAX); -} - -void -sc_data_init(sc_data_t *sc_data) { - size_classes(sc_data, LG_SIZEOF_PTR, LG_QUANTUM, SC_LG_TINY_MIN, - SC_LG_MAX_LOOKUP, LG_PAGE, SC_LG_NGROUP); - - sc_data->initialized = true; -} - -static void -sc_data_update_sc_slab_size(sc_t *sc, size_t reg_size, size_t pgs_guess) { - size_t min_pgs = reg_size / PAGE; - if (reg_size % PAGE != 0) { - min_pgs++; - } - /* - * BITMAP_MAXBITS is actually determined by putting the smallest - * possible size-class on one page, so this can never be 0. - */ - size_t max_pgs = BITMAP_MAXBITS * reg_size / PAGE; - - assert(min_pgs <= max_pgs); - assert(min_pgs > 0); - assert(max_pgs >= 1); - if (pgs_guess < min_pgs) { - sc->pgs = (int)min_pgs; - } else if (pgs_guess > max_pgs) { - sc->pgs = (int)max_pgs; - } else { - sc->pgs = (int)pgs_guess; - } -} - -void -sc_data_update_slab_size(sc_data_t *data, size_t begin, size_t end, int pgs) { - assert(data->initialized); - for (int i = 0; i < data->nsizes; i++) { - sc_t *sc = &data->sc[i]; - if (!sc->bin) { - break; - } - size_t reg_size = reg_size_compute(sc->lg_base, sc->lg_delta, - sc->ndelta); - if (begin <= reg_size && reg_size <= end) { - sc_data_update_sc_slab_size(sc, reg_size, pgs); - } - } -} - -void -sc_boot(sc_data_t *data) { - sc_data_init(data); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/sec.c b/fluent-bit/lib/jemalloc-5.3.0/src/sec.c deleted file mode 100644 index df675590..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/sec.c +++ /dev/null @@ -1,422 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/sec.h" - -static edata_t *sec_alloc(tsdn_t *tsdn, pai_t *self, size_t size, - size_t alignment, bool zero, bool guarded, bool frequent_reuse, - bool *deferred_work_generated); -static bool sec_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool zero, bool *deferred_work_generated); -static bool sec_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata, - size_t old_size, size_t new_size, bool *deferred_work_generated); -static void sec_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata, - bool *deferred_work_generated); - -static void -sec_bin_init(sec_bin_t *bin) { - bin->being_batch_filled = false; - bin->bytes_cur = 0; - edata_list_active_init(&bin->freelist); -} - -bool -sec_init(tsdn_t *tsdn, sec_t *sec, base_t *base, pai_t *fallback, - const sec_opts_t *opts) { - assert(opts->max_alloc >= PAGE); - - size_t max_alloc = PAGE_FLOOR(opts->max_alloc); - pszind_t npsizes = sz_psz2ind(max_alloc) + 1; - - size_t sz_shards = opts->nshards * sizeof(sec_shard_t); - size_t sz_bins = opts->nshards * (size_t)npsizes * sizeof(sec_bin_t); - size_t sz_alloc = sz_shards + sz_bins; - void *dynalloc = base_alloc(tsdn, base, sz_alloc, CACHELINE); - if (dynalloc == NULL) { - return true; - } - sec_shard_t *shard_cur = (sec_shard_t *)dynalloc; - sec->shards = shard_cur; - sec_bin_t *bin_cur = (sec_bin_t *)&shard_cur[opts->nshards]; - /* Just for asserts, below. */ - sec_bin_t *bin_start = bin_cur; - - for (size_t i = 0; i < opts->nshards; i++) { - sec_shard_t *shard = shard_cur; - shard_cur++; - bool err = malloc_mutex_init(&shard->mtx, "sec_shard", - WITNESS_RANK_SEC_SHARD, malloc_mutex_rank_exclusive); - if (err) { - return true; - } - shard->enabled = true; - shard->bins = bin_cur; - for (pszind_t j = 0; j < npsizes; j++) { - sec_bin_init(&shard->bins[j]); - bin_cur++; - } - shard->bytes_cur = 0; - shard->to_flush_next = 0; - } - /* - * Should have exactly matched the bin_start to the first unused byte - * after the shards. - */ - assert((void *)shard_cur == (void *)bin_start); - /* And the last bin to use up the last bytes of the allocation. */ - assert((char *)bin_cur == ((char *)dynalloc + sz_alloc)); - sec->fallback = fallback; - - - sec->opts = *opts; - sec->npsizes = npsizes; - - /* - * Initialize these last so that an improper use of an SEC whose - * initialization failed will segfault in an easy-to-spot way. - */ - sec->pai.alloc = &sec_alloc; - sec->pai.alloc_batch = &pai_alloc_batch_default; - sec->pai.expand = &sec_expand; - sec->pai.shrink = &sec_shrink; - sec->pai.dalloc = &sec_dalloc; - sec->pai.dalloc_batch = &pai_dalloc_batch_default; - - return false; -} - -static sec_shard_t * -sec_shard_pick(tsdn_t *tsdn, sec_t *sec) { - /* - * Eventually, we should implement affinity, tracking source shard using - * the edata_t's newly freed up fields. For now, just randomly - * distribute across all shards. - */ - if (tsdn_null(tsdn)) { - return &sec->shards[0]; - } - tsd_t *tsd = tsdn_tsd(tsdn); - uint8_t *idxp = tsd_sec_shardp_get(tsd); - if (*idxp == (uint8_t)-1) { - /* - * First use; initialize using the trick from Daniel Lemire's - * "A fast alternative to the modulo reduction. Use a 64 bit - * number to store 32 bits, since we'll deliberately overflow - * when we multiply by the number of shards. - */ - uint64_t rand32 = prng_lg_range_u64(tsd_prng_statep_get(tsd), 32); - uint32_t idx = - (uint32_t)((rand32 * (uint64_t)sec->opts.nshards) >> 32); - assert(idx < (uint32_t)sec->opts.nshards); - *idxp = (uint8_t)idx; - } - return &sec->shards[*idxp]; -} - -/* - * Perhaps surprisingly, this can be called on the alloc pathways; if we hit an - * empty cache, we'll try to fill it, which can push the shard over it's limit. - */ -static void -sec_flush_some_and_unlock(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - edata_list_active_t to_flush; - edata_list_active_init(&to_flush); - while (shard->bytes_cur > sec->opts.bytes_after_flush) { - /* Pick a victim. */ - sec_bin_t *bin = &shard->bins[shard->to_flush_next]; - - /* Update our victim-picking state. */ - shard->to_flush_next++; - if (shard->to_flush_next == sec->npsizes) { - shard->to_flush_next = 0; - } - - assert(shard->bytes_cur >= bin->bytes_cur); - if (bin->bytes_cur != 0) { - shard->bytes_cur -= bin->bytes_cur; - bin->bytes_cur = 0; - edata_list_active_concat(&to_flush, &bin->freelist); - } - /* - * Either bin->bytes_cur was 0, in which case we didn't touch - * the bin list but it should be empty anyways (or else we - * missed a bytes_cur update on a list modification), or it - * *was* 0 and we emptied it ourselves. Either way, it should - * be empty now. - */ - assert(edata_list_active_empty(&bin->freelist)); - } - - malloc_mutex_unlock(tsdn, &shard->mtx); - bool deferred_work_generated = false; - pai_dalloc_batch(tsdn, sec->fallback, &to_flush, - &deferred_work_generated); -} - -static edata_t * -sec_shard_alloc_locked(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard, - sec_bin_t *bin) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - if (!shard->enabled) { - return NULL; - } - edata_t *edata = edata_list_active_first(&bin->freelist); - if (edata != NULL) { - edata_list_active_remove(&bin->freelist, edata); - assert(edata_size_get(edata) <= bin->bytes_cur); - bin->bytes_cur -= edata_size_get(edata); - assert(edata_size_get(edata) <= shard->bytes_cur); - shard->bytes_cur -= edata_size_get(edata); - } - return edata; -} - -static edata_t * -sec_batch_fill_and_alloc(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard, - sec_bin_t *bin, size_t size) { - malloc_mutex_assert_not_owner(tsdn, &shard->mtx); - - edata_list_active_t result; - edata_list_active_init(&result); - bool deferred_work_generated = false; - size_t nalloc = pai_alloc_batch(tsdn, sec->fallback, size, - 1 + sec->opts.batch_fill_extra, &result, &deferred_work_generated); - - edata_t *ret = edata_list_active_first(&result); - if (ret != NULL) { - edata_list_active_remove(&result, ret); - } - - malloc_mutex_lock(tsdn, &shard->mtx); - bin->being_batch_filled = false; - /* - * Handle the easy case first: nothing to cache. Note that this can - * only happen in case of OOM, since sec_alloc checks the expected - * number of allocs, and doesn't bother going down the batch_fill - * pathway if there won't be anything left to cache. So to be in this - * code path, we must have asked for > 1 alloc, but only gotten 1 back. - */ - if (nalloc <= 1) { - malloc_mutex_unlock(tsdn, &shard->mtx); - return ret; - } - - size_t new_cached_bytes = (nalloc - 1) * size; - - edata_list_active_concat(&bin->freelist, &result); - bin->bytes_cur += new_cached_bytes; - shard->bytes_cur += new_cached_bytes; - - if (shard->bytes_cur > sec->opts.max_bytes) { - sec_flush_some_and_unlock(tsdn, sec, shard); - } else { - malloc_mutex_unlock(tsdn, &shard->mtx); - } - - return ret; -} - -static edata_t * -sec_alloc(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment, bool zero, - bool guarded, bool frequent_reuse, bool *deferred_work_generated) { - assert((size & PAGE_MASK) == 0); - assert(!guarded); - - sec_t *sec = (sec_t *)self; - - if (zero || alignment > PAGE || sec->opts.nshards == 0 - || size > sec->opts.max_alloc) { - return pai_alloc(tsdn, sec->fallback, size, alignment, zero, - /* guarded */ false, frequent_reuse, - deferred_work_generated); - } - pszind_t pszind = sz_psz2ind(size); - assert(pszind < sec->npsizes); - - sec_shard_t *shard = sec_shard_pick(tsdn, sec); - sec_bin_t *bin = &shard->bins[pszind]; - bool do_batch_fill = false; - - malloc_mutex_lock(tsdn, &shard->mtx); - edata_t *edata = sec_shard_alloc_locked(tsdn, sec, shard, bin); - if (edata == NULL) { - if (!bin->being_batch_filled - && sec->opts.batch_fill_extra > 0) { - bin->being_batch_filled = true; - do_batch_fill = true; - } - } - malloc_mutex_unlock(tsdn, &shard->mtx); - if (edata == NULL) { - if (do_batch_fill) { - edata = sec_batch_fill_and_alloc(tsdn, sec, shard, bin, - size); - } else { - edata = pai_alloc(tsdn, sec->fallback, size, alignment, - zero, /* guarded */ false, frequent_reuse, - deferred_work_generated); - } - } - return edata; -} - -static bool -sec_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size, - size_t new_size, bool zero, bool *deferred_work_generated) { - sec_t *sec = (sec_t *)self; - return pai_expand(tsdn, sec->fallback, edata, old_size, new_size, zero, - deferred_work_generated); -} - -static bool -sec_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size, - size_t new_size, bool *deferred_work_generated) { - sec_t *sec = (sec_t *)self; - return pai_shrink(tsdn, sec->fallback, edata, old_size, new_size, - deferred_work_generated); -} - -static void -sec_flush_all_locked(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - shard->bytes_cur = 0; - edata_list_active_t to_flush; - edata_list_active_init(&to_flush); - for (pszind_t i = 0; i < sec->npsizes; i++) { - sec_bin_t *bin = &shard->bins[i]; - bin->bytes_cur = 0; - edata_list_active_concat(&to_flush, &bin->freelist); - } - - /* - * Ordinarily we would try to avoid doing the batch deallocation while - * holding the shard mutex, but the flush_all pathways only happen when - * we're disabling the HPA or resetting the arena, both of which are - * rare pathways. - */ - bool deferred_work_generated = false; - pai_dalloc_batch(tsdn, sec->fallback, &to_flush, - &deferred_work_generated); -} - -static void -sec_shard_dalloc_and_unlock(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard, - edata_t *edata) { - malloc_mutex_assert_owner(tsdn, &shard->mtx); - assert(shard->bytes_cur <= sec->opts.max_bytes); - size_t size = edata_size_get(edata); - pszind_t pszind = sz_psz2ind(size); - assert(pszind < sec->npsizes); - /* - * Prepending here results in LIFO allocation per bin, which seems - * reasonable. - */ - sec_bin_t *bin = &shard->bins[pszind]; - edata_list_active_prepend(&bin->freelist, edata); - bin->bytes_cur += size; - shard->bytes_cur += size; - if (shard->bytes_cur > sec->opts.max_bytes) { - /* - * We've exceeded the shard limit. We make two nods in the - * direction of fragmentation avoidance: we flush everything in - * the shard, rather than one particular bin, and we hold the - * lock while flushing (in case one of the extents we flush is - * highly preferred from a fragmentation-avoidance perspective - * in the backing allocator). This has the extra advantage of - * not requiring advanced cache balancing strategies. - */ - sec_flush_some_and_unlock(tsdn, sec, shard); - malloc_mutex_assert_not_owner(tsdn, &shard->mtx); - } else { - malloc_mutex_unlock(tsdn, &shard->mtx); - } -} - -static void -sec_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata, - bool *deferred_work_generated) { - sec_t *sec = (sec_t *)self; - if (sec->opts.nshards == 0 - || edata_size_get(edata) > sec->opts.max_alloc) { - pai_dalloc(tsdn, sec->fallback, edata, - deferred_work_generated); - return; - } - sec_shard_t *shard = sec_shard_pick(tsdn, sec); - malloc_mutex_lock(tsdn, &shard->mtx); - if (shard->enabled) { - sec_shard_dalloc_and_unlock(tsdn, sec, shard, edata); - } else { - malloc_mutex_unlock(tsdn, &shard->mtx); - pai_dalloc(tsdn, sec->fallback, edata, - deferred_work_generated); - } -} - -void -sec_flush(tsdn_t *tsdn, sec_t *sec) { - for (size_t i = 0; i < sec->opts.nshards; i++) { - malloc_mutex_lock(tsdn, &sec->shards[i].mtx); - sec_flush_all_locked(tsdn, sec, &sec->shards[i]); - malloc_mutex_unlock(tsdn, &sec->shards[i].mtx); - } -} - -void -sec_disable(tsdn_t *tsdn, sec_t *sec) { - for (size_t i = 0; i < sec->opts.nshards; i++) { - malloc_mutex_lock(tsdn, &sec->shards[i].mtx); - sec->shards[i].enabled = false; - sec_flush_all_locked(tsdn, sec, &sec->shards[i]); - malloc_mutex_unlock(tsdn, &sec->shards[i].mtx); - } -} - -void -sec_stats_merge(tsdn_t *tsdn, sec_t *sec, sec_stats_t *stats) { - size_t sum = 0; - for (size_t i = 0; i < sec->opts.nshards; i++) { - /* - * We could save these lock acquisitions by making bytes_cur - * atomic, but stats collection is rare anyways and we expect - * the number and type of stats to get more interesting. - */ - malloc_mutex_lock(tsdn, &sec->shards[i].mtx); - sum += sec->shards[i].bytes_cur; - malloc_mutex_unlock(tsdn, &sec->shards[i].mtx); - } - stats->bytes += sum; -} - -void -sec_mutex_stats_read(tsdn_t *tsdn, sec_t *sec, - mutex_prof_data_t *mutex_prof_data) { - for (size_t i = 0; i < sec->opts.nshards; i++) { - malloc_mutex_lock(tsdn, &sec->shards[i].mtx); - malloc_mutex_prof_accum(tsdn, mutex_prof_data, - &sec->shards[i].mtx); - malloc_mutex_unlock(tsdn, &sec->shards[i].mtx); - } -} - -void -sec_prefork2(tsdn_t *tsdn, sec_t *sec) { - for (size_t i = 0; i < sec->opts.nshards; i++) { - malloc_mutex_prefork(tsdn, &sec->shards[i].mtx); - } -} - -void -sec_postfork_parent(tsdn_t *tsdn, sec_t *sec) { - for (size_t i = 0; i < sec->opts.nshards; i++) { - malloc_mutex_postfork_parent(tsdn, &sec->shards[i].mtx); - } -} - -void -sec_postfork_child(tsdn_t *tsdn, sec_t *sec) { - for (size_t i = 0; i < sec->opts.nshards; i++) { - malloc_mutex_postfork_child(tsdn, &sec->shards[i].mtx); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/stats.c b/fluent-bit/lib/jemalloc-5.3.0/src/stats.c deleted file mode 100644 index efc70fd3..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/stats.c +++ /dev/null @@ -1,1973 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/ctl.h" -#include "jemalloc/internal/emitter.h" -#include "jemalloc/internal/fxp.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/mutex_prof.h" -#include "jemalloc/internal/prof_stats.h" - -const char *global_mutex_names[mutex_prof_num_global_mutexes] = { -#define OP(mtx) #mtx, - MUTEX_PROF_GLOBAL_MUTEXES -#undef OP -}; - -const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = { -#define OP(mtx) #mtx, - MUTEX_PROF_ARENA_MUTEXES -#undef OP -}; - -#define CTL_GET(n, v, t) do { \ - size_t sz = sizeof(t); \ - xmallctl(n, (void *)v, &sz, NULL, 0); \ -} while (0) - -#define CTL_LEAF_PREPARE(mib, miblen, name) do { \ - assert(miblen < CTL_MAX_DEPTH); \ - size_t miblen_new = CTL_MAX_DEPTH; \ - xmallctlmibnametomib(mib, miblen, name, &miblen_new); \ - assert(miblen_new > miblen); \ -} while (0) - -#define CTL_LEAF(mib, miblen, leaf, v, t) do { \ - assert(miblen < CTL_MAX_DEPTH); \ - size_t miblen_new = CTL_MAX_DEPTH; \ - size_t sz = sizeof(t); \ - xmallctlbymibname(mib, miblen, leaf, &miblen_new, (void *)v, \ - &sz, NULL, 0); \ - assert(miblen_new == miblen + 1); \ -} while (0) - -#define CTL_M2_GET(n, i, v, t) do { \ - size_t mib[CTL_MAX_DEPTH]; \ - size_t miblen = sizeof(mib) / sizeof(size_t); \ - size_t sz = sizeof(t); \ - xmallctlnametomib(n, mib, &miblen); \ - mib[2] = (i); \ - xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \ -} while (0) - -/******************************************************************************/ -/* Data. */ - -bool opt_stats_print = false; -char opt_stats_print_opts[stats_print_tot_num_options+1] = ""; - -int64_t opt_stats_interval = STATS_INTERVAL_DEFAULT; -char opt_stats_interval_opts[stats_print_tot_num_options+1] = ""; - -static counter_accum_t stats_interval_accumulated; -/* Per thread batch accum size for stats_interval. */ -static uint64_t stats_interval_accum_batch; - -/******************************************************************************/ - -static uint64_t -rate_per_second(uint64_t value, uint64_t uptime_ns) { - uint64_t billion = 1000000000; - if (uptime_ns == 0 || value == 0) { - return 0; - } - if (uptime_ns < billion) { - return value; - } else { - uint64_t uptime_s = uptime_ns / billion; - return value / uptime_s; - } -} - -/* Calculate x.yyy and output a string (takes a fixed sized char array). */ -static bool -get_rate_str(uint64_t dividend, uint64_t divisor, char str[6]) { - if (divisor == 0 || dividend > divisor) { - /* The rate is not supposed to be greater than 1. */ - return true; - } - if (dividend > 0) { - assert(UINT64_MAX / dividend >= 1000); - } - - unsigned n = (unsigned)((dividend * 1000) / divisor); - if (n < 10) { - malloc_snprintf(str, 6, "0.00%u", n); - } else if (n < 100) { - malloc_snprintf(str, 6, "0.0%u", n); - } else if (n < 1000) { - malloc_snprintf(str, 6, "0.%u", n); - } else { - malloc_snprintf(str, 6, "1"); - } - - return false; -} - -static void -mutex_stats_init_cols(emitter_row_t *row, const char *table_name, - emitter_col_t *name, - emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters], - emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) { - mutex_prof_uint64_t_counter_ind_t k_uint64_t = 0; - mutex_prof_uint32_t_counter_ind_t k_uint32_t = 0; - - emitter_col_t *col; - - if (name != NULL) { - emitter_col_init(name, row); - name->justify = emitter_justify_left; - name->width = 21; - name->type = emitter_type_title; - name->str_val = table_name; - } - -#define WIDTH_uint32_t 12 -#define WIDTH_uint64_t 16 -#define OP(counter, counter_type, human, derived, base_counter) \ - col = &col_##counter_type[k_##counter_type]; \ - ++k_##counter_type; \ - emitter_col_init(col, row); \ - col->justify = emitter_justify_right; \ - col->width = derived ? 8 : WIDTH_##counter_type; \ - col->type = emitter_type_title; \ - col->str_val = human; - MUTEX_PROF_COUNTERS -#undef OP -#undef WIDTH_uint32_t -#undef WIDTH_uint64_t - col_uint64_t[mutex_counter_total_wait_time_ps].width = 10; -} - -static void -mutex_stats_read_global(size_t mib[], size_t miblen, const char *name, - emitter_col_t *col_name, - emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters], - emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters], - uint64_t uptime) { - CTL_LEAF_PREPARE(mib, miblen, name); - size_t miblen_name = miblen + 1; - - col_name->str_val = name; - - emitter_col_t *dst; -#define EMITTER_TYPE_uint32_t emitter_type_uint32 -#define EMITTER_TYPE_uint64_t emitter_type_uint64 -#define OP(counter, counter_type, human, derived, base_counter) \ - dst = &col_##counter_type[mutex_counter_##counter]; \ - dst->type = EMITTER_TYPE_##counter_type; \ - if (!derived) { \ - CTL_LEAF(mib, miblen_name, #counter, \ - (counter_type *)&dst->bool_val, counter_type); \ - } else { \ - emitter_col_t *base = \ - &col_##counter_type[mutex_counter_##base_counter]; \ - dst->counter_type##_val = \ - (counter_type)rate_per_second( \ - base->counter_type##_val, uptime); \ - } - MUTEX_PROF_COUNTERS -#undef OP -#undef EMITTER_TYPE_uint32_t -#undef EMITTER_TYPE_uint64_t -} - -static void -mutex_stats_read_arena(size_t mib[], size_t miblen, const char *name, - emitter_col_t *col_name, - emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters], - emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters], - uint64_t uptime) { - CTL_LEAF_PREPARE(mib, miblen, name); - size_t miblen_name = miblen + 1; - - col_name->str_val = name; - - emitter_col_t *dst; -#define EMITTER_TYPE_uint32_t emitter_type_uint32 -#define EMITTER_TYPE_uint64_t emitter_type_uint64 -#define OP(counter, counter_type, human, derived, base_counter) \ - dst = &col_##counter_type[mutex_counter_##counter]; \ - dst->type = EMITTER_TYPE_##counter_type; \ - if (!derived) { \ - CTL_LEAF(mib, miblen_name, #counter, \ - (counter_type *)&dst->bool_val, counter_type); \ - } else { \ - emitter_col_t *base = \ - &col_##counter_type[mutex_counter_##base_counter]; \ - dst->counter_type##_val = \ - (counter_type)rate_per_second( \ - base->counter_type##_val, uptime); \ - } - MUTEX_PROF_COUNTERS -#undef OP -#undef EMITTER_TYPE_uint32_t -#undef EMITTER_TYPE_uint64_t -} - -static void -mutex_stats_read_arena_bin(size_t mib[], size_t miblen, - emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters], - emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters], - uint64_t uptime) { - CTL_LEAF_PREPARE(mib, miblen, "mutex"); - size_t miblen_mutex = miblen + 1; - - emitter_col_t *dst; - -#define EMITTER_TYPE_uint32_t emitter_type_uint32 -#define EMITTER_TYPE_uint64_t emitter_type_uint64 -#define OP(counter, counter_type, human, derived, base_counter) \ - dst = &col_##counter_type[mutex_counter_##counter]; \ - dst->type = EMITTER_TYPE_##counter_type; \ - if (!derived) { \ - CTL_LEAF(mib, miblen_mutex, #counter, \ - (counter_type *)&dst->bool_val, counter_type); \ - } else { \ - emitter_col_t *base = \ - &col_##counter_type[mutex_counter_##base_counter]; \ - dst->counter_type##_val = \ - (counter_type)rate_per_second( \ - base->counter_type##_val, uptime); \ - } - MUTEX_PROF_COUNTERS -#undef OP -#undef EMITTER_TYPE_uint32_t -#undef EMITTER_TYPE_uint64_t -} - -/* "row" can be NULL to avoid emitting in table mode. */ -static void -mutex_stats_emit(emitter_t *emitter, emitter_row_t *row, - emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters], - emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) { - if (row != NULL) { - emitter_table_row(emitter, row); - } - - mutex_prof_uint64_t_counter_ind_t k_uint64_t = 0; - mutex_prof_uint32_t_counter_ind_t k_uint32_t = 0; - - emitter_col_t *col; - -#define EMITTER_TYPE_uint32_t emitter_type_uint32 -#define EMITTER_TYPE_uint64_t emitter_type_uint64 -#define OP(counter, type, human, derived, base_counter) \ - if (!derived) { \ - col = &col_##type[k_##type]; \ - ++k_##type; \ - emitter_json_kv(emitter, #counter, EMITTER_TYPE_##type, \ - (const void *)&col->bool_val); \ - } - MUTEX_PROF_COUNTERS; -#undef OP -#undef EMITTER_TYPE_uint32_t -#undef EMITTER_TYPE_uint64_t -} - -#define COL_DECLARE(column_name) \ - emitter_col_t col_##column_name; - -#define COL_INIT(row_name, column_name, left_or_right, col_width, etype)\ - emitter_col_init(&col_##column_name, &row_name); \ - col_##column_name.justify = emitter_justify_##left_or_right; \ - col_##column_name.width = col_width; \ - col_##column_name.type = emitter_type_##etype; - -#define COL(row_name, column_name, left_or_right, col_width, etype) \ - COL_DECLARE(column_name); \ - COL_INIT(row_name, column_name, left_or_right, col_width, etype) - -#define COL_HDR_DECLARE(column_name) \ - COL_DECLARE(column_name); \ - emitter_col_t header_##column_name; - -#define COL_HDR_INIT(row_name, column_name, human, left_or_right, \ - col_width, etype) \ - COL_INIT(row_name, column_name, left_or_right, col_width, etype)\ - emitter_col_init(&header_##column_name, &header_##row_name); \ - header_##column_name.justify = emitter_justify_##left_or_right; \ - header_##column_name.width = col_width; \ - header_##column_name.type = emitter_type_title; \ - header_##column_name.str_val = human ? human : #column_name; - -#define COL_HDR(row_name, column_name, human, left_or_right, col_width, \ - etype) \ - COL_HDR_DECLARE(column_name) \ - COL_HDR_INIT(row_name, column_name, human, left_or_right, \ - col_width, etype) - -JEMALLOC_COLD -static void -stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, - uint64_t uptime) { - size_t page; - bool in_gap, in_gap_prev; - unsigned nbins, j; - - CTL_GET("arenas.page", &page, size_t); - - CTL_GET("arenas.nbins", &nbins, unsigned); - - emitter_row_t header_row; - emitter_row_init(&header_row); - - emitter_row_t row; - emitter_row_init(&row); - - bool prof_stats_on = config_prof && opt_prof && opt_prof_stats - && i == MALLCTL_ARENAS_ALL; - - COL_HDR(row, size, NULL, right, 20, size) - COL_HDR(row, ind, NULL, right, 4, unsigned) - COL_HDR(row, allocated, NULL, right, 13, uint64) - COL_HDR(row, nmalloc, NULL, right, 13, uint64) - COL_HDR(row, nmalloc_ps, "(#/sec)", right, 8, uint64) - COL_HDR(row, ndalloc, NULL, right, 13, uint64) - COL_HDR(row, ndalloc_ps, "(#/sec)", right, 8, uint64) - COL_HDR(row, nrequests, NULL, right, 13, uint64) - COL_HDR(row, nrequests_ps, "(#/sec)", right, 10, uint64) - COL_HDR_DECLARE(prof_live_requested); - COL_HDR_DECLARE(prof_live_count); - COL_HDR_DECLARE(prof_accum_requested); - COL_HDR_DECLARE(prof_accum_count); - if (prof_stats_on) { - COL_HDR_INIT(row, prof_live_requested, NULL, right, 21, uint64) - COL_HDR_INIT(row, prof_live_count, NULL, right, 17, uint64) - COL_HDR_INIT(row, prof_accum_requested, NULL, right, 21, uint64) - COL_HDR_INIT(row, prof_accum_count, NULL, right, 17, uint64) - } - COL_HDR(row, nshards, NULL, right, 9, unsigned) - COL_HDR(row, curregs, NULL, right, 13, size) - COL_HDR(row, curslabs, NULL, right, 13, size) - COL_HDR(row, nonfull_slabs, NULL, right, 15, size) - COL_HDR(row, regs, NULL, right, 5, unsigned) - COL_HDR(row, pgs, NULL, right, 4, size) - /* To buffer a right- and left-justified column. */ - COL_HDR(row, justify_spacer, NULL, right, 1, title) - COL_HDR(row, util, NULL, right, 6, title) - COL_HDR(row, nfills, NULL, right, 13, uint64) - COL_HDR(row, nfills_ps, "(#/sec)", right, 8, uint64) - COL_HDR(row, nflushes, NULL, right, 13, uint64) - COL_HDR(row, nflushes_ps, "(#/sec)", right, 8, uint64) - COL_HDR(row, nslabs, NULL, right, 13, uint64) - COL_HDR(row, nreslabs, NULL, right, 13, uint64) - COL_HDR(row, nreslabs_ps, "(#/sec)", right, 8, uint64) - - /* Don't want to actually print the name. */ - header_justify_spacer.str_val = " "; - col_justify_spacer.str_val = " "; - - emitter_col_t col_mutex64[mutex_prof_num_uint64_t_counters]; - emitter_col_t col_mutex32[mutex_prof_num_uint32_t_counters]; - - emitter_col_t header_mutex64[mutex_prof_num_uint64_t_counters]; - emitter_col_t header_mutex32[mutex_prof_num_uint32_t_counters]; - - if (mutex) { - mutex_stats_init_cols(&row, NULL, NULL, col_mutex64, - col_mutex32); - mutex_stats_init_cols(&header_row, NULL, NULL, header_mutex64, - header_mutex32); - } - - /* - * We print a "bins:" header as part of the table row; we need to adjust - * the header size column to compensate. - */ - header_size.width -=5; - emitter_table_printf(emitter, "bins:"); - emitter_table_row(emitter, &header_row); - emitter_json_array_kv_begin(emitter, "bins"); - - size_t stats_arenas_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas"); - stats_arenas_mib[2] = i; - CTL_LEAF_PREPARE(stats_arenas_mib, 3, "bins"); - - size_t arenas_bin_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(arenas_bin_mib, 0, "arenas.bin"); - - size_t prof_stats_mib[CTL_MAX_DEPTH]; - if (prof_stats_on) { - CTL_LEAF_PREPARE(prof_stats_mib, 0, "prof.stats.bins"); - } - - for (j = 0, in_gap = false; j < nbins; j++) { - uint64_t nslabs; - size_t reg_size, slab_size, curregs; - size_t curslabs; - size_t nonfull_slabs; - uint32_t nregs, nshards; - uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes; - uint64_t nreslabs; - prof_stats_t prof_live; - prof_stats_t prof_accum; - - stats_arenas_mib[4] = j; - arenas_bin_mib[2] = j; - - CTL_LEAF(stats_arenas_mib, 5, "nslabs", &nslabs, uint64_t); - - if (prof_stats_on) { - prof_stats_mib[3] = j; - CTL_LEAF(prof_stats_mib, 4, "live", &prof_live, - prof_stats_t); - CTL_LEAF(prof_stats_mib, 4, "accum", &prof_accum, - prof_stats_t); - } - - in_gap_prev = in_gap; - if (prof_stats_on) { - in_gap = (nslabs == 0 && prof_accum.count == 0); - } else { - in_gap = (nslabs == 0); - } - - if (in_gap_prev && !in_gap) { - emitter_table_printf(emitter, - " ---\n"); - } - - if (in_gap && !emitter_outputs_json(emitter)) { - continue; - } - - CTL_LEAF(arenas_bin_mib, 3, "size", ®_size, size_t); - CTL_LEAF(arenas_bin_mib, 3, "nregs", &nregs, uint32_t); - CTL_LEAF(arenas_bin_mib, 3, "slab_size", &slab_size, size_t); - CTL_LEAF(arenas_bin_mib, 3, "nshards", &nshards, uint32_t); - CTL_LEAF(stats_arenas_mib, 5, "nmalloc", &nmalloc, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "ndalloc", &ndalloc, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "curregs", &curregs, size_t); - CTL_LEAF(stats_arenas_mib, 5, "nrequests", &nrequests, - uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "nfills", &nfills, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "nflushes", &nflushes, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "nreslabs", &nreslabs, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "curslabs", &curslabs, size_t); - CTL_LEAF(stats_arenas_mib, 5, "nonfull_slabs", &nonfull_slabs, - size_t); - - if (mutex) { - mutex_stats_read_arena_bin(stats_arenas_mib, 5, - col_mutex64, col_mutex32, uptime); - } - - emitter_json_object_begin(emitter); - emitter_json_kv(emitter, "nmalloc", emitter_type_uint64, - &nmalloc); - emitter_json_kv(emitter, "ndalloc", emitter_type_uint64, - &ndalloc); - emitter_json_kv(emitter, "curregs", emitter_type_size, - &curregs); - emitter_json_kv(emitter, "nrequests", emitter_type_uint64, - &nrequests); - if (prof_stats_on) { - emitter_json_kv(emitter, "prof_live_requested", - emitter_type_uint64, &prof_live.req_sum); - emitter_json_kv(emitter, "prof_live_count", - emitter_type_uint64, &prof_live.count); - emitter_json_kv(emitter, "prof_accum_requested", - emitter_type_uint64, &prof_accum.req_sum); - emitter_json_kv(emitter, "prof_accum_count", - emitter_type_uint64, &prof_accum.count); - } - emitter_json_kv(emitter, "nfills", emitter_type_uint64, - &nfills); - emitter_json_kv(emitter, "nflushes", emitter_type_uint64, - &nflushes); - emitter_json_kv(emitter, "nreslabs", emitter_type_uint64, - &nreslabs); - emitter_json_kv(emitter, "curslabs", emitter_type_size, - &curslabs); - emitter_json_kv(emitter, "nonfull_slabs", emitter_type_size, - &nonfull_slabs); - if (mutex) { - emitter_json_object_kv_begin(emitter, "mutex"); - mutex_stats_emit(emitter, NULL, col_mutex64, - col_mutex32); - emitter_json_object_end(emitter); - } - emitter_json_object_end(emitter); - - size_t availregs = nregs * curslabs; - char util[6]; - if (get_rate_str((uint64_t)curregs, (uint64_t)availregs, util)) - { - if (availregs == 0) { - malloc_snprintf(util, sizeof(util), "1"); - } else if (curregs > availregs) { - /* - * Race detected: the counters were read in - * separate mallctl calls and concurrent - * operations happened in between. In this case - * no meaningful utilization can be computed. - */ - malloc_snprintf(util, sizeof(util), " race"); - } else { - not_reached(); - } - } - - col_size.size_val = reg_size; - col_ind.unsigned_val = j; - col_allocated.size_val = curregs * reg_size; - col_nmalloc.uint64_val = nmalloc; - col_nmalloc_ps.uint64_val = rate_per_second(nmalloc, uptime); - col_ndalloc.uint64_val = ndalloc; - col_ndalloc_ps.uint64_val = rate_per_second(ndalloc, uptime); - col_nrequests.uint64_val = nrequests; - col_nrequests_ps.uint64_val = rate_per_second(nrequests, uptime); - if (prof_stats_on) { - col_prof_live_requested.uint64_val = prof_live.req_sum; - col_prof_live_count.uint64_val = prof_live.count; - col_prof_accum_requested.uint64_val = - prof_accum.req_sum; - col_prof_accum_count.uint64_val = prof_accum.count; - } - col_nshards.unsigned_val = nshards; - col_curregs.size_val = curregs; - col_curslabs.size_val = curslabs; - col_nonfull_slabs.size_val = nonfull_slabs; - col_regs.unsigned_val = nregs; - col_pgs.size_val = slab_size / page; - col_util.str_val = util; - col_nfills.uint64_val = nfills; - col_nfills_ps.uint64_val = rate_per_second(nfills, uptime); - col_nflushes.uint64_val = nflushes; - col_nflushes_ps.uint64_val = rate_per_second(nflushes, uptime); - col_nslabs.uint64_val = nslabs; - col_nreslabs.uint64_val = nreslabs; - col_nreslabs_ps.uint64_val = rate_per_second(nreslabs, uptime); - - /* - * Note that mutex columns were initialized above, if mutex == - * true. - */ - - emitter_table_row(emitter, &row); - } - emitter_json_array_end(emitter); /* Close "bins". */ - - if (in_gap) { - emitter_table_printf(emitter, " ---\n"); - } -} - -JEMALLOC_COLD -static void -stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) { - unsigned nbins, nlextents, j; - bool in_gap, in_gap_prev; - - CTL_GET("arenas.nbins", &nbins, unsigned); - CTL_GET("arenas.nlextents", &nlextents, unsigned); - - emitter_row_t header_row; - emitter_row_init(&header_row); - emitter_row_t row; - emitter_row_init(&row); - - bool prof_stats_on = config_prof && opt_prof && opt_prof_stats - && i == MALLCTL_ARENAS_ALL; - - COL_HDR(row, size, NULL, right, 20, size) - COL_HDR(row, ind, NULL, right, 4, unsigned) - COL_HDR(row, allocated, NULL, right, 13, size) - COL_HDR(row, nmalloc, NULL, right, 13, uint64) - COL_HDR(row, nmalloc_ps, "(#/sec)", right, 8, uint64) - COL_HDR(row, ndalloc, NULL, right, 13, uint64) - COL_HDR(row, ndalloc_ps, "(#/sec)", right, 8, uint64) - COL_HDR(row, nrequests, NULL, right, 13, uint64) - COL_HDR(row, nrequests_ps, "(#/sec)", right, 8, uint64) - COL_HDR_DECLARE(prof_live_requested) - COL_HDR_DECLARE(prof_live_count) - COL_HDR_DECLARE(prof_accum_requested) - COL_HDR_DECLARE(prof_accum_count) - if (prof_stats_on) { - COL_HDR_INIT(row, prof_live_requested, NULL, right, 21, uint64) - COL_HDR_INIT(row, prof_live_count, NULL, right, 17, uint64) - COL_HDR_INIT(row, prof_accum_requested, NULL, right, 21, uint64) - COL_HDR_INIT(row, prof_accum_count, NULL, right, 17, uint64) - } - COL_HDR(row, curlextents, NULL, right, 13, size) - - /* As with bins, we label the large extents table. */ - header_size.width -= 6; - emitter_table_printf(emitter, "large:"); - emitter_table_row(emitter, &header_row); - emitter_json_array_kv_begin(emitter, "lextents"); - - size_t stats_arenas_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas"); - stats_arenas_mib[2] = i; - CTL_LEAF_PREPARE(stats_arenas_mib, 3, "lextents"); - - size_t arenas_lextent_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(arenas_lextent_mib, 0, "arenas.lextent"); - - size_t prof_stats_mib[CTL_MAX_DEPTH]; - if (prof_stats_on) { - CTL_LEAF_PREPARE(prof_stats_mib, 0, "prof.stats.lextents"); - } - - for (j = 0, in_gap = false; j < nlextents; j++) { - uint64_t nmalloc, ndalloc, nrequests; - size_t lextent_size, curlextents; - prof_stats_t prof_live; - prof_stats_t prof_accum; - - stats_arenas_mib[4] = j; - arenas_lextent_mib[2] = j; - - CTL_LEAF(stats_arenas_mib, 5, "nmalloc", &nmalloc, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "ndalloc", &ndalloc, uint64_t); - CTL_LEAF(stats_arenas_mib, 5, "nrequests", &nrequests, - uint64_t); - - in_gap_prev = in_gap; - in_gap = (nrequests == 0); - - if (in_gap_prev && !in_gap) { - emitter_table_printf(emitter, - " ---\n"); - } - - CTL_LEAF(arenas_lextent_mib, 3, "size", &lextent_size, size_t); - CTL_LEAF(stats_arenas_mib, 5, "curlextents", &curlextents, - size_t); - - if (prof_stats_on) { - prof_stats_mib[3] = j; - CTL_LEAF(prof_stats_mib, 4, "live", &prof_live, - prof_stats_t); - CTL_LEAF(prof_stats_mib, 4, "accum", &prof_accum, - prof_stats_t); - } - - emitter_json_object_begin(emitter); - if (prof_stats_on) { - emitter_json_kv(emitter, "prof_live_requested", - emitter_type_uint64, &prof_live.req_sum); - emitter_json_kv(emitter, "prof_live_count", - emitter_type_uint64, &prof_live.count); - emitter_json_kv(emitter, "prof_accum_requested", - emitter_type_uint64, &prof_accum.req_sum); - emitter_json_kv(emitter, "prof_accum_count", - emitter_type_uint64, &prof_accum.count); - } - emitter_json_kv(emitter, "curlextents", emitter_type_size, - &curlextents); - emitter_json_object_end(emitter); - - col_size.size_val = lextent_size; - col_ind.unsigned_val = nbins + j; - col_allocated.size_val = curlextents * lextent_size; - col_nmalloc.uint64_val = nmalloc; - col_nmalloc_ps.uint64_val = rate_per_second(nmalloc, uptime); - col_ndalloc.uint64_val = ndalloc; - col_ndalloc_ps.uint64_val = rate_per_second(ndalloc, uptime); - col_nrequests.uint64_val = nrequests; - col_nrequests_ps.uint64_val = rate_per_second(nrequests, uptime); - if (prof_stats_on) { - col_prof_live_requested.uint64_val = prof_live.req_sum; - col_prof_live_count.uint64_val = prof_live.count; - col_prof_accum_requested.uint64_val = - prof_accum.req_sum; - col_prof_accum_count.uint64_val = prof_accum.count; - } - col_curlextents.size_val = curlextents; - - if (!in_gap) { - emitter_table_row(emitter, &row); - } - } - emitter_json_array_end(emitter); /* Close "lextents". */ - if (in_gap) { - emitter_table_printf(emitter, " ---\n"); - } -} - -JEMALLOC_COLD -static void -stats_arena_extents_print(emitter_t *emitter, unsigned i) { - unsigned j; - bool in_gap, in_gap_prev; - emitter_row_t header_row; - emitter_row_init(&header_row); - emitter_row_t row; - emitter_row_init(&row); - - COL_HDR(row, size, NULL, right, 20, size) - COL_HDR(row, ind, NULL, right, 4, unsigned) - COL_HDR(row, ndirty, NULL, right, 13, size) - COL_HDR(row, dirty, NULL, right, 13, size) - COL_HDR(row, nmuzzy, NULL, right, 13, size) - COL_HDR(row, muzzy, NULL, right, 13, size) - COL_HDR(row, nretained, NULL, right, 13, size) - COL_HDR(row, retained, NULL, right, 13, size) - COL_HDR(row, ntotal, NULL, right, 13, size) - COL_HDR(row, total, NULL, right, 13, size) - - /* Label this section. */ - header_size.width -= 8; - emitter_table_printf(emitter, "extents:"); - emitter_table_row(emitter, &header_row); - emitter_json_array_kv_begin(emitter, "extents"); - - size_t stats_arenas_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas"); - stats_arenas_mib[2] = i; - CTL_LEAF_PREPARE(stats_arenas_mib, 3, "extents"); - - in_gap = false; - for (j = 0; j < SC_NPSIZES; j++) { - size_t ndirty, nmuzzy, nretained, total, dirty_bytes, - muzzy_bytes, retained_bytes, total_bytes; - stats_arenas_mib[4] = j; - - CTL_LEAF(stats_arenas_mib, 5, "ndirty", &ndirty, size_t); - CTL_LEAF(stats_arenas_mib, 5, "nmuzzy", &nmuzzy, size_t); - CTL_LEAF(stats_arenas_mib, 5, "nretained", &nretained, size_t); - CTL_LEAF(stats_arenas_mib, 5, "dirty_bytes", &dirty_bytes, - size_t); - CTL_LEAF(stats_arenas_mib, 5, "muzzy_bytes", &muzzy_bytes, - size_t); - CTL_LEAF(stats_arenas_mib, 5, "retained_bytes", - &retained_bytes, size_t); - - total = ndirty + nmuzzy + nretained; - total_bytes = dirty_bytes + muzzy_bytes + retained_bytes; - - in_gap_prev = in_gap; - in_gap = (total == 0); - - if (in_gap_prev && !in_gap) { - emitter_table_printf(emitter, - " ---\n"); - } - - emitter_json_object_begin(emitter); - emitter_json_kv(emitter, "ndirty", emitter_type_size, &ndirty); - emitter_json_kv(emitter, "nmuzzy", emitter_type_size, &nmuzzy); - emitter_json_kv(emitter, "nretained", emitter_type_size, - &nretained); - - emitter_json_kv(emitter, "dirty_bytes", emitter_type_size, - &dirty_bytes); - emitter_json_kv(emitter, "muzzy_bytes", emitter_type_size, - &muzzy_bytes); - emitter_json_kv(emitter, "retained_bytes", emitter_type_size, - &retained_bytes); - emitter_json_object_end(emitter); - - col_size.size_val = sz_pind2sz(j); - col_ind.size_val = j; - col_ndirty.size_val = ndirty; - col_dirty.size_val = dirty_bytes; - col_nmuzzy.size_val = nmuzzy; - col_muzzy.size_val = muzzy_bytes; - col_nretained.size_val = nretained; - col_retained.size_val = retained_bytes; - col_ntotal.size_val = total; - col_total.size_val = total_bytes; - - if (!in_gap) { - emitter_table_row(emitter, &row); - } - } - emitter_json_array_end(emitter); /* Close "extents". */ - if (in_gap) { - emitter_table_printf(emitter, " ---\n"); - } -} - -static void -stats_arena_hpa_shard_print(emitter_t *emitter, unsigned i, uint64_t uptime) { - emitter_row_t header_row; - emitter_row_init(&header_row); - emitter_row_t row; - emitter_row_init(&row); - - uint64_t npurge_passes; - uint64_t npurges; - uint64_t nhugifies; - uint64_t ndehugifies; - - CTL_M2_GET("stats.arenas.0.hpa_shard.npurge_passes", - i, &npurge_passes, uint64_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.npurges", - i, &npurges, uint64_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.nhugifies", - i, &nhugifies, uint64_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.ndehugifies", - i, &ndehugifies, uint64_t); - - size_t npageslabs_huge; - size_t nactive_huge; - size_t ndirty_huge; - - size_t npageslabs_nonhuge; - size_t nactive_nonhuge; - size_t ndirty_nonhuge; - size_t nretained_nonhuge; - - size_t sec_bytes; - CTL_M2_GET("stats.arenas.0.hpa_sec_bytes", i, &sec_bytes, size_t); - emitter_kv(emitter, "sec_bytes", "Bytes in small extent cache", - emitter_type_size, &sec_bytes); - - /* First, global stats. */ - emitter_table_printf(emitter, - "HPA shard stats:\n" - " Purge passes: %" FMTu64 " (%" FMTu64 " / sec)\n" - " Purges: %" FMTu64 " (%" FMTu64 " / sec)\n" - " Hugeifies: %" FMTu64 " (%" FMTu64 " / sec)\n" - " Dehugifies: %" FMTu64 " (%" FMTu64 " / sec)\n" - "\n", - npurge_passes, rate_per_second(npurge_passes, uptime), - npurges, rate_per_second(npurges, uptime), - nhugifies, rate_per_second(nhugifies, uptime), - ndehugifies, rate_per_second(ndehugifies, uptime)); - - emitter_json_object_kv_begin(emitter, "hpa_shard"); - emitter_json_kv(emitter, "npurge_passes", emitter_type_uint64, - &npurge_passes); - emitter_json_kv(emitter, "npurges", emitter_type_uint64, - &npurges); - emitter_json_kv(emitter, "nhugifies", emitter_type_uint64, - &nhugifies); - emitter_json_kv(emitter, "ndehugifies", emitter_type_uint64, - &ndehugifies); - - /* Next, full slab stats. */ - CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.npageslabs_huge", - i, &npageslabs_huge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.nactive_huge", - i, &nactive_huge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.ndirty_huge", - i, &ndirty_huge, size_t); - - CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.npageslabs_nonhuge", - i, &npageslabs_nonhuge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.nactive_nonhuge", - i, &nactive_nonhuge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.ndirty_nonhuge", - i, &ndirty_nonhuge, size_t); - nretained_nonhuge = npageslabs_nonhuge * HUGEPAGE_PAGES - - nactive_nonhuge - ndirty_nonhuge; - - emitter_table_printf(emitter, - " In full slabs:\n" - " npageslabs: %zu huge, %zu nonhuge\n" - " nactive: %zu huge, %zu nonhuge \n" - " ndirty: %zu huge, %zu nonhuge \n" - " nretained: 0 huge, %zu nonhuge \n", - npageslabs_huge, npageslabs_nonhuge, - nactive_huge, nactive_nonhuge, - ndirty_huge, ndirty_nonhuge, - nretained_nonhuge); - - emitter_json_object_kv_begin(emitter, "full_slabs"); - emitter_json_kv(emitter, "npageslabs_huge", emitter_type_size, - &npageslabs_huge); - emitter_json_kv(emitter, "nactive_huge", emitter_type_size, - &nactive_huge); - emitter_json_kv(emitter, "nactive_huge", emitter_type_size, - &nactive_huge); - emitter_json_kv(emitter, "npageslabs_nonhuge", emitter_type_size, - &npageslabs_nonhuge); - emitter_json_kv(emitter, "nactive_nonhuge", emitter_type_size, - &nactive_nonhuge); - emitter_json_kv(emitter, "ndirty_nonhuge", emitter_type_size, - &ndirty_nonhuge); - emitter_json_object_end(emitter); /* End "full_slabs" */ - - /* Next, empty slab stats. */ - CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.npageslabs_huge", - i, &npageslabs_huge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.nactive_huge", - i, &nactive_huge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.ndirty_huge", - i, &ndirty_huge, size_t); - - CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.npageslabs_nonhuge", - i, &npageslabs_nonhuge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.nactive_nonhuge", - i, &nactive_nonhuge, size_t); - CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.ndirty_nonhuge", - i, &ndirty_nonhuge, size_t); - nretained_nonhuge = npageslabs_nonhuge * HUGEPAGE_PAGES - - nactive_nonhuge - ndirty_nonhuge; - - emitter_table_printf(emitter, - " In empty slabs:\n" - " npageslabs: %zu huge, %zu nonhuge\n" - " nactive: %zu huge, %zu nonhuge \n" - " ndirty: %zu huge, %zu nonhuge \n" - " nretained: 0 huge, %zu nonhuge \n" - "\n", - npageslabs_huge, npageslabs_nonhuge, - nactive_huge, nactive_nonhuge, - ndirty_huge, ndirty_nonhuge, - nretained_nonhuge); - - emitter_json_object_kv_begin(emitter, "empty_slabs"); - emitter_json_kv(emitter, "npageslabs_huge", emitter_type_size, - &npageslabs_huge); - emitter_json_kv(emitter, "nactive_huge", emitter_type_size, - &nactive_huge); - emitter_json_kv(emitter, "nactive_huge", emitter_type_size, - &nactive_huge); - emitter_json_kv(emitter, "npageslabs_nonhuge", emitter_type_size, - &npageslabs_nonhuge); - emitter_json_kv(emitter, "nactive_nonhuge", emitter_type_size, - &nactive_nonhuge); - emitter_json_kv(emitter, "ndirty_nonhuge", emitter_type_size, - &ndirty_nonhuge); - emitter_json_object_end(emitter); /* End "empty_slabs" */ - - COL_HDR(row, size, NULL, right, 20, size) - COL_HDR(row, ind, NULL, right, 4, unsigned) - COL_HDR(row, npageslabs_huge, NULL, right, 16, size) - COL_HDR(row, nactive_huge, NULL, right, 16, size) - COL_HDR(row, ndirty_huge, NULL, right, 16, size) - COL_HDR(row, npageslabs_nonhuge, NULL, right, 20, size) - COL_HDR(row, nactive_nonhuge, NULL, right, 20, size) - COL_HDR(row, ndirty_nonhuge, NULL, right, 20, size) - COL_HDR(row, nretained_nonhuge, NULL, right, 20, size) - - size_t stats_arenas_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas"); - stats_arenas_mib[2] = i; - CTL_LEAF_PREPARE(stats_arenas_mib, 3, "hpa_shard.nonfull_slabs"); - - emitter_table_row(emitter, &header_row); - emitter_json_array_kv_begin(emitter, "nonfull_slabs"); - bool in_gap = false; - for (pszind_t j = 0; j < PSSET_NPSIZES && j < SC_NPSIZES; j++) { - stats_arenas_mib[5] = j; - - CTL_LEAF(stats_arenas_mib, 6, "npageslabs_huge", - &npageslabs_huge, size_t); - CTL_LEAF(stats_arenas_mib, 6, "nactive_huge", - &nactive_huge, size_t); - CTL_LEAF(stats_arenas_mib, 6, "ndirty_huge", - &ndirty_huge, size_t); - - CTL_LEAF(stats_arenas_mib, 6, "npageslabs_nonhuge", - &npageslabs_nonhuge, size_t); - CTL_LEAF(stats_arenas_mib, 6, "nactive_nonhuge", - &nactive_nonhuge, size_t); - CTL_LEAF(stats_arenas_mib, 6, "ndirty_nonhuge", - &ndirty_nonhuge, size_t); - nretained_nonhuge = npageslabs_nonhuge * HUGEPAGE_PAGES - - nactive_nonhuge - ndirty_nonhuge; - - bool in_gap_prev = in_gap; - in_gap = (npageslabs_huge == 0 && npageslabs_nonhuge == 0); - if (in_gap_prev && !in_gap) { - emitter_table_printf(emitter, - " ---\n"); - } - - col_size.size_val = sz_pind2sz(j); - col_ind.size_val = j; - col_npageslabs_huge.size_val = npageslabs_huge; - col_nactive_huge.size_val = nactive_huge; - col_ndirty_huge.size_val = ndirty_huge; - col_npageslabs_nonhuge.size_val = npageslabs_nonhuge; - col_nactive_nonhuge.size_val = nactive_nonhuge; - col_ndirty_nonhuge.size_val = ndirty_nonhuge; - col_nretained_nonhuge.size_val = nretained_nonhuge; - if (!in_gap) { - emitter_table_row(emitter, &row); - } - - emitter_json_object_begin(emitter); - emitter_json_kv(emitter, "npageslabs_huge", emitter_type_size, - &npageslabs_huge); - emitter_json_kv(emitter, "nactive_huge", emitter_type_size, - &nactive_huge); - emitter_json_kv(emitter, "ndirty_huge", emitter_type_size, - &ndirty_huge); - emitter_json_kv(emitter, "npageslabs_nonhuge", emitter_type_size, - &npageslabs_nonhuge); - emitter_json_kv(emitter, "nactive_nonhuge", emitter_type_size, - &nactive_nonhuge); - emitter_json_kv(emitter, "ndirty_nonhuge", emitter_type_size, - &ndirty_nonhuge); - emitter_json_object_end(emitter); - } - emitter_json_array_end(emitter); /* End "nonfull_slabs" */ - emitter_json_object_end(emitter); /* End "hpa_shard" */ - if (in_gap) { - emitter_table_printf(emitter, " ---\n"); - } -} - -static void -stats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind, uint64_t uptime) { - emitter_row_t row; - emitter_col_t col_name; - emitter_col_t col64[mutex_prof_num_uint64_t_counters]; - emitter_col_t col32[mutex_prof_num_uint32_t_counters]; - - emitter_row_init(&row); - mutex_stats_init_cols(&row, "", &col_name, col64, col32); - - emitter_json_object_kv_begin(emitter, "mutexes"); - emitter_table_row(emitter, &row); - - size_t stats_arenas_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas"); - stats_arenas_mib[2] = arena_ind; - CTL_LEAF_PREPARE(stats_arenas_mib, 3, "mutexes"); - - for (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes; - i++) { - const char *name = arena_mutex_names[i]; - emitter_json_object_kv_begin(emitter, name); - mutex_stats_read_arena(stats_arenas_mib, 4, name, &col_name, - col64, col32, uptime); - mutex_stats_emit(emitter, &row, col64, col32); - emitter_json_object_end(emitter); /* Close the mutex dict. */ - } - emitter_json_object_end(emitter); /* End "mutexes". */ -} - -JEMALLOC_COLD -static void -stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large, - bool mutex, bool extents, bool hpa) { - unsigned nthreads; - const char *dss; - ssize_t dirty_decay_ms, muzzy_decay_ms; - size_t page, pactive, pdirty, pmuzzy, mapped, retained; - size_t base, internal, resident, metadata_thp, extent_avail; - uint64_t dirty_npurge, dirty_nmadvise, dirty_purged; - uint64_t muzzy_npurge, muzzy_nmadvise, muzzy_purged; - size_t small_allocated; - uint64_t small_nmalloc, small_ndalloc, small_nrequests, small_nfills, - small_nflushes; - size_t large_allocated; - uint64_t large_nmalloc, large_ndalloc, large_nrequests, large_nfills, - large_nflushes; - size_t tcache_bytes, tcache_stashed_bytes, abandoned_vm; - uint64_t uptime; - - CTL_GET("arenas.page", &page, size_t); - - CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned); - emitter_kv(emitter, "nthreads", "assigned threads", - emitter_type_unsigned, &nthreads); - - CTL_M2_GET("stats.arenas.0.uptime", i, &uptime, uint64_t); - emitter_kv(emitter, "uptime_ns", "uptime", emitter_type_uint64, - &uptime); - - CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *); - emitter_kv(emitter, "dss", "dss allocation precedence", - emitter_type_string, &dss); - - CTL_M2_GET("stats.arenas.0.dirty_decay_ms", i, &dirty_decay_ms, - ssize_t); - CTL_M2_GET("stats.arenas.0.muzzy_decay_ms", i, &muzzy_decay_ms, - ssize_t); - CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t); - CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t); - CTL_M2_GET("stats.arenas.0.pmuzzy", i, &pmuzzy, size_t); - CTL_M2_GET("stats.arenas.0.dirty_npurge", i, &dirty_npurge, uint64_t); - CTL_M2_GET("stats.arenas.0.dirty_nmadvise", i, &dirty_nmadvise, - uint64_t); - CTL_M2_GET("stats.arenas.0.dirty_purged", i, &dirty_purged, uint64_t); - CTL_M2_GET("stats.arenas.0.muzzy_npurge", i, &muzzy_npurge, uint64_t); - CTL_M2_GET("stats.arenas.0.muzzy_nmadvise", i, &muzzy_nmadvise, - uint64_t); - CTL_M2_GET("stats.arenas.0.muzzy_purged", i, &muzzy_purged, uint64_t); - - emitter_row_t decay_row; - emitter_row_init(&decay_row); - - /* JSON-style emission. */ - emitter_json_kv(emitter, "dirty_decay_ms", emitter_type_ssize, - &dirty_decay_ms); - emitter_json_kv(emitter, "muzzy_decay_ms", emitter_type_ssize, - &muzzy_decay_ms); - - emitter_json_kv(emitter, "pactive", emitter_type_size, &pactive); - emitter_json_kv(emitter, "pdirty", emitter_type_size, &pdirty); - emitter_json_kv(emitter, "pmuzzy", emitter_type_size, &pmuzzy); - - emitter_json_kv(emitter, "dirty_npurge", emitter_type_uint64, - &dirty_npurge); - emitter_json_kv(emitter, "dirty_nmadvise", emitter_type_uint64, - &dirty_nmadvise); - emitter_json_kv(emitter, "dirty_purged", emitter_type_uint64, - &dirty_purged); - - emitter_json_kv(emitter, "muzzy_npurge", emitter_type_uint64, - &muzzy_npurge); - emitter_json_kv(emitter, "muzzy_nmadvise", emitter_type_uint64, - &muzzy_nmadvise); - emitter_json_kv(emitter, "muzzy_purged", emitter_type_uint64, - &muzzy_purged); - - /* Table-style emission. */ - COL(decay_row, decay_type, right, 9, title); - col_decay_type.str_val = "decaying:"; - - COL(decay_row, decay_time, right, 6, title); - col_decay_time.str_val = "time"; - - COL(decay_row, decay_npages, right, 13, title); - col_decay_npages.str_val = "npages"; - - COL(decay_row, decay_sweeps, right, 13, title); - col_decay_sweeps.str_val = "sweeps"; - - COL(decay_row, decay_madvises, right, 13, title); - col_decay_madvises.str_val = "madvises"; - - COL(decay_row, decay_purged, right, 13, title); - col_decay_purged.str_val = "purged"; - - /* Title row. */ - emitter_table_row(emitter, &decay_row); - - /* Dirty row. */ - col_decay_type.str_val = "dirty:"; - - if (dirty_decay_ms >= 0) { - col_decay_time.type = emitter_type_ssize; - col_decay_time.ssize_val = dirty_decay_ms; - } else { - col_decay_time.type = emitter_type_title; - col_decay_time.str_val = "N/A"; - } - - col_decay_npages.type = emitter_type_size; - col_decay_npages.size_val = pdirty; - - col_decay_sweeps.type = emitter_type_uint64; - col_decay_sweeps.uint64_val = dirty_npurge; - - col_decay_madvises.type = emitter_type_uint64; - col_decay_madvises.uint64_val = dirty_nmadvise; - - col_decay_purged.type = emitter_type_uint64; - col_decay_purged.uint64_val = dirty_purged; - - emitter_table_row(emitter, &decay_row); - - /* Muzzy row. */ - col_decay_type.str_val = "muzzy:"; - - if (muzzy_decay_ms >= 0) { - col_decay_time.type = emitter_type_ssize; - col_decay_time.ssize_val = muzzy_decay_ms; - } else { - col_decay_time.type = emitter_type_title; - col_decay_time.str_val = "N/A"; - } - - col_decay_npages.type = emitter_type_size; - col_decay_npages.size_val = pmuzzy; - - col_decay_sweeps.type = emitter_type_uint64; - col_decay_sweeps.uint64_val = muzzy_npurge; - - col_decay_madvises.type = emitter_type_uint64; - col_decay_madvises.uint64_val = muzzy_nmadvise; - - col_decay_purged.type = emitter_type_uint64; - col_decay_purged.uint64_val = muzzy_purged; - - emitter_table_row(emitter, &decay_row); - - /* Small / large / total allocation counts. */ - emitter_row_t alloc_count_row; - emitter_row_init(&alloc_count_row); - - COL(alloc_count_row, count_title, left, 21, title); - col_count_title.str_val = ""; - - COL(alloc_count_row, count_allocated, right, 16, title); - col_count_allocated.str_val = "allocated"; - - COL(alloc_count_row, count_nmalloc, right, 16, title); - col_count_nmalloc.str_val = "nmalloc"; - COL(alloc_count_row, count_nmalloc_ps, right, 10, title); - col_count_nmalloc_ps.str_val = "(#/sec)"; - - COL(alloc_count_row, count_ndalloc, right, 16, title); - col_count_ndalloc.str_val = "ndalloc"; - COL(alloc_count_row, count_ndalloc_ps, right, 10, title); - col_count_ndalloc_ps.str_val = "(#/sec)"; - - COL(alloc_count_row, count_nrequests, right, 16, title); - col_count_nrequests.str_val = "nrequests"; - COL(alloc_count_row, count_nrequests_ps, right, 10, title); - col_count_nrequests_ps.str_val = "(#/sec)"; - - COL(alloc_count_row, count_nfills, right, 16, title); - col_count_nfills.str_val = "nfill"; - COL(alloc_count_row, count_nfills_ps, right, 10, title); - col_count_nfills_ps.str_val = "(#/sec)"; - - COL(alloc_count_row, count_nflushes, right, 16, title); - col_count_nflushes.str_val = "nflush"; - COL(alloc_count_row, count_nflushes_ps, right, 10, title); - col_count_nflushes_ps.str_val = "(#/sec)"; - - emitter_table_row(emitter, &alloc_count_row); - - col_count_nmalloc_ps.type = emitter_type_uint64; - col_count_ndalloc_ps.type = emitter_type_uint64; - col_count_nrequests_ps.type = emitter_type_uint64; - col_count_nfills_ps.type = emitter_type_uint64; - col_count_nflushes_ps.type = emitter_type_uint64; - -#define GET_AND_EMIT_ALLOC_STAT(small_or_large, name, valtype) \ - CTL_M2_GET("stats.arenas.0." #small_or_large "." #name, i, \ - &small_or_large##_##name, valtype##_t); \ - emitter_json_kv(emitter, #name, emitter_type_##valtype, \ - &small_or_large##_##name); \ - col_count_##name.type = emitter_type_##valtype; \ - col_count_##name.valtype##_val = small_or_large##_##name; - - emitter_json_object_kv_begin(emitter, "small"); - col_count_title.str_val = "small:"; - - GET_AND_EMIT_ALLOC_STAT(small, allocated, size) - GET_AND_EMIT_ALLOC_STAT(small, nmalloc, uint64) - col_count_nmalloc_ps.uint64_val = - rate_per_second(col_count_nmalloc.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(small, ndalloc, uint64) - col_count_ndalloc_ps.uint64_val = - rate_per_second(col_count_ndalloc.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64) - col_count_nrequests_ps.uint64_val = - rate_per_second(col_count_nrequests.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(small, nfills, uint64) - col_count_nfills_ps.uint64_val = - rate_per_second(col_count_nfills.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(small, nflushes, uint64) - col_count_nflushes_ps.uint64_val = - rate_per_second(col_count_nflushes.uint64_val, uptime); - - emitter_table_row(emitter, &alloc_count_row); - emitter_json_object_end(emitter); /* Close "small". */ - - emitter_json_object_kv_begin(emitter, "large"); - col_count_title.str_val = "large:"; - - GET_AND_EMIT_ALLOC_STAT(large, allocated, size) - GET_AND_EMIT_ALLOC_STAT(large, nmalloc, uint64) - col_count_nmalloc_ps.uint64_val = - rate_per_second(col_count_nmalloc.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(large, ndalloc, uint64) - col_count_ndalloc_ps.uint64_val = - rate_per_second(col_count_ndalloc.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64) - col_count_nrequests_ps.uint64_val = - rate_per_second(col_count_nrequests.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(large, nfills, uint64) - col_count_nfills_ps.uint64_val = - rate_per_second(col_count_nfills.uint64_val, uptime); - GET_AND_EMIT_ALLOC_STAT(large, nflushes, uint64) - col_count_nflushes_ps.uint64_val = - rate_per_second(col_count_nflushes.uint64_val, uptime); - - emitter_table_row(emitter, &alloc_count_row); - emitter_json_object_end(emitter); /* Close "large". */ - -#undef GET_AND_EMIT_ALLOC_STAT - - /* Aggregated small + large stats are emitter only in table mode. */ - col_count_title.str_val = "total:"; - col_count_allocated.size_val = small_allocated + large_allocated; - col_count_nmalloc.uint64_val = small_nmalloc + large_nmalloc; - col_count_ndalloc.uint64_val = small_ndalloc + large_ndalloc; - col_count_nrequests.uint64_val = small_nrequests + large_nrequests; - col_count_nfills.uint64_val = small_nfills + large_nfills; - col_count_nflushes.uint64_val = small_nflushes + large_nflushes; - col_count_nmalloc_ps.uint64_val = - rate_per_second(col_count_nmalloc.uint64_val, uptime); - col_count_ndalloc_ps.uint64_val = - rate_per_second(col_count_ndalloc.uint64_val, uptime); - col_count_nrequests_ps.uint64_val = - rate_per_second(col_count_nrequests.uint64_val, uptime); - col_count_nfills_ps.uint64_val = - rate_per_second(col_count_nfills.uint64_val, uptime); - col_count_nflushes_ps.uint64_val = - rate_per_second(col_count_nflushes.uint64_val, uptime); - emitter_table_row(emitter, &alloc_count_row); - - emitter_row_t mem_count_row; - emitter_row_init(&mem_count_row); - - emitter_col_t mem_count_title; - emitter_col_init(&mem_count_title, &mem_count_row); - mem_count_title.justify = emitter_justify_left; - mem_count_title.width = 21; - mem_count_title.type = emitter_type_title; - mem_count_title.str_val = ""; - - emitter_col_t mem_count_val; - emitter_col_init(&mem_count_val, &mem_count_row); - mem_count_val.justify = emitter_justify_right; - mem_count_val.width = 16; - mem_count_val.type = emitter_type_title; - mem_count_val.str_val = ""; - - emitter_table_row(emitter, &mem_count_row); - mem_count_val.type = emitter_type_size; - - /* Active count in bytes is emitted only in table mode. */ - mem_count_title.str_val = "active:"; - mem_count_val.size_val = pactive * page; - emitter_table_row(emitter, &mem_count_row); - -#define GET_AND_EMIT_MEM_STAT(stat) \ - CTL_M2_GET("stats.arenas.0."#stat, i, &stat, size_t); \ - emitter_json_kv(emitter, #stat, emitter_type_size, &stat); \ - mem_count_title.str_val = #stat":"; \ - mem_count_val.size_val = stat; \ - emitter_table_row(emitter, &mem_count_row); - - GET_AND_EMIT_MEM_STAT(mapped) - GET_AND_EMIT_MEM_STAT(retained) - GET_AND_EMIT_MEM_STAT(base) - GET_AND_EMIT_MEM_STAT(internal) - GET_AND_EMIT_MEM_STAT(metadata_thp) - GET_AND_EMIT_MEM_STAT(tcache_bytes) - GET_AND_EMIT_MEM_STAT(tcache_stashed_bytes) - GET_AND_EMIT_MEM_STAT(resident) - GET_AND_EMIT_MEM_STAT(abandoned_vm) - GET_AND_EMIT_MEM_STAT(extent_avail) -#undef GET_AND_EMIT_MEM_STAT - - if (mutex) { - stats_arena_mutexes_print(emitter, i, uptime); - } - if (bins) { - stats_arena_bins_print(emitter, mutex, i, uptime); - } - if (large) { - stats_arena_lextents_print(emitter, i, uptime); - } - if (extents) { - stats_arena_extents_print(emitter, i); - } - if (hpa) { - stats_arena_hpa_shard_print(emitter, i, uptime); - } -} - -JEMALLOC_COLD -static void -stats_general_print(emitter_t *emitter) { - const char *cpv; - bool bv, bv2; - unsigned uv; - uint32_t u32v; - uint64_t u64v; - int64_t i64v; - ssize_t ssv, ssv2; - size_t sv, bsz, usz, u32sz, u64sz, i64sz, ssz, sssz, cpsz; - - bsz = sizeof(bool); - usz = sizeof(unsigned); - ssz = sizeof(size_t); - sssz = sizeof(ssize_t); - cpsz = sizeof(const char *); - u32sz = sizeof(uint32_t); - i64sz = sizeof(int64_t); - u64sz = sizeof(uint64_t); - - CTL_GET("version", &cpv, const char *); - emitter_kv(emitter, "version", "Version", emitter_type_string, &cpv); - - /* config. */ - emitter_dict_begin(emitter, "config", "Build-time option settings"); -#define CONFIG_WRITE_BOOL(name) \ - do { \ - CTL_GET("config."#name, &bv, bool); \ - emitter_kv(emitter, #name, "config."#name, \ - emitter_type_bool, &bv); \ - } while (0) - - CONFIG_WRITE_BOOL(cache_oblivious); - CONFIG_WRITE_BOOL(debug); - CONFIG_WRITE_BOOL(fill); - CONFIG_WRITE_BOOL(lazy_lock); - emitter_kv(emitter, "malloc_conf", "config.malloc_conf", - emitter_type_string, &config_malloc_conf); - - CONFIG_WRITE_BOOL(opt_safety_checks); - CONFIG_WRITE_BOOL(prof); - CONFIG_WRITE_BOOL(prof_libgcc); - CONFIG_WRITE_BOOL(prof_libunwind); - CONFIG_WRITE_BOOL(stats); - CONFIG_WRITE_BOOL(utrace); - CONFIG_WRITE_BOOL(xmalloc); -#undef CONFIG_WRITE_BOOL - emitter_dict_end(emitter); /* Close "config" dict. */ - - /* opt. */ -#define OPT_WRITE(name, var, size, emitter_type) \ - if (je_mallctl("opt."name, (void *)&var, &size, NULL, 0) == \ - 0) { \ - emitter_kv(emitter, name, "opt."name, emitter_type, \ - &var); \ - } - -#define OPT_WRITE_MUTABLE(name, var1, var2, size, emitter_type, \ - altname) \ - if (je_mallctl("opt."name, (void *)&var1, &size, NULL, 0) == \ - 0 && je_mallctl(altname, (void *)&var2, &size, NULL, 0) \ - == 0) { \ - emitter_kv_note(emitter, name, "opt."name, \ - emitter_type, &var1, altname, emitter_type, \ - &var2); \ - } - -#define OPT_WRITE_BOOL(name) OPT_WRITE(name, bv, bsz, emitter_type_bool) -#define OPT_WRITE_BOOL_MUTABLE(name, altname) \ - OPT_WRITE_MUTABLE(name, bv, bv2, bsz, emitter_type_bool, altname) - -#define OPT_WRITE_UNSIGNED(name) \ - OPT_WRITE(name, uv, usz, emitter_type_unsigned) - -#define OPT_WRITE_INT64(name) \ - OPT_WRITE(name, i64v, i64sz, emitter_type_int64) -#define OPT_WRITE_UINT64(name) \ - OPT_WRITE(name, u64v, u64sz, emitter_type_uint64) - -#define OPT_WRITE_SIZE_T(name) \ - OPT_WRITE(name, sv, ssz, emitter_type_size) -#define OPT_WRITE_SSIZE_T(name) \ - OPT_WRITE(name, ssv, sssz, emitter_type_ssize) -#define OPT_WRITE_SSIZE_T_MUTABLE(name, altname) \ - OPT_WRITE_MUTABLE(name, ssv, ssv2, sssz, emitter_type_ssize, \ - altname) - -#define OPT_WRITE_CHAR_P(name) \ - OPT_WRITE(name, cpv, cpsz, emitter_type_string) - - emitter_dict_begin(emitter, "opt", "Run-time option settings"); - - OPT_WRITE_BOOL("abort") - OPT_WRITE_BOOL("abort_conf") - OPT_WRITE_BOOL("cache_oblivious") - OPT_WRITE_BOOL("confirm_conf") - OPT_WRITE_BOOL("retain") - OPT_WRITE_CHAR_P("dss") - OPT_WRITE_UNSIGNED("narenas") - OPT_WRITE_CHAR_P("percpu_arena") - OPT_WRITE_SIZE_T("oversize_threshold") - OPT_WRITE_BOOL("hpa") - OPT_WRITE_SIZE_T("hpa_slab_max_alloc") - OPT_WRITE_SIZE_T("hpa_hugification_threshold") - OPT_WRITE_UINT64("hpa_hugify_delay_ms") - OPT_WRITE_UINT64("hpa_min_purge_interval_ms") - if (je_mallctl("opt.hpa_dirty_mult", (void *)&u32v, &u32sz, NULL, 0) - == 0) { - /* - * We cheat a little and "know" the secret meaning of this - * representation. - */ - if (u32v == (uint32_t)-1) { - const char *neg1 = "-1"; - emitter_kv(emitter, "hpa_dirty_mult", - "opt.hpa_dirty_mult", emitter_type_string, &neg1); - } else { - char buf[FXP_BUF_SIZE]; - fxp_print(u32v, buf); - const char *bufp = buf; - emitter_kv(emitter, "hpa_dirty_mult", - "opt.hpa_dirty_mult", emitter_type_string, &bufp); - } - } - OPT_WRITE_SIZE_T("hpa_sec_nshards") - OPT_WRITE_SIZE_T("hpa_sec_max_alloc") - OPT_WRITE_SIZE_T("hpa_sec_max_bytes") - OPT_WRITE_SIZE_T("hpa_sec_bytes_after_flush") - OPT_WRITE_SIZE_T("hpa_sec_batch_fill_extra") - OPT_WRITE_CHAR_P("metadata_thp") - OPT_WRITE_INT64("mutex_max_spin") - OPT_WRITE_BOOL_MUTABLE("background_thread", "background_thread") - OPT_WRITE_SSIZE_T_MUTABLE("dirty_decay_ms", "arenas.dirty_decay_ms") - OPT_WRITE_SSIZE_T_MUTABLE("muzzy_decay_ms", "arenas.muzzy_decay_ms") - OPT_WRITE_SIZE_T("lg_extent_max_active_fit") - OPT_WRITE_CHAR_P("junk") - OPT_WRITE_BOOL("zero") - OPT_WRITE_BOOL("utrace") - OPT_WRITE_BOOL("xmalloc") - OPT_WRITE_BOOL("experimental_infallible_new") - OPT_WRITE_BOOL("tcache") - OPT_WRITE_SIZE_T("tcache_max") - OPT_WRITE_UNSIGNED("tcache_nslots_small_min") - OPT_WRITE_UNSIGNED("tcache_nslots_small_max") - OPT_WRITE_UNSIGNED("tcache_nslots_large") - OPT_WRITE_SSIZE_T("lg_tcache_nslots_mul") - OPT_WRITE_SIZE_T("tcache_gc_incr_bytes") - OPT_WRITE_SIZE_T("tcache_gc_delay_bytes") - OPT_WRITE_UNSIGNED("lg_tcache_flush_small_div") - OPT_WRITE_UNSIGNED("lg_tcache_flush_large_div") - OPT_WRITE_CHAR_P("thp") - OPT_WRITE_BOOL("prof") - OPT_WRITE_CHAR_P("prof_prefix") - OPT_WRITE_BOOL_MUTABLE("prof_active", "prof.active") - OPT_WRITE_BOOL_MUTABLE("prof_thread_active_init", - "prof.thread_active_init") - OPT_WRITE_SSIZE_T_MUTABLE("lg_prof_sample", "prof.lg_sample") - OPT_WRITE_BOOL("prof_accum") - OPT_WRITE_SSIZE_T("lg_prof_interval") - OPT_WRITE_BOOL("prof_gdump") - OPT_WRITE_BOOL("prof_final") - OPT_WRITE_BOOL("prof_leak") - OPT_WRITE_BOOL("prof_leak_error") - OPT_WRITE_BOOL("stats_print") - OPT_WRITE_CHAR_P("stats_print_opts") - OPT_WRITE_BOOL("stats_print") - OPT_WRITE_CHAR_P("stats_print_opts") - OPT_WRITE_INT64("stats_interval") - OPT_WRITE_CHAR_P("stats_interval_opts") - OPT_WRITE_CHAR_P("zero_realloc") - - emitter_dict_end(emitter); - -#undef OPT_WRITE -#undef OPT_WRITE_MUTABLE -#undef OPT_WRITE_BOOL -#undef OPT_WRITE_BOOL_MUTABLE -#undef OPT_WRITE_UNSIGNED -#undef OPT_WRITE_SSIZE_T -#undef OPT_WRITE_SSIZE_T_MUTABLE -#undef OPT_WRITE_CHAR_P - - /* prof. */ - if (config_prof) { - emitter_dict_begin(emitter, "prof", "Profiling settings"); - - CTL_GET("prof.thread_active_init", &bv, bool); - emitter_kv(emitter, "thread_active_init", - "prof.thread_active_init", emitter_type_bool, &bv); - - CTL_GET("prof.active", &bv, bool); - emitter_kv(emitter, "active", "prof.active", emitter_type_bool, - &bv); - - CTL_GET("prof.gdump", &bv, bool); - emitter_kv(emitter, "gdump", "prof.gdump", emitter_type_bool, - &bv); - - CTL_GET("prof.interval", &u64v, uint64_t); - emitter_kv(emitter, "interval", "prof.interval", - emitter_type_uint64, &u64v); - - CTL_GET("prof.lg_sample", &ssv, ssize_t); - emitter_kv(emitter, "lg_sample", "prof.lg_sample", - emitter_type_ssize, &ssv); - - emitter_dict_end(emitter); /* Close "prof". */ - } - - /* arenas. */ - /* - * The json output sticks arena info into an "arenas" dict; the table - * output puts them at the top-level. - */ - emitter_json_object_kv_begin(emitter, "arenas"); - - CTL_GET("arenas.narenas", &uv, unsigned); - emitter_kv(emitter, "narenas", "Arenas", emitter_type_unsigned, &uv); - - /* - * Decay settings are emitted only in json mode; in table mode, they're - * emitted as notes with the opt output, above. - */ - CTL_GET("arenas.dirty_decay_ms", &ssv, ssize_t); - emitter_json_kv(emitter, "dirty_decay_ms", emitter_type_ssize, &ssv); - - CTL_GET("arenas.muzzy_decay_ms", &ssv, ssize_t); - emitter_json_kv(emitter, "muzzy_decay_ms", emitter_type_ssize, &ssv); - - CTL_GET("arenas.quantum", &sv, size_t); - emitter_kv(emitter, "quantum", "Quantum size", emitter_type_size, &sv); - - CTL_GET("arenas.page", &sv, size_t); - emitter_kv(emitter, "page", "Page size", emitter_type_size, &sv); - - if (je_mallctl("arenas.tcache_max", (void *)&sv, &ssz, NULL, 0) == 0) { - emitter_kv(emitter, "tcache_max", - "Maximum thread-cached size class", emitter_type_size, &sv); - } - - unsigned arenas_nbins; - CTL_GET("arenas.nbins", &arenas_nbins, unsigned); - emitter_kv(emitter, "nbins", "Number of bin size classes", - emitter_type_unsigned, &arenas_nbins); - - unsigned arenas_nhbins; - CTL_GET("arenas.nhbins", &arenas_nhbins, unsigned); - emitter_kv(emitter, "nhbins", "Number of thread-cache bin size classes", - emitter_type_unsigned, &arenas_nhbins); - - /* - * We do enough mallctls in a loop that we actually want to omit them - * (not just omit the printing). - */ - if (emitter_outputs_json(emitter)) { - emitter_json_array_kv_begin(emitter, "bin"); - size_t arenas_bin_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(arenas_bin_mib, 0, "arenas.bin"); - for (unsigned i = 0; i < arenas_nbins; i++) { - arenas_bin_mib[2] = i; - emitter_json_object_begin(emitter); - - CTL_LEAF(arenas_bin_mib, 3, "size", &sv, size_t); - emitter_json_kv(emitter, "size", emitter_type_size, - &sv); - - CTL_LEAF(arenas_bin_mib, 3, "nregs", &u32v, uint32_t); - emitter_json_kv(emitter, "nregs", emitter_type_uint32, - &u32v); - - CTL_LEAF(arenas_bin_mib, 3, "slab_size", &sv, size_t); - emitter_json_kv(emitter, "slab_size", emitter_type_size, - &sv); - - CTL_LEAF(arenas_bin_mib, 3, "nshards", &u32v, uint32_t); - emitter_json_kv(emitter, "nshards", emitter_type_uint32, - &u32v); - - emitter_json_object_end(emitter); - } - emitter_json_array_end(emitter); /* Close "bin". */ - } - - unsigned nlextents; - CTL_GET("arenas.nlextents", &nlextents, unsigned); - emitter_kv(emitter, "nlextents", "Number of large size classes", - emitter_type_unsigned, &nlextents); - - if (emitter_outputs_json(emitter)) { - emitter_json_array_kv_begin(emitter, "lextent"); - size_t arenas_lextent_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(arenas_lextent_mib, 0, "arenas.lextent"); - for (unsigned i = 0; i < nlextents; i++) { - arenas_lextent_mib[2] = i; - emitter_json_object_begin(emitter); - - CTL_LEAF(arenas_lextent_mib, 3, "size", &sv, size_t); - emitter_json_kv(emitter, "size", emitter_type_size, - &sv); - - emitter_json_object_end(emitter); - } - emitter_json_array_end(emitter); /* Close "lextent". */ - } - - emitter_json_object_end(emitter); /* Close "arenas" */ -} - -JEMALLOC_COLD -static void -stats_print_helper(emitter_t *emitter, bool merged, bool destroyed, - bool unmerged, bool bins, bool large, bool mutex, bool extents, bool hpa) { - /* - * These should be deleted. We keep them around for a while, to aid in - * the transition to the emitter code. - */ - size_t allocated, active, metadata, metadata_thp, resident, mapped, - retained; - size_t num_background_threads; - size_t zero_reallocs; - uint64_t background_thread_num_runs, background_thread_run_interval; - - CTL_GET("stats.allocated", &allocated, size_t); - CTL_GET("stats.active", &active, size_t); - CTL_GET("stats.metadata", &metadata, size_t); - CTL_GET("stats.metadata_thp", &metadata_thp, size_t); - CTL_GET("stats.resident", &resident, size_t); - CTL_GET("stats.mapped", &mapped, size_t); - CTL_GET("stats.retained", &retained, size_t); - - CTL_GET("stats.zero_reallocs", &zero_reallocs, size_t); - - if (have_background_thread) { - CTL_GET("stats.background_thread.num_threads", - &num_background_threads, size_t); - CTL_GET("stats.background_thread.num_runs", - &background_thread_num_runs, uint64_t); - CTL_GET("stats.background_thread.run_interval", - &background_thread_run_interval, uint64_t); - } else { - num_background_threads = 0; - background_thread_num_runs = 0; - background_thread_run_interval = 0; - } - - /* Generic global stats. */ - emitter_json_object_kv_begin(emitter, "stats"); - emitter_json_kv(emitter, "allocated", emitter_type_size, &allocated); - emitter_json_kv(emitter, "active", emitter_type_size, &active); - emitter_json_kv(emitter, "metadata", emitter_type_size, &metadata); - emitter_json_kv(emitter, "metadata_thp", emitter_type_size, - &metadata_thp); - emitter_json_kv(emitter, "resident", emitter_type_size, &resident); - emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped); - emitter_json_kv(emitter, "retained", emitter_type_size, &retained); - emitter_json_kv(emitter, "zero_reallocs", emitter_type_size, - &zero_reallocs); - - emitter_table_printf(emitter, "Allocated: %zu, active: %zu, " - "metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, " - "retained: %zu\n", allocated, active, metadata, metadata_thp, - resident, mapped, retained); - - /* Strange behaviors */ - emitter_table_printf(emitter, - "Count of realloc(non-null-ptr, 0) calls: %zu\n", zero_reallocs); - - /* Background thread stats. */ - emitter_json_object_kv_begin(emitter, "background_thread"); - emitter_json_kv(emitter, "num_threads", emitter_type_size, - &num_background_threads); - emitter_json_kv(emitter, "num_runs", emitter_type_uint64, - &background_thread_num_runs); - emitter_json_kv(emitter, "run_interval", emitter_type_uint64, - &background_thread_run_interval); - emitter_json_object_end(emitter); /* Close "background_thread". */ - - emitter_table_printf(emitter, "Background threads: %zu, " - "num_runs: %"FMTu64", run_interval: %"FMTu64" ns\n", - num_background_threads, background_thread_num_runs, - background_thread_run_interval); - - if (mutex) { - emitter_row_t row; - emitter_col_t name; - emitter_col_t col64[mutex_prof_num_uint64_t_counters]; - emitter_col_t col32[mutex_prof_num_uint32_t_counters]; - uint64_t uptime; - - emitter_row_init(&row); - mutex_stats_init_cols(&row, "", &name, col64, col32); - - emitter_table_row(emitter, &row); - emitter_json_object_kv_begin(emitter, "mutexes"); - - CTL_M2_GET("stats.arenas.0.uptime", 0, &uptime, uint64_t); - - size_t stats_mutexes_mib[CTL_MAX_DEPTH]; - CTL_LEAF_PREPARE(stats_mutexes_mib, 0, "stats.mutexes"); - for (int i = 0; i < mutex_prof_num_global_mutexes; i++) { - mutex_stats_read_global(stats_mutexes_mib, 2, - global_mutex_names[i], &name, col64, col32, uptime); - emitter_json_object_kv_begin(emitter, global_mutex_names[i]); - mutex_stats_emit(emitter, &row, col64, col32); - emitter_json_object_end(emitter); - } - - emitter_json_object_end(emitter); /* Close "mutexes". */ - } - - emitter_json_object_end(emitter); /* Close "stats". */ - - if (merged || destroyed || unmerged) { - unsigned narenas; - - emitter_json_object_kv_begin(emitter, "stats.arenas"); - - CTL_GET("arenas.narenas", &narenas, unsigned); - size_t mib[3]; - size_t miblen = sizeof(mib) / sizeof(size_t); - size_t sz; - VARIABLE_ARRAY(bool, initialized, narenas); - bool destroyed_initialized; - unsigned i, j, ninitialized; - - xmallctlnametomib("arena.0.initialized", mib, &miblen); - for (i = ninitialized = 0; i < narenas; i++) { - mib[1] = i; - sz = sizeof(bool); - xmallctlbymib(mib, miblen, &initialized[i], &sz, - NULL, 0); - if (initialized[i]) { - ninitialized++; - } - } - mib[1] = MALLCTL_ARENAS_DESTROYED; - sz = sizeof(bool); - xmallctlbymib(mib, miblen, &destroyed_initialized, &sz, - NULL, 0); - - /* Merged stats. */ - if (merged && (ninitialized > 1 || !unmerged)) { - /* Print merged arena stats. */ - emitter_table_printf(emitter, "Merged arenas stats:\n"); - emitter_json_object_kv_begin(emitter, "merged"); - stats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins, - large, mutex, extents, hpa); - emitter_json_object_end(emitter); /* Close "merged". */ - } - - /* Destroyed stats. */ - if (destroyed_initialized && destroyed) { - /* Print destroyed arena stats. */ - emitter_table_printf(emitter, - "Destroyed arenas stats:\n"); - emitter_json_object_kv_begin(emitter, "destroyed"); - stats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED, - bins, large, mutex, extents, hpa); - emitter_json_object_end(emitter); /* Close "destroyed". */ - } - - /* Unmerged stats. */ - if (unmerged) { - for (i = j = 0; i < narenas; i++) { - if (initialized[i]) { - char arena_ind_str[20]; - malloc_snprintf(arena_ind_str, - sizeof(arena_ind_str), "%u", i); - emitter_json_object_kv_begin(emitter, - arena_ind_str); - emitter_table_printf(emitter, - "arenas[%s]:\n", arena_ind_str); - stats_arena_print(emitter, i, bins, - large, mutex, extents, hpa); - /* Close "<arena-ind>". */ - emitter_json_object_end(emitter); - } - } - } - emitter_json_object_end(emitter); /* Close "stats.arenas". */ - } -} - -void -stats_print(write_cb_t *write_cb, void *cbopaque, const char *opts) { - int err; - uint64_t epoch; - size_t u64sz; -#define OPTION(o, v, d, s) bool v = d; - STATS_PRINT_OPTIONS -#undef OPTION - - /* - * Refresh stats, in case mallctl() was called by the application. - * - * Check for OOM here, since refreshing the ctl cache can trigger - * allocation. In practice, none of the subsequent mallctl()-related - * calls in this function will cause OOM if this one succeeds. - * */ - epoch = 1; - u64sz = sizeof(uint64_t); - err = je_mallctl("epoch", (void *)&epoch, &u64sz, (void *)&epoch, - sizeof(uint64_t)); - if (err != 0) { - if (err == EAGAIN) { - malloc_write("<jemalloc>: Memory allocation failure in " - "mallctl(\"epoch\", ...)\n"); - return; - } - malloc_write("<jemalloc>: Failure in mallctl(\"epoch\", " - "...)\n"); - abort(); - } - - if (opts != NULL) { - for (unsigned i = 0; opts[i] != '\0'; i++) { - switch (opts[i]) { -#define OPTION(o, v, d, s) case o: v = s; break; - STATS_PRINT_OPTIONS -#undef OPTION - default:; - } - } - } - - emitter_t emitter; - emitter_init(&emitter, - json ? emitter_output_json_compact : emitter_output_table, - write_cb, cbopaque); - emitter_begin(&emitter); - emitter_table_printf(&emitter, "___ Begin jemalloc statistics ___\n"); - emitter_json_object_kv_begin(&emitter, "jemalloc"); - - if (general) { - stats_general_print(&emitter); - } - if (config_stats) { - stats_print_helper(&emitter, merged, destroyed, unmerged, - bins, large, mutex, extents, hpa); - } - - emitter_json_object_end(&emitter); /* Closes the "jemalloc" dict. */ - emitter_table_printf(&emitter, "--- End jemalloc statistics ---\n"); - emitter_end(&emitter); -} - -uint64_t -stats_interval_new_event_wait(tsd_t *tsd) { - return stats_interval_accum_batch; -} - -uint64_t -stats_interval_postponed_event_wait(tsd_t *tsd) { - return TE_MIN_START_WAIT; -} - -void -stats_interval_event_handler(tsd_t *tsd, uint64_t elapsed) { - assert(elapsed > 0 && elapsed != TE_INVALID_ELAPSED); - if (counter_accum(tsd_tsdn(tsd), &stats_interval_accumulated, - elapsed)) { - je_malloc_stats_print(NULL, NULL, opt_stats_interval_opts); - } -} - -bool -stats_boot(void) { - uint64_t stats_interval; - if (opt_stats_interval < 0) { - assert(opt_stats_interval == -1); - stats_interval = 0; - stats_interval_accum_batch = 0; - } else{ - /* See comments in stats.h */ - stats_interval = (opt_stats_interval > 0) ? - opt_stats_interval : 1; - uint64_t batch = stats_interval >> - STATS_INTERVAL_ACCUM_LG_BATCH_SIZE; - if (batch > STATS_INTERVAL_ACCUM_BATCH_MAX) { - batch = STATS_INTERVAL_ACCUM_BATCH_MAX; - } else if (batch == 0) { - batch = 1; - } - stats_interval_accum_batch = batch; - } - - return counter_accum_init(&stats_interval_accumulated, stats_interval); -} - -void -stats_prefork(tsdn_t *tsdn) { - counter_prefork(tsdn, &stats_interval_accumulated); -} - -void -stats_postfork_parent(tsdn_t *tsdn) { - counter_postfork_parent(tsdn, &stats_interval_accumulated); -} - -void -stats_postfork_child(tsdn_t *tsdn) { - counter_postfork_child(tsdn, &stats_interval_accumulated); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/sz.c b/fluent-bit/lib/jemalloc-5.3.0/src/sz.c deleted file mode 100644 index d3115dda..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/sz.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" -#include "jemalloc/internal/sz.h" - -JEMALLOC_ALIGNED(CACHELINE) -size_t sz_pind2sz_tab[SC_NPSIZES+1]; -size_t sz_large_pad; - -size_t -sz_psz_quantize_floor(size_t size) { - size_t ret; - pszind_t pind; - - assert(size > 0); - assert((size & PAGE_MASK) == 0); - - pind = sz_psz2ind(size - sz_large_pad + 1); - if (pind == 0) { - /* - * Avoid underflow. This short-circuit would also do the right - * thing for all sizes in the range for which there are - * PAGE-spaced size classes, but it's simplest to just handle - * the one case that would cause erroneous results. - */ - return size; - } - ret = sz_pind2sz(pind - 1) + sz_large_pad; - assert(ret <= size); - return ret; -} - -size_t -sz_psz_quantize_ceil(size_t size) { - size_t ret; - - assert(size > 0); - assert(size - sz_large_pad <= SC_LARGE_MAXCLASS); - assert((size & PAGE_MASK) == 0); - - ret = sz_psz_quantize_floor(size); - if (ret < size) { - /* - * Skip a quantization that may have an adequately large extent, - * because under-sized extents may be mixed in. This only - * happens when an unusual size is requested, i.e. for aligned - * allocation, and is just one of several places where linear - * search would potentially find sufficiently aligned available - * memory somewhere lower. - */ - ret = sz_pind2sz(sz_psz2ind(ret - sz_large_pad + 1)) + - sz_large_pad; - } - return ret; -} - -static void -sz_boot_pind2sz_tab(const sc_data_t *sc_data) { - int pind = 0; - for (unsigned i = 0; i < SC_NSIZES; i++) { - const sc_t *sc = &sc_data->sc[i]; - if (sc->psz) { - sz_pind2sz_tab[pind] = (ZU(1) << sc->lg_base) - + (ZU(sc->ndelta) << sc->lg_delta); - pind++; - } - } - for (int i = pind; i <= (int)SC_NPSIZES; i++) { - sz_pind2sz_tab[pind] = sc_data->large_maxclass + PAGE; - } -} - -JEMALLOC_ALIGNED(CACHELINE) -size_t sz_index2size_tab[SC_NSIZES]; - -static void -sz_boot_index2size_tab(const sc_data_t *sc_data) { - for (unsigned i = 0; i < SC_NSIZES; i++) { - const sc_t *sc = &sc_data->sc[i]; - sz_index2size_tab[i] = (ZU(1) << sc->lg_base) - + (ZU(sc->ndelta) << (sc->lg_delta)); - } -} - -/* - * To keep this table small, we divide sizes by the tiny min size, which gives - * the smallest interval for which the result can change. - */ -JEMALLOC_ALIGNED(CACHELINE) -uint8_t sz_size2index_tab[(SC_LOOKUP_MAXCLASS >> SC_LG_TINY_MIN) + 1]; - -static void -sz_boot_size2index_tab(const sc_data_t *sc_data) { - size_t dst_max = (SC_LOOKUP_MAXCLASS >> SC_LG_TINY_MIN) + 1; - size_t dst_ind = 0; - for (unsigned sc_ind = 0; sc_ind < SC_NSIZES && dst_ind < dst_max; - sc_ind++) { - const sc_t *sc = &sc_data->sc[sc_ind]; - size_t sz = (ZU(1) << sc->lg_base) - + (ZU(sc->ndelta) << sc->lg_delta); - size_t max_ind = ((sz + (ZU(1) << SC_LG_TINY_MIN) - 1) - >> SC_LG_TINY_MIN); - for (; dst_ind <= max_ind && dst_ind < dst_max; dst_ind++) { - sz_size2index_tab[dst_ind] = sc_ind; - } - } -} - -void -sz_boot(const sc_data_t *sc_data, bool cache_oblivious) { - sz_large_pad = cache_oblivious ? PAGE : 0; - sz_boot_pind2sz_tab(sc_data); - sz_boot_index2size_tab(sc_data); - sz_boot_size2index_tab(sc_data); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/tcache.c b/fluent-bit/lib/jemalloc-5.3.0/src/tcache.c deleted file mode 100644 index fa16732e..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/tcache.c +++ /dev/null @@ -1,1101 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/safety_check.h" -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/sc.h" - -/******************************************************************************/ -/* Data. */ - -bool opt_tcache = true; - -/* tcache_maxclass is set to 32KB by default. */ -size_t opt_tcache_max = ((size_t)1) << 15; - -/* Reasonable defaults for min and max values. */ -unsigned opt_tcache_nslots_small_min = 20; -unsigned opt_tcache_nslots_small_max = 200; -unsigned opt_tcache_nslots_large = 20; - -/* - * We attempt to make the number of slots in a tcache bin for a given size class - * equal to the number of objects in a slab times some multiplier. By default, - * the multiplier is 2 (i.e. we set the maximum number of objects in the tcache - * to twice the number of objects in a slab). - * This is bounded by some other constraints as well, like the fact that it - * must be even, must be less than opt_tcache_nslots_small_max, etc.. - */ -ssize_t opt_lg_tcache_nslots_mul = 1; - -/* - * Number of allocation bytes between tcache incremental GCs. Again, this - * default just seems to work well; more tuning is possible. - */ -size_t opt_tcache_gc_incr_bytes = 65536; - -/* - * With default settings, we may end up flushing small bins frequently with - * small flush amounts. To limit this tendency, we can set a number of bytes to - * "delay" by. If we try to flush N M-byte items, we decrease that size-class's - * delay by N * M. So, if delay is 1024 and we're looking at the 64-byte size - * class, we won't do any flushing until we've been asked to flush 1024/64 == 16 - * items. This can happen in any configuration (i.e. being asked to flush 16 - * items once, or 4 items 4 times). - * - * Practically, this is stored as a count of items in a uint8_t, so the - * effective maximum value for a size class is 255 * sz. - */ -size_t opt_tcache_gc_delay_bytes = 0; - -/* - * When a cache bin is flushed because it's full, how much of it do we flush? - * By default, we flush half the maximum number of items. - */ -unsigned opt_lg_tcache_flush_small_div = 1; -unsigned opt_lg_tcache_flush_large_div = 1; - -cache_bin_info_t *tcache_bin_info; - -/* Total stack size required (per tcache). Include the padding above. */ -static size_t tcache_bin_alloc_size; -static size_t tcache_bin_alloc_alignment; - -/* Number of cache bins enabled, including both large and small. */ -unsigned nhbins; -/* Max size class to be cached (can be small or large). */ -size_t tcache_maxclass; - -tcaches_t *tcaches; - -/* Index of first element within tcaches that has never been used. */ -static unsigned tcaches_past; - -/* Head of singly linked list tracking available tcaches elements. */ -static tcaches_t *tcaches_avail; - -/* Protects tcaches{,_past,_avail}. */ -static malloc_mutex_t tcaches_mtx; - -/******************************************************************************/ - -size_t -tcache_salloc(tsdn_t *tsdn, const void *ptr) { - return arena_salloc(tsdn, ptr); -} - -uint64_t -tcache_gc_new_event_wait(tsd_t *tsd) { - return opt_tcache_gc_incr_bytes; -} - -uint64_t -tcache_gc_postponed_event_wait(tsd_t *tsd) { - return TE_MIN_START_WAIT; -} - -uint64_t -tcache_gc_dalloc_new_event_wait(tsd_t *tsd) { - return opt_tcache_gc_incr_bytes; -} - -uint64_t -tcache_gc_dalloc_postponed_event_wait(tsd_t *tsd) { - return TE_MIN_START_WAIT; -} - -static uint8_t -tcache_gc_item_delay_compute(szind_t szind) { - assert(szind < SC_NBINS); - size_t sz = sz_index2size(szind); - size_t item_delay = opt_tcache_gc_delay_bytes / sz; - size_t delay_max = ZU(1) - << (sizeof(((tcache_slow_t *)NULL)->bin_flush_delay_items[0]) * 8); - if (item_delay >= delay_max) { - item_delay = delay_max - 1; - } - return (uint8_t)item_delay; -} - -static void -tcache_gc_small(tsd_t *tsd, tcache_slow_t *tcache_slow, tcache_t *tcache, - szind_t szind) { - /* Aim to flush 3/4 of items below low-water. */ - assert(szind < SC_NBINS); - - cache_bin_t *cache_bin = &tcache->bins[szind]; - cache_bin_sz_t ncached = cache_bin_ncached_get_local(cache_bin, - &tcache_bin_info[szind]); - cache_bin_sz_t low_water = cache_bin_low_water_get(cache_bin, - &tcache_bin_info[szind]); - assert(!tcache_slow->bin_refilled[szind]); - - size_t nflush = low_water - (low_water >> 2); - if (nflush < tcache_slow->bin_flush_delay_items[szind]) { - /* Workaround for a conversion warning. */ - uint8_t nflush_uint8 = (uint8_t)nflush; - assert(sizeof(tcache_slow->bin_flush_delay_items[0]) == - sizeof(nflush_uint8)); - tcache_slow->bin_flush_delay_items[szind] -= nflush_uint8; - return; - } else { - tcache_slow->bin_flush_delay_items[szind] - = tcache_gc_item_delay_compute(szind); - } - - tcache_bin_flush_small(tsd, tcache, cache_bin, szind, - (unsigned)(ncached - nflush)); - - /* - * Reduce fill count by 2X. Limit lg_fill_div such that - * the fill count is always at least 1. - */ - if ((cache_bin_info_ncached_max(&tcache_bin_info[szind]) - >> (tcache_slow->lg_fill_div[szind] + 1)) >= 1) { - tcache_slow->lg_fill_div[szind]++; - } -} - -static void -tcache_gc_large(tsd_t *tsd, tcache_slow_t *tcache_slow, tcache_t *tcache, - szind_t szind) { - /* Like the small GC; flush 3/4 of untouched items. */ - assert(szind >= SC_NBINS); - cache_bin_t *cache_bin = &tcache->bins[szind]; - cache_bin_sz_t ncached = cache_bin_ncached_get_local(cache_bin, - &tcache_bin_info[szind]); - cache_bin_sz_t low_water = cache_bin_low_water_get(cache_bin, - &tcache_bin_info[szind]); - tcache_bin_flush_large(tsd, tcache, cache_bin, szind, - (unsigned)(ncached - low_water + (low_water >> 2))); -} - -static void -tcache_event(tsd_t *tsd) { - tcache_t *tcache = tcache_get(tsd); - if (tcache == NULL) { - return; - } - - tcache_slow_t *tcache_slow = tsd_tcache_slowp_get(tsd); - szind_t szind = tcache_slow->next_gc_bin; - bool is_small = (szind < SC_NBINS); - cache_bin_t *cache_bin = &tcache->bins[szind]; - - tcache_bin_flush_stashed(tsd, tcache, cache_bin, szind, is_small); - - cache_bin_sz_t low_water = cache_bin_low_water_get(cache_bin, - &tcache_bin_info[szind]); - if (low_water > 0) { - if (is_small) { - tcache_gc_small(tsd, tcache_slow, tcache, szind); - } else { - tcache_gc_large(tsd, tcache_slow, tcache, szind); - } - } else if (is_small && tcache_slow->bin_refilled[szind]) { - assert(low_water == 0); - /* - * Increase fill count by 2X for small bins. Make sure - * lg_fill_div stays greater than 0. - */ - if (tcache_slow->lg_fill_div[szind] > 1) { - tcache_slow->lg_fill_div[szind]--; - } - tcache_slow->bin_refilled[szind] = false; - } - cache_bin_low_water_set(cache_bin); - - tcache_slow->next_gc_bin++; - if (tcache_slow->next_gc_bin == nhbins) { - tcache_slow->next_gc_bin = 0; - } -} - -void -tcache_gc_event_handler(tsd_t *tsd, uint64_t elapsed) { - assert(elapsed == TE_INVALID_ELAPSED); - tcache_event(tsd); -} - -void -tcache_gc_dalloc_event_handler(tsd_t *tsd, uint64_t elapsed) { - assert(elapsed == TE_INVALID_ELAPSED); - tcache_event(tsd); -} - -void * -tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, - tcache_t *tcache, cache_bin_t *cache_bin, szind_t binind, - bool *tcache_success) { - tcache_slow_t *tcache_slow = tcache->tcache_slow; - void *ret; - - assert(tcache_slow->arena != NULL); - unsigned nfill = cache_bin_info_ncached_max(&tcache_bin_info[binind]) - >> tcache_slow->lg_fill_div[binind]; - arena_cache_bin_fill_small(tsdn, arena, cache_bin, - &tcache_bin_info[binind], binind, nfill); - tcache_slow->bin_refilled[binind] = true; - ret = cache_bin_alloc(cache_bin, tcache_success); - - return ret; -} - -static const void * -tcache_bin_flush_ptr_getter(void *arr_ctx, size_t ind) { - cache_bin_ptr_array_t *arr = (cache_bin_ptr_array_t *)arr_ctx; - return arr->ptr[ind]; -} - -static void -tcache_bin_flush_metadata_visitor(void *szind_sum_ctx, - emap_full_alloc_ctx_t *alloc_ctx) { - size_t *szind_sum = (size_t *)szind_sum_ctx; - *szind_sum -= alloc_ctx->szind; - util_prefetch_write_range(alloc_ctx->edata, sizeof(edata_t)); -} - -JEMALLOC_NOINLINE static void -tcache_bin_flush_size_check_fail(cache_bin_ptr_array_t *arr, szind_t szind, - size_t nptrs, emap_batch_lookup_result_t *edatas) { - bool found_mismatch = false; - for (size_t i = 0; i < nptrs; i++) { - szind_t true_szind = edata_szind_get(edatas[i].edata); - if (true_szind != szind) { - found_mismatch = true; - safety_check_fail_sized_dealloc( - /* current_dealloc */ false, - /* ptr */ tcache_bin_flush_ptr_getter(arr, i), - /* true_size */ sz_index2size(true_szind), - /* input_size */ sz_index2size(szind)); - } - } - assert(found_mismatch); -} - -static void -tcache_bin_flush_edatas_lookup(tsd_t *tsd, cache_bin_ptr_array_t *arr, - szind_t binind, size_t nflush, emap_batch_lookup_result_t *edatas) { - - /* - * This gets compiled away when config_opt_safety_checks is false. - * Checks for sized deallocation bugs, failing early rather than - * corrupting metadata. - */ - size_t szind_sum = binind * nflush; - emap_edata_lookup_batch(tsd, &arena_emap_global, nflush, - &tcache_bin_flush_ptr_getter, (void *)arr, - &tcache_bin_flush_metadata_visitor, (void *)&szind_sum, - edatas); - if (config_opt_safety_checks && unlikely(szind_sum != 0)) { - tcache_bin_flush_size_check_fail(arr, binind, nflush, edatas); - } -} - -JEMALLOC_ALWAYS_INLINE bool -tcache_bin_flush_match(edata_t *edata, unsigned cur_arena_ind, - unsigned cur_binshard, bool small) { - if (small) { - return edata_arena_ind_get(edata) == cur_arena_ind - && edata_binshard_get(edata) == cur_binshard; - } else { - return edata_arena_ind_get(edata) == cur_arena_ind; - } -} - -JEMALLOC_ALWAYS_INLINE void -tcache_bin_flush_impl(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin, - szind_t binind, cache_bin_ptr_array_t *ptrs, unsigned nflush, bool small) { - tcache_slow_t *tcache_slow = tcache->tcache_slow; - /* - * A couple lookup calls take tsdn; declare it once for convenience - * instead of calling tsd_tsdn(tsd) all the time. - */ - tsdn_t *tsdn = tsd_tsdn(tsd); - - if (small) { - assert(binind < SC_NBINS); - } else { - assert(binind < nhbins); - } - arena_t *tcache_arena = tcache_slow->arena; - assert(tcache_arena != NULL); - - /* - * Variable length array must have > 0 length; the last element is never - * touched (it's just included to satisfy the no-zero-length rule). - */ - VARIABLE_ARRAY(emap_batch_lookup_result_t, item_edata, nflush + 1); - tcache_bin_flush_edatas_lookup(tsd, ptrs, binind, nflush, item_edata); - - /* - * The slabs where we freed the last remaining object in the slab (and - * so need to free the slab itself). - * Used only if small == true. - */ - unsigned dalloc_count = 0; - VARIABLE_ARRAY(edata_t *, dalloc_slabs, nflush + 1); - - /* - * We're about to grab a bunch of locks. If one of them happens to be - * the one guarding the arena-level stats counters we flush our - * thread-local ones to, we do so under one critical section. - */ - bool merged_stats = false; - while (nflush > 0) { - /* Lock the arena, or bin, associated with the first object. */ - edata_t *edata = item_edata[0].edata; - unsigned cur_arena_ind = edata_arena_ind_get(edata); - arena_t *cur_arena = arena_get(tsdn, cur_arena_ind, false); - - /* - * These assignments are always overwritten when small is true, - * and their values are always ignored when small is false, but - * to avoid the technical UB when we pass them as parameters, we - * need to intialize them. - */ - unsigned cur_binshard = 0; - bin_t *cur_bin = NULL; - if (small) { - cur_binshard = edata_binshard_get(edata); - cur_bin = arena_get_bin(cur_arena, binind, - cur_binshard); - assert(cur_binshard < bin_infos[binind].n_shards); - /* - * If you're looking at profiles, you might think this - * is a good place to prefetch the bin stats, which are - * often a cache miss. This turns out not to be - * helpful on the workloads we've looked at, with moving - * the bin stats next to the lock seeming to do better. - */ - } - - if (small) { - malloc_mutex_lock(tsdn, &cur_bin->lock); - } - if (!small && !arena_is_auto(cur_arena)) { - malloc_mutex_lock(tsdn, &cur_arena->large_mtx); - } - - /* - * If we acquired the right lock and have some stats to flush, - * flush them. - */ - if (config_stats && tcache_arena == cur_arena - && !merged_stats) { - merged_stats = true; - if (small) { - cur_bin->stats.nflushes++; - cur_bin->stats.nrequests += - cache_bin->tstats.nrequests; - cache_bin->tstats.nrequests = 0; - } else { - arena_stats_large_flush_nrequests_add(tsdn, - &tcache_arena->stats, binind, - cache_bin->tstats.nrequests); - cache_bin->tstats.nrequests = 0; - } - } - - /* - * Large allocations need special prep done. Afterwards, we can - * drop the large lock. - */ - if (!small) { - for (unsigned i = 0; i < nflush; i++) { - void *ptr = ptrs->ptr[i]; - edata = item_edata[i].edata; - assert(ptr != NULL && edata != NULL); - - if (tcache_bin_flush_match(edata, cur_arena_ind, - cur_binshard, small)) { - large_dalloc_prep_locked(tsdn, - edata); - } - } - } - if (!small && !arena_is_auto(cur_arena)) { - malloc_mutex_unlock(tsdn, &cur_arena->large_mtx); - } - - /* Deallocate whatever we can. */ - unsigned ndeferred = 0; - /* Init only to avoid used-uninitialized warning. */ - arena_dalloc_bin_locked_info_t dalloc_bin_info = {0}; - if (small) { - arena_dalloc_bin_locked_begin(&dalloc_bin_info, binind); - } - for (unsigned i = 0; i < nflush; i++) { - void *ptr = ptrs->ptr[i]; - edata = item_edata[i].edata; - assert(ptr != NULL && edata != NULL); - if (!tcache_bin_flush_match(edata, cur_arena_ind, - cur_binshard, small)) { - /* - * The object was allocated either via a - * different arena, or a different bin in this - * arena. Either way, stash the object so that - * it can be handled in a future pass. - */ - ptrs->ptr[ndeferred] = ptr; - item_edata[ndeferred].edata = edata; - ndeferred++; - continue; - } - if (small) { - if (arena_dalloc_bin_locked_step(tsdn, - cur_arena, cur_bin, &dalloc_bin_info, - binind, edata, ptr)) { - dalloc_slabs[dalloc_count] = edata; - dalloc_count++; - } - } else { - if (large_dalloc_safety_checks(edata, ptr, - binind)) { - /* See the comment in isfree. */ - continue; - } - large_dalloc_finish(tsdn, edata); - } - } - - if (small) { - arena_dalloc_bin_locked_finish(tsdn, cur_arena, cur_bin, - &dalloc_bin_info); - malloc_mutex_unlock(tsdn, &cur_bin->lock); - } - arena_decay_ticks(tsdn, cur_arena, nflush - ndeferred); - nflush = ndeferred; - } - - /* Handle all deferred slab dalloc. */ - assert(small || dalloc_count == 0); - for (unsigned i = 0; i < dalloc_count; i++) { - edata_t *slab = dalloc_slabs[i]; - arena_slab_dalloc(tsdn, arena_get_from_edata(slab), slab); - - } - - if (config_stats && !merged_stats) { - if (small) { - /* - * The flush loop didn't happen to flush to this - * thread's arena, so the stats didn't get merged. - * Manually do so now. - */ - bin_t *bin = arena_bin_choose(tsdn, tcache_arena, - binind, NULL); - malloc_mutex_lock(tsdn, &bin->lock); - bin->stats.nflushes++; - bin->stats.nrequests += cache_bin->tstats.nrequests; - cache_bin->tstats.nrequests = 0; - malloc_mutex_unlock(tsdn, &bin->lock); - } else { - arena_stats_large_flush_nrequests_add(tsdn, - &tcache_arena->stats, binind, - cache_bin->tstats.nrequests); - cache_bin->tstats.nrequests = 0; - } - } - -} - -JEMALLOC_ALWAYS_INLINE void -tcache_bin_flush_bottom(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin, - szind_t binind, unsigned rem, bool small) { - tcache_bin_flush_stashed(tsd, tcache, cache_bin, binind, small); - - cache_bin_sz_t ncached = cache_bin_ncached_get_local(cache_bin, - &tcache_bin_info[binind]); - assert((cache_bin_sz_t)rem <= ncached); - unsigned nflush = ncached - rem; - - CACHE_BIN_PTR_ARRAY_DECLARE(ptrs, nflush); - cache_bin_init_ptr_array_for_flush(cache_bin, &tcache_bin_info[binind], - &ptrs, nflush); - - tcache_bin_flush_impl(tsd, tcache, cache_bin, binind, &ptrs, nflush, - small); - - cache_bin_finish_flush(cache_bin, &tcache_bin_info[binind], &ptrs, - ncached - rem); -} - -void -tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin, - szind_t binind, unsigned rem) { - tcache_bin_flush_bottom(tsd, tcache, cache_bin, binind, rem, true); -} - -void -tcache_bin_flush_large(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin, - szind_t binind, unsigned rem) { - tcache_bin_flush_bottom(tsd, tcache, cache_bin, binind, rem, false); -} - -/* - * Flushing stashed happens when 1) tcache fill, 2) tcache flush, or 3) tcache - * GC event. This makes sure that the stashed items do not hold memory for too - * long, and new buffers can only be allocated when nothing is stashed. - * - * The downside is, the time between stash and flush may be relatively short, - * especially when the request rate is high. It lowers the chance of detecting - * write-after-free -- however that is a delayed detection anyway, and is less - * of a focus than the memory overhead. - */ -void -tcache_bin_flush_stashed(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin, - szind_t binind, bool is_small) { - cache_bin_info_t *info = &tcache_bin_info[binind]; - /* - * The two below are for assertion only. The content of original cached - * items remain unchanged -- the stashed items reside on the other end - * of the stack. Checking the stack head and ncached to verify. - */ - void *head_content = *cache_bin->stack_head; - cache_bin_sz_t orig_cached = cache_bin_ncached_get_local(cache_bin, - info); - - cache_bin_sz_t nstashed = cache_bin_nstashed_get_local(cache_bin, info); - assert(orig_cached + nstashed <= cache_bin_info_ncached_max(info)); - if (nstashed == 0) { - return; - } - - CACHE_BIN_PTR_ARRAY_DECLARE(ptrs, nstashed); - cache_bin_init_ptr_array_for_stashed(cache_bin, binind, info, &ptrs, - nstashed); - san_check_stashed_ptrs(ptrs.ptr, nstashed, sz_index2size(binind)); - tcache_bin_flush_impl(tsd, tcache, cache_bin, binind, &ptrs, nstashed, - is_small); - cache_bin_finish_flush_stashed(cache_bin, info); - - assert(cache_bin_nstashed_get_local(cache_bin, info) == 0); - assert(cache_bin_ncached_get_local(cache_bin, info) == orig_cached); - assert(head_content == *cache_bin->stack_head); -} - -void -tcache_arena_associate(tsdn_t *tsdn, tcache_slow_t *tcache_slow, - tcache_t *tcache, arena_t *arena) { - assert(tcache_slow->arena == NULL); - tcache_slow->arena = arena; - - if (config_stats) { - /* Link into list of extant tcaches. */ - malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); - - ql_elm_new(tcache_slow, link); - ql_tail_insert(&arena->tcache_ql, tcache_slow, link); - cache_bin_array_descriptor_init( - &tcache_slow->cache_bin_array_descriptor, tcache->bins); - ql_tail_insert(&arena->cache_bin_array_descriptor_ql, - &tcache_slow->cache_bin_array_descriptor, link); - - malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); - } -} - -static void -tcache_arena_dissociate(tsdn_t *tsdn, tcache_slow_t *tcache_slow, - tcache_t *tcache) { - arena_t *arena = tcache_slow->arena; - assert(arena != NULL); - if (config_stats) { - /* Unlink from list of extant tcaches. */ - malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); - if (config_debug) { - bool in_ql = false; - tcache_slow_t *iter; - ql_foreach(iter, &arena->tcache_ql, link) { - if (iter == tcache_slow) { - in_ql = true; - break; - } - } - assert(in_ql); - } - ql_remove(&arena->tcache_ql, tcache_slow, link); - ql_remove(&arena->cache_bin_array_descriptor_ql, - &tcache_slow->cache_bin_array_descriptor, link); - tcache_stats_merge(tsdn, tcache_slow->tcache, arena); - malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); - } - tcache_slow->arena = NULL; -} - -void -tcache_arena_reassociate(tsdn_t *tsdn, tcache_slow_t *tcache_slow, - tcache_t *tcache, arena_t *arena) { - tcache_arena_dissociate(tsdn, tcache_slow, tcache); - tcache_arena_associate(tsdn, tcache_slow, tcache, arena); -} - -bool -tsd_tcache_enabled_data_init(tsd_t *tsd) { - /* Called upon tsd initialization. */ - tsd_tcache_enabled_set(tsd, opt_tcache); - tsd_slow_update(tsd); - - if (opt_tcache) { - /* Trigger tcache init. */ - tsd_tcache_data_init(tsd); - } - - return false; -} - -static void -tcache_init(tsd_t *tsd, tcache_slow_t *tcache_slow, tcache_t *tcache, - void *mem) { - tcache->tcache_slow = tcache_slow; - tcache_slow->tcache = tcache; - - memset(&tcache_slow->link, 0, sizeof(ql_elm(tcache_t))); - tcache_slow->next_gc_bin = 0; - tcache_slow->arena = NULL; - tcache_slow->dyn_alloc = mem; - - /* - * We reserve cache bins for all small size classes, even if some may - * not get used (i.e. bins higher than nhbins). This allows the fast - * and common paths to access cache bin metadata safely w/o worrying - * about which ones are disabled. - */ - unsigned n_reserved_bins = nhbins < SC_NBINS ? SC_NBINS : nhbins; - memset(tcache->bins, 0, sizeof(cache_bin_t) * n_reserved_bins); - - size_t cur_offset = 0; - cache_bin_preincrement(tcache_bin_info, nhbins, mem, - &cur_offset); - for (unsigned i = 0; i < nhbins; i++) { - if (i < SC_NBINS) { - tcache_slow->lg_fill_div[i] = 1; - tcache_slow->bin_refilled[i] = false; - tcache_slow->bin_flush_delay_items[i] - = tcache_gc_item_delay_compute(i); - } - cache_bin_t *cache_bin = &tcache->bins[i]; - cache_bin_init(cache_bin, &tcache_bin_info[i], mem, - &cur_offset); - } - /* - * For small size classes beyond tcache_maxclass (i.e. nhbins < NBINS), - * their cache bins are initialized to a state to safely and efficiently - * fail all fastpath alloc / free, so that no additional check around - * nhbins is needed on fastpath. - */ - for (unsigned i = nhbins; i < SC_NBINS; i++) { - /* Disabled small bins. */ - cache_bin_t *cache_bin = &tcache->bins[i]; - void *fake_stack = mem; - size_t fake_offset = 0; - - cache_bin_init(cache_bin, &tcache_bin_info[i], fake_stack, - &fake_offset); - assert(tcache_small_bin_disabled(i, cache_bin)); - } - - cache_bin_postincrement(tcache_bin_info, nhbins, mem, - &cur_offset); - /* Sanity check that the whole stack is used. */ - assert(cur_offset == tcache_bin_alloc_size); -} - -/* Initialize auto tcache (embedded in TSD). */ -bool -tsd_tcache_data_init(tsd_t *tsd) { - tcache_slow_t *tcache_slow = tsd_tcache_slowp_get_unsafe(tsd); - tcache_t *tcache = tsd_tcachep_get_unsafe(tsd); - - assert(cache_bin_still_zero_initialized(&tcache->bins[0])); - size_t alignment = tcache_bin_alloc_alignment; - size_t size = sz_sa2u(tcache_bin_alloc_size, alignment); - - void *mem = ipallocztm(tsd_tsdn(tsd), size, alignment, true, NULL, - true, arena_get(TSDN_NULL, 0, true)); - if (mem == NULL) { - return true; - } - - tcache_init(tsd, tcache_slow, tcache, mem); - /* - * Initialization is a bit tricky here. After malloc init is done, all - * threads can rely on arena_choose and associate tcache accordingly. - * However, the thread that does actual malloc bootstrapping relies on - * functional tsd, and it can only rely on a0. In that case, we - * associate its tcache to a0 temporarily, and later on - * arena_choose_hard() will re-associate properly. - */ - tcache_slow->arena = NULL; - arena_t *arena; - if (!malloc_initialized()) { - /* If in initialization, assign to a0. */ - arena = arena_get(tsd_tsdn(tsd), 0, false); - tcache_arena_associate(tsd_tsdn(tsd), tcache_slow, tcache, - arena); - } else { - arena = arena_choose(tsd, NULL); - /* This may happen if thread.tcache.enabled is used. */ - if (tcache_slow->arena == NULL) { - tcache_arena_associate(tsd_tsdn(tsd), tcache_slow, - tcache, arena); - } - } - assert(arena == tcache_slow->arena); - - return false; -} - -/* Created manual tcache for tcache.create mallctl. */ -tcache_t * -tcache_create_explicit(tsd_t *tsd) { - /* - * We place the cache bin stacks, then the tcache_t, then a pointer to - * the beginning of the whole allocation (for freeing). The makes sure - * the cache bins have the requested alignment. - */ - size_t size = tcache_bin_alloc_size + sizeof(tcache_t) - + sizeof(tcache_slow_t); - /* Naturally align the pointer stacks. */ - size = PTR_CEILING(size); - size = sz_sa2u(size, tcache_bin_alloc_alignment); - - void *mem = ipallocztm(tsd_tsdn(tsd), size, tcache_bin_alloc_alignment, - true, NULL, true, arena_get(TSDN_NULL, 0, true)); - if (mem == NULL) { - return NULL; - } - tcache_t *tcache = (void *)((uintptr_t)mem + tcache_bin_alloc_size); - tcache_slow_t *tcache_slow = - (void *)((uintptr_t)mem + tcache_bin_alloc_size + sizeof(tcache_t)); - tcache_init(tsd, tcache_slow, tcache, mem); - - tcache_arena_associate(tsd_tsdn(tsd), tcache_slow, tcache, - arena_ichoose(tsd, NULL)); - - return tcache; -} - -static void -tcache_flush_cache(tsd_t *tsd, tcache_t *tcache) { - tcache_slow_t *tcache_slow = tcache->tcache_slow; - assert(tcache_slow->arena != NULL); - - for (unsigned i = 0; i < nhbins; i++) { - cache_bin_t *cache_bin = &tcache->bins[i]; - if (i < SC_NBINS) { - tcache_bin_flush_small(tsd, tcache, cache_bin, i, 0); - } else { - tcache_bin_flush_large(tsd, tcache, cache_bin, i, 0); - } - if (config_stats) { - assert(cache_bin->tstats.nrequests == 0); - } - } -} - -void -tcache_flush(tsd_t *tsd) { - assert(tcache_available(tsd)); - tcache_flush_cache(tsd, tsd_tcachep_get(tsd)); -} - -static void -tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) { - tcache_slow_t *tcache_slow = tcache->tcache_slow; - tcache_flush_cache(tsd, tcache); - arena_t *arena = tcache_slow->arena; - tcache_arena_dissociate(tsd_tsdn(tsd), tcache_slow, tcache); - - if (tsd_tcache) { - cache_bin_t *cache_bin = &tcache->bins[0]; - cache_bin_assert_empty(cache_bin, &tcache_bin_info[0]); - } - idalloctm(tsd_tsdn(tsd), tcache_slow->dyn_alloc, NULL, NULL, true, - true); - - /* - * The deallocation and tcache flush above may not trigger decay since - * we are on the tcache shutdown path (potentially with non-nominal - * tsd). Manually trigger decay to avoid pathological cases. Also - * include arena 0 because the tcache array is allocated from it. - */ - arena_decay(tsd_tsdn(tsd), arena_get(tsd_tsdn(tsd), 0, false), - false, false); - - if (arena_nthreads_get(arena, false) == 0 && - !background_thread_enabled()) { - /* Force purging when no threads assigned to the arena anymore. */ - arena_decay(tsd_tsdn(tsd), arena, - /* is_background_thread */ false, /* all */ true); - } else { - arena_decay(tsd_tsdn(tsd), arena, - /* is_background_thread */ false, /* all */ false); - } -} - -/* For auto tcache (embedded in TSD) only. */ -void -tcache_cleanup(tsd_t *tsd) { - tcache_t *tcache = tsd_tcachep_get(tsd); - if (!tcache_available(tsd)) { - assert(tsd_tcache_enabled_get(tsd) == false); - assert(cache_bin_still_zero_initialized(&tcache->bins[0])); - return; - } - assert(tsd_tcache_enabled_get(tsd)); - assert(!cache_bin_still_zero_initialized(&tcache->bins[0])); - - tcache_destroy(tsd, tcache, true); - if (config_debug) { - /* - * For debug testing only, we want to pretend we're still in the - * zero-initialized state. - */ - memset(tcache->bins, 0, sizeof(cache_bin_t) * nhbins); - } -} - -void -tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { - cassert(config_stats); - - /* Merge and reset tcache stats. */ - for (unsigned i = 0; i < nhbins; i++) { - cache_bin_t *cache_bin = &tcache->bins[i]; - if (i < SC_NBINS) { - bin_t *bin = arena_bin_choose(tsdn, arena, i, NULL); - malloc_mutex_lock(tsdn, &bin->lock); - bin->stats.nrequests += cache_bin->tstats.nrequests; - malloc_mutex_unlock(tsdn, &bin->lock); - } else { - arena_stats_large_flush_nrequests_add(tsdn, - &arena->stats, i, cache_bin->tstats.nrequests); - } - cache_bin->tstats.nrequests = 0; - } -} - -static bool -tcaches_create_prep(tsd_t *tsd, base_t *base) { - bool err; - - malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx); - - if (tcaches == NULL) { - tcaches = base_alloc(tsd_tsdn(tsd), base, - sizeof(tcache_t *) * (MALLOCX_TCACHE_MAX+1), CACHELINE); - if (tcaches == NULL) { - err = true; - goto label_return; - } - } - - if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) { - err = true; - goto label_return; - } - - err = false; -label_return: - return err; -} - -bool -tcaches_create(tsd_t *tsd, base_t *base, unsigned *r_ind) { - witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); - - bool err; - - malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); - - if (tcaches_create_prep(tsd, base)) { - err = true; - goto label_return; - } - - tcache_t *tcache = tcache_create_explicit(tsd); - if (tcache == NULL) { - err = true; - goto label_return; - } - - tcaches_t *elm; - if (tcaches_avail != NULL) { - elm = tcaches_avail; - tcaches_avail = tcaches_avail->next; - elm->tcache = tcache; - *r_ind = (unsigned)(elm - tcaches); - } else { - elm = &tcaches[tcaches_past]; - elm->tcache = tcache; - *r_ind = tcaches_past; - tcaches_past++; - } - - err = false; -label_return: - malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); - witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); - return err; -} - -static tcache_t * -tcaches_elm_remove(tsd_t *tsd, tcaches_t *elm, bool allow_reinit) { - malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx); - - if (elm->tcache == NULL) { - return NULL; - } - tcache_t *tcache = elm->tcache; - if (allow_reinit) { - elm->tcache = TCACHES_ELM_NEED_REINIT; - } else { - elm->tcache = NULL; - } - - if (tcache == TCACHES_ELM_NEED_REINIT) { - return NULL; - } - return tcache; -} - -void -tcaches_flush(tsd_t *tsd, unsigned ind) { - malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); - tcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind], true); - malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); - if (tcache != NULL) { - /* Destroy the tcache; recreate in tcaches_get() if needed. */ - tcache_destroy(tsd, tcache, false); - } -} - -void -tcaches_destroy(tsd_t *tsd, unsigned ind) { - malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); - tcaches_t *elm = &tcaches[ind]; - tcache_t *tcache = tcaches_elm_remove(tsd, elm, false); - elm->next = tcaches_avail; - tcaches_avail = elm; - malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); - if (tcache != NULL) { - tcache_destroy(tsd, tcache, false); - } -} - -static unsigned -tcache_ncached_max_compute(szind_t szind) { - if (szind >= SC_NBINS) { - assert(szind < nhbins); - return opt_tcache_nslots_large; - } - unsigned slab_nregs = bin_infos[szind].nregs; - - /* We may modify these values; start with the opt versions. */ - unsigned nslots_small_min = opt_tcache_nslots_small_min; - unsigned nslots_small_max = opt_tcache_nslots_small_max; - - /* - * Clamp values to meet our constraints -- even, nonzero, min < max, and - * suitable for a cache bin size. - */ - if (opt_tcache_nslots_small_max > CACHE_BIN_NCACHED_MAX) { - nslots_small_max = CACHE_BIN_NCACHED_MAX; - } - if (nslots_small_min % 2 != 0) { - nslots_small_min++; - } - if (nslots_small_max % 2 != 0) { - nslots_small_max--; - } - if (nslots_small_min < 2) { - nslots_small_min = 2; - } - if (nslots_small_max < 2) { - nslots_small_max = 2; - } - if (nslots_small_min > nslots_small_max) { - nslots_small_min = nslots_small_max; - } - - unsigned candidate; - if (opt_lg_tcache_nslots_mul < 0) { - candidate = slab_nregs >> (-opt_lg_tcache_nslots_mul); - } else { - candidate = slab_nregs << opt_lg_tcache_nslots_mul; - } - if (candidate % 2 != 0) { - /* - * We need the candidate size to be even -- we assume that we - * can divide by two and get a positive number (e.g. when - * flushing). - */ - ++candidate; - } - if (candidate <= nslots_small_min) { - return nslots_small_min; - } else if (candidate <= nslots_small_max) { - return candidate; - } else { - return nslots_small_max; - } -} - -bool -tcache_boot(tsdn_t *tsdn, base_t *base) { - tcache_maxclass = sz_s2u(opt_tcache_max); - assert(tcache_maxclass <= TCACHE_MAXCLASS_LIMIT); - nhbins = sz_size2index(tcache_maxclass) + 1; - - if (malloc_mutex_init(&tcaches_mtx, "tcaches", WITNESS_RANK_TCACHES, - malloc_mutex_rank_exclusive)) { - return true; - } - - /* Initialize tcache_bin_info. See comments in tcache_init(). */ - unsigned n_reserved_bins = nhbins < SC_NBINS ? SC_NBINS : nhbins; - size_t size = n_reserved_bins * sizeof(cache_bin_info_t); - tcache_bin_info = (cache_bin_info_t *)base_alloc(tsdn, base, size, - CACHELINE); - if (tcache_bin_info == NULL) { - return true; - } - - for (szind_t i = 0; i < nhbins; i++) { - unsigned ncached_max = tcache_ncached_max_compute(i); - cache_bin_info_init(&tcache_bin_info[i], ncached_max); - } - for (szind_t i = nhbins; i < SC_NBINS; i++) { - /* Disabled small bins. */ - cache_bin_info_init(&tcache_bin_info[i], 0); - assert(tcache_small_bin_disabled(i, NULL)); - } - - cache_bin_info_compute_alloc(tcache_bin_info, nhbins, - &tcache_bin_alloc_size, &tcache_bin_alloc_alignment); - - return false; -} - -void -tcache_prefork(tsdn_t *tsdn) { - malloc_mutex_prefork(tsdn, &tcaches_mtx); -} - -void -tcache_postfork_parent(tsdn_t *tsdn) { - malloc_mutex_postfork_parent(tsdn, &tcaches_mtx); -} - -void -tcache_postfork_child(tsdn_t *tsdn) { - malloc_mutex_postfork_child(tsdn, &tcaches_mtx); -} - -void tcache_assert_initialized(tcache_t *tcache) { - assert(!cache_bin_still_zero_initialized(&tcache->bins[0])); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/test_hooks.c b/fluent-bit/lib/jemalloc-5.3.0/src/test_hooks.c deleted file mode 100644 index ace00d9c..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/test_hooks.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" - -/* - * The hooks are a little bit screwy -- they're not genuinely exported in the - * sense that we want them available to end-users, but we do want them visible - * from outside the generated library, so that we can use them in test code. - */ -JEMALLOC_EXPORT -void (*test_hooks_arena_new_hook)() = NULL; - -JEMALLOC_EXPORT -void (*test_hooks_libc_hook)() = NULL; diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/thread_event.c b/fluent-bit/lib/jemalloc-5.3.0/src/thread_event.c deleted file mode 100644 index 37eb5827..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/thread_event.c +++ /dev/null @@ -1,343 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/thread_event.h" - -/* - * Signatures for event specific functions. These functions should be defined - * by the modules owning each event. The signatures here verify that the - * definitions follow the right format. - * - * The first two are functions computing new / postponed event wait time. New - * event wait time is the time till the next event if an event is currently - * being triggered; postponed event wait time is the time till the next event - * if an event should be triggered but needs to be postponed, e.g. when the TSD - * is not nominal or during reentrancy. - * - * The third is the event handler function, which is called whenever an event - * is triggered. The parameter is the elapsed time since the last time an - * event of the same type was triggered. - */ -#define E(event, condition_unused, is_alloc_event_unused) \ -uint64_t event##_new_event_wait(tsd_t *tsd); \ -uint64_t event##_postponed_event_wait(tsd_t *tsd); \ -void event##_event_handler(tsd_t *tsd, uint64_t elapsed); - -ITERATE_OVER_ALL_EVENTS -#undef E - -/* Signatures for internal functions fetching elapsed time. */ -#define E(event, condition_unused, is_alloc_event_unused) \ -static uint64_t event##_fetch_elapsed(tsd_t *tsd); - -ITERATE_OVER_ALL_EVENTS -#undef E - -static uint64_t -tcache_gc_fetch_elapsed(tsd_t *tsd) { - return TE_INVALID_ELAPSED; -} - -static uint64_t -tcache_gc_dalloc_fetch_elapsed(tsd_t *tsd) { - return TE_INVALID_ELAPSED; -} - -static uint64_t -prof_sample_fetch_elapsed(tsd_t *tsd) { - uint64_t last_event = thread_allocated_last_event_get(tsd); - uint64_t last_sample_event = prof_sample_last_event_get(tsd); - prof_sample_last_event_set(tsd, last_event); - return last_event - last_sample_event; -} - -static uint64_t -stats_interval_fetch_elapsed(tsd_t *tsd) { - uint64_t last_event = thread_allocated_last_event_get(tsd); - uint64_t last_stats_event = stats_interval_last_event_get(tsd); - stats_interval_last_event_set(tsd, last_event); - return last_event - last_stats_event; -} - -static uint64_t -peak_alloc_fetch_elapsed(tsd_t *tsd) { - return TE_INVALID_ELAPSED; -} - -static uint64_t -peak_dalloc_fetch_elapsed(tsd_t *tsd) { - return TE_INVALID_ELAPSED; -} - -/* Per event facilities done. */ - -static bool -te_ctx_has_active_events(te_ctx_t *ctx) { - assert(config_debug); -#define E(event, condition, alloc_event) \ - if (condition && alloc_event == ctx->is_alloc) { \ - return true; \ - } - ITERATE_OVER_ALL_EVENTS -#undef E - return false; -} - -static uint64_t -te_next_event_compute(tsd_t *tsd, bool is_alloc) { - uint64_t wait = TE_MAX_START_WAIT; -#define E(event, condition, alloc_event) \ - if (is_alloc == alloc_event && condition) { \ - uint64_t event_wait = \ - event##_event_wait_get(tsd); \ - assert(event_wait <= TE_MAX_START_WAIT); \ - if (event_wait > 0U && event_wait < wait) { \ - wait = event_wait; \ - } \ - } - - ITERATE_OVER_ALL_EVENTS -#undef E - assert(wait <= TE_MAX_START_WAIT); - return wait; -} - -static void -te_assert_invariants_impl(tsd_t *tsd, te_ctx_t *ctx) { - uint64_t current_bytes = te_ctx_current_bytes_get(ctx); - uint64_t last_event = te_ctx_last_event_get(ctx); - uint64_t next_event = te_ctx_next_event_get(ctx); - uint64_t next_event_fast = te_ctx_next_event_fast_get(ctx); - - assert(last_event != next_event); - if (next_event > TE_NEXT_EVENT_FAST_MAX || !tsd_fast(tsd)) { - assert(next_event_fast == 0U); - } else { - assert(next_event_fast == next_event); - } - - /* The subtraction is intentionally susceptible to underflow. */ - uint64_t interval = next_event - last_event; - - /* The subtraction is intentionally susceptible to underflow. */ - assert(current_bytes - last_event < interval); - uint64_t min_wait = te_next_event_compute(tsd, te_ctx_is_alloc(ctx)); - /* - * next_event should have been pushed up only except when no event is - * on and the TSD is just initialized. The last_event == 0U guard - * below is stronger than needed, but having an exactly accurate guard - * is more complicated to implement. - */ - assert((!te_ctx_has_active_events(ctx) && last_event == 0U) || - interval == min_wait || - (interval < min_wait && interval == TE_MAX_INTERVAL)); -} - -void -te_assert_invariants_debug(tsd_t *tsd) { - te_ctx_t ctx; - te_ctx_get(tsd, &ctx, true); - te_assert_invariants_impl(tsd, &ctx); - - te_ctx_get(tsd, &ctx, false); - te_assert_invariants_impl(tsd, &ctx); -} - -/* - * Synchronization around the fast threshold in tsd -- - * There are two threads to consider in the synchronization here: - * - The owner of the tsd being updated by a slow path change - * - The remote thread, doing that slow path change. - * - * As a design constraint, we want to ensure that a slow-path transition cannot - * be ignored for arbitrarily long, and that if the remote thread causes a - * slow-path transition and then communicates with the owner thread that it has - * occurred, then the owner will go down the slow path on the next allocator - * operation (so that we don't want to just wait until the owner hits its slow - * path reset condition on its own). - * - * Here's our strategy to do that: - * - * The remote thread will update the slow-path stores to TSD variables, issue a - * SEQ_CST fence, and then update the TSD next_event_fast counter. The owner - * thread will update next_event_fast, issue an SEQ_CST fence, and then check - * its TSD to see if it's on the slow path. - - * This is fairly straightforward when 64-bit atomics are supported. Assume that - * the remote fence is sandwiched between two owner fences in the reset pathway. - * The case where there is no preceding or trailing owner fence (i.e. because - * the owner thread is near the beginning or end of its life) can be analyzed - * similarly. The owner store to next_event_fast preceding the earlier owner - * fence will be earlier in coherence order than the remote store to it, so that - * the owner thread will go down the slow path once the store becomes visible to - * it, which is no later than the time of the second fence. - - * The case where we don't support 64-bit atomics is trickier, since word - * tearing is possible. We'll repeat the same analysis, and look at the two - * owner fences sandwiching the remote fence. The next_event_fast stores done - * alongside the earlier owner fence cannot overwrite any of the remote stores - * (since they precede the earlier owner fence in sb, which precedes the remote - * fence in sc, which precedes the remote stores in sb). After the second owner - * fence there will be a re-check of the slow-path variables anyways, so the - * "owner will notice that it's on the slow path eventually" guarantee is - * satisfied. To make sure that the out-of-band-messaging constraint is as well, - * note that either the message passing is sequenced before the second owner - * fence (in which case the remote stores happen before the second set of owner - * stores, so malloc sees a value of zero for next_event_fast and goes down the - * slow path), or it is not (in which case the owner sees the tsd slow-path - * writes on its previous update). This leaves open the possibility that the - * remote thread will (at some arbitrary point in the future) zero out one half - * of the owner thread's next_event_fast, but that's always safe (it just sends - * it down the slow path earlier). - */ -static void -te_ctx_next_event_fast_update(te_ctx_t *ctx) { - uint64_t next_event = te_ctx_next_event_get(ctx); - uint64_t next_event_fast = (next_event <= TE_NEXT_EVENT_FAST_MAX) ? - next_event : 0U; - te_ctx_next_event_fast_set(ctx, next_event_fast); -} - -void -te_recompute_fast_threshold(tsd_t *tsd) { - if (tsd_state_get(tsd) != tsd_state_nominal) { - /* Check first because this is also called on purgatory. */ - te_next_event_fast_set_non_nominal(tsd); - return; - } - - te_ctx_t ctx; - te_ctx_get(tsd, &ctx, true); - te_ctx_next_event_fast_update(&ctx); - te_ctx_get(tsd, &ctx, false); - te_ctx_next_event_fast_update(&ctx); - - atomic_fence(ATOMIC_SEQ_CST); - if (tsd_state_get(tsd) != tsd_state_nominal) { - te_next_event_fast_set_non_nominal(tsd); - } -} - -static void -te_adjust_thresholds_helper(tsd_t *tsd, te_ctx_t *ctx, - uint64_t wait) { - /* - * The next threshold based on future events can only be adjusted after - * progressing the last_event counter (which is set to current). - */ - assert(te_ctx_current_bytes_get(ctx) == te_ctx_last_event_get(ctx)); - assert(wait <= TE_MAX_START_WAIT); - - uint64_t next_event = te_ctx_last_event_get(ctx) + (wait <= - TE_MAX_INTERVAL ? wait : TE_MAX_INTERVAL); - te_ctx_next_event_set(tsd, ctx, next_event); -} - -static uint64_t -te_clip_event_wait(uint64_t event_wait) { - assert(event_wait > 0U); - if (TE_MIN_START_WAIT > 1U && - unlikely(event_wait < TE_MIN_START_WAIT)) { - event_wait = TE_MIN_START_WAIT; - } - if (TE_MAX_START_WAIT < UINT64_MAX && - unlikely(event_wait > TE_MAX_START_WAIT)) { - event_wait = TE_MAX_START_WAIT; - } - return event_wait; -} - -void -te_event_trigger(tsd_t *tsd, te_ctx_t *ctx) { - /* usize has already been added to thread_allocated. */ - uint64_t bytes_after = te_ctx_current_bytes_get(ctx); - /* The subtraction is intentionally susceptible to underflow. */ - uint64_t accumbytes = bytes_after - te_ctx_last_event_get(ctx); - - te_ctx_last_event_set(ctx, bytes_after); - - bool allow_event_trigger = tsd_nominal(tsd) && - tsd_reentrancy_level_get(tsd) == 0; - bool is_alloc = ctx->is_alloc; - uint64_t wait = TE_MAX_START_WAIT; - -#define E(event, condition, alloc_event) \ - bool is_##event##_triggered = false; \ - if (is_alloc == alloc_event && condition) { \ - uint64_t event_wait = event##_event_wait_get(tsd); \ - assert(event_wait <= TE_MAX_START_WAIT); \ - if (event_wait > accumbytes) { \ - event_wait -= accumbytes; \ - } else if (!allow_event_trigger) { \ - event_wait = event##_postponed_event_wait(tsd); \ - } else { \ - is_##event##_triggered = true; \ - event_wait = event##_new_event_wait(tsd); \ - } \ - event_wait = te_clip_event_wait(event_wait); \ - event##_event_wait_set(tsd, event_wait); \ - if (event_wait < wait) { \ - wait = event_wait; \ - } \ - } - - ITERATE_OVER_ALL_EVENTS -#undef E - - assert(wait <= TE_MAX_START_WAIT); - te_adjust_thresholds_helper(tsd, ctx, wait); - te_assert_invariants(tsd); - -#define E(event, condition, alloc_event) \ - if (is_alloc == alloc_event && condition && \ - is_##event##_triggered) { \ - assert(allow_event_trigger); \ - uint64_t elapsed = event##_fetch_elapsed(tsd); \ - event##_event_handler(tsd, elapsed); \ - } - - ITERATE_OVER_ALL_EVENTS -#undef E - - te_assert_invariants(tsd); -} - -static void -te_init(tsd_t *tsd, bool is_alloc) { - te_ctx_t ctx; - te_ctx_get(tsd, &ctx, is_alloc); - /* - * Reset the last event to current, which starts the events from a clean - * state. This is necessary when re-init the tsd event counters. - * - * The event counters maintain a relationship with the current bytes: - * last_event <= current < next_event. When a reinit happens (e.g. - * reincarnated tsd), the last event needs progressing because all - * events start fresh from the current bytes. - */ - te_ctx_last_event_set(&ctx, te_ctx_current_bytes_get(&ctx)); - - uint64_t wait = TE_MAX_START_WAIT; -#define E(event, condition, alloc_event) \ - if (is_alloc == alloc_event && condition) { \ - uint64_t event_wait = event##_new_event_wait(tsd); \ - event_wait = te_clip_event_wait(event_wait); \ - event##_event_wait_set(tsd, event_wait); \ - if (event_wait < wait) { \ - wait = event_wait; \ - } \ - } - - ITERATE_OVER_ALL_EVENTS -#undef E - te_adjust_thresholds_helper(tsd, &ctx, wait); -} - -void -tsd_te_init(tsd_t *tsd) { - /* Make sure no overflow for the bytes accumulated on event_trigger. */ - assert(TE_MAX_INTERVAL <= UINT64_MAX - SC_LARGE_MAXCLASS + 1); - te_init(tsd, true); - te_init(tsd, false); - te_assert_invariants(tsd); -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/ticker.c b/fluent-bit/lib/jemalloc-5.3.0/src/ticker.c deleted file mode 100644 index 790b5c20..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/ticker.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -/* - * To avoid using floating point math down core paths (still necessary because - * versions of the glibc dynamic loader that did not preserve xmm registers are - * still somewhat common, requiring us to be compilable with -mno-sse), and also - * to avoid generally expensive library calls, we use a precomputed table of - * values. We want to sample U uniformly on [0, 1], and then compute - * ceil(log(u)/log(1-1/nticks)). We're mostly interested in the case where - * nticks is reasonably big, so 1/log(1-1/nticks) is well-approximated by - * -nticks. - * - * To compute log(u), we sample an integer in [1, 64] and divide, then just look - * up results in a table. As a space-compression mechanism, we store these as - * uint8_t by dividing the range (255) by the highest-magnitude value the log - * can take on, and using that as a multiplier. We then have to divide by that - * multiplier at the end of the computation. - * - * The values here are computed in src/ticker.py - */ - -const uint8_t ticker_geom_table[1 << TICKER_GEOM_NBITS] = { - 254, 211, 187, 169, 156, 144, 135, 127, - 120, 113, 107, 102, 97, 93, 89, 85, - 81, 77, 74, 71, 68, 65, 62, 60, - 57, 55, 53, 50, 48, 46, 44, 42, - 40, 39, 37, 35, 33, 32, 30, 29, - 27, 26, 24, 23, 21, 20, 19, 18, - 16, 15, 14, 13, 12, 10, 9, 8, - 7, 6, 5, 4, 3, 2, 1, 0 -}; diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/ticker.py b/fluent-bit/lib/jemalloc-5.3.0/src/ticker.py deleted file mode 100755 index 3807740c..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/ticker.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 - -import math - -# Must match TICKER_GEOM_NBITS -lg_table_size = 6 -table_size = 2**lg_table_size -byte_max = 255 -mul = math.floor(-byte_max/math.log(1 / table_size)) -values = [round(-mul * math.log(i / table_size)) - for i in range(1, table_size+1)] -print("mul =", mul) -print("values:") -for i in range(table_size // 8): - print(", ".join((str(x) for x in values[i*8 : i*8 + 8]))) diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/tsd.c b/fluent-bit/lib/jemalloc-5.3.0/src/tsd.c deleted file mode 100644 index e8e4f3a3..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/tsd.c +++ /dev/null @@ -1,549 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/san.h" -#include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/rtree.h" - -/******************************************************************************/ -/* Data. */ - -/* TSD_INITIALIZER triggers "-Wmissing-field-initializer" */ -JEMALLOC_DIAGNOSTIC_PUSH -JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS - -#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP -JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls = TSD_INITIALIZER; -JEMALLOC_TSD_TYPE_ATTR(bool) JEMALLOC_TLS_MODEL tsd_initialized = false; -bool tsd_booted = false; -#elif (defined(JEMALLOC_TLS)) -JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls = TSD_INITIALIZER; -pthread_key_t tsd_tsd; -bool tsd_booted = false; -#elif (defined(_WIN32)) -DWORD tsd_tsd; -tsd_wrapper_t tsd_boot_wrapper = {false, TSD_INITIALIZER}; -bool tsd_booted = false; -#else - -/* - * This contains a mutex, but it's pretty convenient to allow the mutex code to - * have a dependency on tsd. So we define the struct here, and only refer to it - * by pointer in the header. - */ -struct tsd_init_head_s { - ql_head(tsd_init_block_t) blocks; - malloc_mutex_t lock; -}; - -pthread_key_t tsd_tsd; -tsd_init_head_t tsd_init_head = { - ql_head_initializer(blocks), - MALLOC_MUTEX_INITIALIZER -}; - -tsd_wrapper_t tsd_boot_wrapper = { - false, - TSD_INITIALIZER -}; -bool tsd_booted = false; -#endif - -JEMALLOC_DIAGNOSTIC_POP - -/******************************************************************************/ - -/* A list of all the tsds in the nominal state. */ -typedef ql_head(tsd_t) tsd_list_t; -static tsd_list_t tsd_nominal_tsds = ql_head_initializer(tsd_nominal_tsds); -static malloc_mutex_t tsd_nominal_tsds_lock; - -/* How many slow-path-enabling features are turned on. */ -static atomic_u32_t tsd_global_slow_count = ATOMIC_INIT(0); - -static bool -tsd_in_nominal_list(tsd_t *tsd) { - tsd_t *tsd_list; - bool found = false; - /* - * We don't know that tsd is nominal; it might not be safe to get data - * out of it here. - */ - malloc_mutex_lock(TSDN_NULL, &tsd_nominal_tsds_lock); - ql_foreach(tsd_list, &tsd_nominal_tsds, TSD_MANGLE(tsd_link)) { - if (tsd == tsd_list) { - found = true; - break; - } - } - malloc_mutex_unlock(TSDN_NULL, &tsd_nominal_tsds_lock); - return found; -} - -static void -tsd_add_nominal(tsd_t *tsd) { - assert(!tsd_in_nominal_list(tsd)); - assert(tsd_state_get(tsd) <= tsd_state_nominal_max); - ql_elm_new(tsd, TSD_MANGLE(tsd_link)); - malloc_mutex_lock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); - ql_tail_insert(&tsd_nominal_tsds, tsd, TSD_MANGLE(tsd_link)); - malloc_mutex_unlock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); -} - -static void -tsd_remove_nominal(tsd_t *tsd) { - assert(tsd_in_nominal_list(tsd)); - assert(tsd_state_get(tsd) <= tsd_state_nominal_max); - malloc_mutex_lock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); - ql_remove(&tsd_nominal_tsds, tsd, TSD_MANGLE(tsd_link)); - malloc_mutex_unlock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); -} - -static void -tsd_force_recompute(tsdn_t *tsdn) { - /* - * The stores to tsd->state here need to synchronize with the exchange - * in tsd_slow_update. - */ - atomic_fence(ATOMIC_RELEASE); - malloc_mutex_lock(tsdn, &tsd_nominal_tsds_lock); - tsd_t *remote_tsd; - ql_foreach(remote_tsd, &tsd_nominal_tsds, TSD_MANGLE(tsd_link)) { - assert(tsd_atomic_load(&remote_tsd->state, ATOMIC_RELAXED) - <= tsd_state_nominal_max); - tsd_atomic_store(&remote_tsd->state, - tsd_state_nominal_recompute, ATOMIC_RELAXED); - /* See comments in te_recompute_fast_threshold(). */ - atomic_fence(ATOMIC_SEQ_CST); - te_next_event_fast_set_non_nominal(remote_tsd); - } - malloc_mutex_unlock(tsdn, &tsd_nominal_tsds_lock); -} - -void -tsd_global_slow_inc(tsdn_t *tsdn) { - atomic_fetch_add_u32(&tsd_global_slow_count, 1, ATOMIC_RELAXED); - /* - * We unconditionally force a recompute, even if the global slow count - * was already positive. If we didn't, then it would be possible for us - * to return to the user, have the user synchronize externally with some - * other thread, and then have that other thread not have picked up the - * update yet (since the original incrementing thread might still be - * making its way through the tsd list). - */ - tsd_force_recompute(tsdn); -} - -void tsd_global_slow_dec(tsdn_t *tsdn) { - atomic_fetch_sub_u32(&tsd_global_slow_count, 1, ATOMIC_RELAXED); - /* See the note in ..._inc(). */ - tsd_force_recompute(tsdn); -} - -static bool -tsd_local_slow(tsd_t *tsd) { - return !tsd_tcache_enabled_get(tsd) - || tsd_reentrancy_level_get(tsd) > 0; -} - -bool -tsd_global_slow() { - return atomic_load_u32(&tsd_global_slow_count, ATOMIC_RELAXED) > 0; -} - -/******************************************************************************/ - -static uint8_t -tsd_state_compute(tsd_t *tsd) { - if (!tsd_nominal(tsd)) { - return tsd_state_get(tsd); - } - /* We're in *a* nominal state; but which one? */ - if (malloc_slow || tsd_local_slow(tsd) || tsd_global_slow()) { - return tsd_state_nominal_slow; - } else { - return tsd_state_nominal; - } -} - -void -tsd_slow_update(tsd_t *tsd) { - uint8_t old_state; - do { - uint8_t new_state = tsd_state_compute(tsd); - old_state = tsd_atomic_exchange(&tsd->state, new_state, - ATOMIC_ACQUIRE); - } while (old_state == tsd_state_nominal_recompute); - - te_recompute_fast_threshold(tsd); -} - -void -tsd_state_set(tsd_t *tsd, uint8_t new_state) { - /* Only the tsd module can change the state *to* recompute. */ - assert(new_state != tsd_state_nominal_recompute); - uint8_t old_state = tsd_atomic_load(&tsd->state, ATOMIC_RELAXED); - if (old_state > tsd_state_nominal_max) { - /* - * Not currently in the nominal list, but it might need to be - * inserted there. - */ - assert(!tsd_in_nominal_list(tsd)); - tsd_atomic_store(&tsd->state, new_state, ATOMIC_RELAXED); - if (new_state <= tsd_state_nominal_max) { - tsd_add_nominal(tsd); - } - } else { - /* - * We're currently nominal. If the new state is non-nominal, - * great; we take ourselves off the list and just enter the new - * state. - */ - assert(tsd_in_nominal_list(tsd)); - if (new_state > tsd_state_nominal_max) { - tsd_remove_nominal(tsd); - tsd_atomic_store(&tsd->state, new_state, - ATOMIC_RELAXED); - } else { - /* - * This is the tricky case. We're transitioning from - * one nominal state to another. The caller can't know - * about any races that are occurring at the same time, - * so we always have to recompute no matter what. - */ - tsd_slow_update(tsd); - } - } - te_recompute_fast_threshold(tsd); -} - -static void -tsd_prng_state_init(tsd_t *tsd) { - /* - * A nondeterministic seed based on the address of tsd reduces - * the likelihood of lockstep non-uniform cache index - * utilization among identical concurrent processes, but at the - * cost of test repeatability. For debug builds, instead use a - * deterministic seed. - */ - *tsd_prng_statep_get(tsd) = config_debug ? 0 : - (uint64_t)(uintptr_t)tsd; -} - -static bool -tsd_data_init(tsd_t *tsd) { - /* - * We initialize the rtree context first (before the tcache), since the - * tcache initialization depends on it. - */ - rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd)); - tsd_prng_state_init(tsd); - tsd_te_init(tsd); /* event_init may use the prng state above. */ - tsd_san_init(tsd); - return tsd_tcache_enabled_data_init(tsd); -} - -static void -assert_tsd_data_cleanup_done(tsd_t *tsd) { - assert(!tsd_nominal(tsd)); - assert(!tsd_in_nominal_list(tsd)); - assert(*tsd_arenap_get_unsafe(tsd) == NULL); - assert(*tsd_iarenap_get_unsafe(tsd) == NULL); - assert(*tsd_tcache_enabledp_get_unsafe(tsd) == false); - assert(*tsd_prof_tdatap_get_unsafe(tsd) == NULL); -} - -static bool -tsd_data_init_nocleanup(tsd_t *tsd) { - assert(tsd_state_get(tsd) == tsd_state_reincarnated || - tsd_state_get(tsd) == tsd_state_minimal_initialized); - /* - * During reincarnation, there is no guarantee that the cleanup function - * will be called (deallocation may happen after all tsd destructors). - * We set up tsd in a way that no cleanup is needed. - */ - rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd)); - *tsd_tcache_enabledp_get_unsafe(tsd) = false; - *tsd_reentrancy_levelp_get(tsd) = 1; - tsd_prng_state_init(tsd); - tsd_te_init(tsd); /* event_init may use the prng state above. */ - tsd_san_init(tsd); - assert_tsd_data_cleanup_done(tsd); - - return false; -} - -tsd_t * -tsd_fetch_slow(tsd_t *tsd, bool minimal) { - assert(!tsd_fast(tsd)); - - if (tsd_state_get(tsd) == tsd_state_nominal_slow) { - /* - * On slow path but no work needed. Note that we can't - * necessarily *assert* that we're slow, because we might be - * slow because of an asynchronous modification to global state, - * which might be asynchronously modified *back*. - */ - } else if (tsd_state_get(tsd) == tsd_state_nominal_recompute) { - tsd_slow_update(tsd); - } else if (tsd_state_get(tsd) == tsd_state_uninitialized) { - if (!minimal) { - if (tsd_booted) { - tsd_state_set(tsd, tsd_state_nominal); - tsd_slow_update(tsd); - /* Trigger cleanup handler registration. */ - tsd_set(tsd); - tsd_data_init(tsd); - } - } else { - tsd_state_set(tsd, tsd_state_minimal_initialized); - tsd_set(tsd); - tsd_data_init_nocleanup(tsd); - } - } else if (tsd_state_get(tsd) == tsd_state_minimal_initialized) { - if (!minimal) { - /* Switch to fully initialized. */ - tsd_state_set(tsd, tsd_state_nominal); - assert(*tsd_reentrancy_levelp_get(tsd) >= 1); - (*tsd_reentrancy_levelp_get(tsd))--; - tsd_slow_update(tsd); - tsd_data_init(tsd); - } else { - assert_tsd_data_cleanup_done(tsd); - } - } else if (tsd_state_get(tsd) == tsd_state_purgatory) { - tsd_state_set(tsd, tsd_state_reincarnated); - tsd_set(tsd); - tsd_data_init_nocleanup(tsd); - } else { - assert(tsd_state_get(tsd) == tsd_state_reincarnated); - } - - return tsd; -} - -void * -malloc_tsd_malloc(size_t size) { - return a0malloc(CACHELINE_CEILING(size)); -} - -void -malloc_tsd_dalloc(void *wrapper) { - a0dalloc(wrapper); -} - -#if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32) -static unsigned ncleanups; -static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX]; - -#ifndef _WIN32 -JEMALLOC_EXPORT -#endif -void -_malloc_thread_cleanup(void) { - bool pending[MALLOC_TSD_CLEANUPS_MAX], again; - unsigned i; - - for (i = 0; i < ncleanups; i++) { - pending[i] = true; - } - - do { - again = false; - for (i = 0; i < ncleanups; i++) { - if (pending[i]) { - pending[i] = cleanups[i](); - if (pending[i]) { - again = true; - } - } - } - } while (again); -} - -#ifndef _WIN32 -JEMALLOC_EXPORT -#endif -void -_malloc_tsd_cleanup_register(bool (*f)(void)) { - assert(ncleanups < MALLOC_TSD_CLEANUPS_MAX); - cleanups[ncleanups] = f; - ncleanups++; -} - -#endif - -static void -tsd_do_data_cleanup(tsd_t *tsd) { - prof_tdata_cleanup(tsd); - iarena_cleanup(tsd); - arena_cleanup(tsd); - tcache_cleanup(tsd); - witnesses_cleanup(tsd_witness_tsdp_get_unsafe(tsd)); - *tsd_reentrancy_levelp_get(tsd) = 1; -} - -void -tsd_cleanup(void *arg) { - tsd_t *tsd = (tsd_t *)arg; - - switch (tsd_state_get(tsd)) { - case tsd_state_uninitialized: - /* Do nothing. */ - break; - case tsd_state_minimal_initialized: - /* This implies the thread only did free() in its life time. */ - /* Fall through. */ - case tsd_state_reincarnated: - /* - * Reincarnated means another destructor deallocated memory - * after the destructor was called. Cleanup isn't required but - * is still called for testing and completeness. - */ - assert_tsd_data_cleanup_done(tsd); - JEMALLOC_FALLTHROUGH; - case tsd_state_nominal: - case tsd_state_nominal_slow: - tsd_do_data_cleanup(tsd); - tsd_state_set(tsd, tsd_state_purgatory); - tsd_set(tsd); - break; - case tsd_state_purgatory: - /* - * The previous time this destructor was called, we set the - * state to tsd_state_purgatory so that other destructors - * wouldn't cause re-creation of the tsd. This time, do - * nothing, and do not request another callback. - */ - break; - default: - not_reached(); - } -#ifdef JEMALLOC_JET - test_callback_t test_callback = *tsd_test_callbackp_get_unsafe(tsd); - int *data = tsd_test_datap_get_unsafe(tsd); - if (test_callback != NULL) { - test_callback(data); - } -#endif -} - -tsd_t * -malloc_tsd_boot0(void) { - tsd_t *tsd; - -#if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32) - ncleanups = 0; -#endif - if (malloc_mutex_init(&tsd_nominal_tsds_lock, "tsd_nominal_tsds_lock", - WITNESS_RANK_OMIT, malloc_mutex_rank_exclusive)) { - return NULL; - } - if (tsd_boot0()) { - return NULL; - } - tsd = tsd_fetch(); - return tsd; -} - -void -malloc_tsd_boot1(void) { - tsd_boot1(); - tsd_t *tsd = tsd_fetch(); - /* malloc_slow has been set properly. Update tsd_slow. */ - tsd_slow_update(tsd); -} - -#ifdef _WIN32 -static BOOL WINAPI -_tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - switch (fdwReason) { -#ifdef JEMALLOC_LAZY_LOCK - case DLL_THREAD_ATTACH: - isthreaded = true; - break; -#endif - case DLL_THREAD_DETACH: - _malloc_thread_cleanup(); - break; - default: - break; - } - return true; -} - -/* - * We need to be able to say "read" here (in the "pragma section"), but have - * hooked "read". We won't read for the rest of the file, so we can get away - * with unhooking. - */ -#ifdef read -# undef read -#endif - -#ifdef _MSC_VER -# ifdef _M_IX86 -# pragma comment(linker, "/INCLUDE:__tls_used") -# pragma comment(linker, "/INCLUDE:_tls_callback") -# else -# pragma comment(linker, "/INCLUDE:_tls_used") -# pragma comment(linker, "/INCLUDE:" STRINGIFY(tls_callback) ) -# endif -# pragma section(".CRT$XLY",long,read) -#endif -JEMALLOC_SECTION(".CRT$XLY") JEMALLOC_ATTR(used) -BOOL (WINAPI *const tls_callback)(HINSTANCE hinstDLL, - DWORD fdwReason, LPVOID lpvReserved) = _tls_callback; -#endif - -#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ - !defined(_WIN32)) -void * -tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block) { - pthread_t self = pthread_self(); - tsd_init_block_t *iter; - - /* Check whether this thread has already inserted into the list. */ - malloc_mutex_lock(TSDN_NULL, &head->lock); - ql_foreach(iter, &head->blocks, link) { - if (iter->thread == self) { - malloc_mutex_unlock(TSDN_NULL, &head->lock); - return iter->data; - } - } - /* Insert block into list. */ - ql_elm_new(block, link); - block->thread = self; - ql_tail_insert(&head->blocks, block, link); - malloc_mutex_unlock(TSDN_NULL, &head->lock); - return NULL; -} - -void -tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) { - malloc_mutex_lock(TSDN_NULL, &head->lock); - ql_remove(&head->blocks, block, link); - malloc_mutex_unlock(TSDN_NULL, &head->lock); -} -#endif - -void -tsd_prefork(tsd_t *tsd) { - malloc_mutex_prefork(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); -} - -void -tsd_postfork_parent(tsd_t *tsd) { - malloc_mutex_postfork_parent(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); -} - -void -tsd_postfork_child(tsd_t *tsd) { - malloc_mutex_postfork_child(tsd_tsdn(tsd), &tsd_nominal_tsds_lock); - ql_new(&tsd_nominal_tsds); - - if (tsd_state_get(tsd) <= tsd_state_nominal_max) { - tsd_add_nominal(tsd); - } -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/witness.c b/fluent-bit/lib/jemalloc-5.3.0/src/witness.c deleted file mode 100644 index 4474af04..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/witness.c +++ /dev/null @@ -1,122 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" -#include "jemalloc/internal/malloc_io.h" - -void -witness_init(witness_t *witness, const char *name, witness_rank_t rank, - witness_comp_t *comp, void *opaque) { - witness->name = name; - witness->rank = rank; - witness->comp = comp; - witness->opaque = opaque; -} - -static void -witness_print_witness(witness_t *w, unsigned n) { - assert(n > 0); - if (n == 1) { - malloc_printf(" %s(%u)", w->name, w->rank); - } else { - malloc_printf(" %s(%u)X%u", w->name, w->rank, n); - } -} - -static void -witness_print_witnesses(const witness_list_t *witnesses) { - witness_t *w, *last = NULL; - unsigned n = 0; - ql_foreach(w, witnesses, link) { - if (last != NULL && w->rank > last->rank) { - assert(w->name != last->name); - witness_print_witness(last, n); - n = 0; - } else if (last != NULL) { - assert(w->rank == last->rank); - assert(w->name == last->name); - } - last = w; - ++n; - } - if (last != NULL) { - witness_print_witness(last, n); - } -} - -static void -witness_lock_error_impl(const witness_list_t *witnesses, - const witness_t *witness) { - malloc_printf("<jemalloc>: Lock rank order reversal:"); - witness_print_witnesses(witnesses); - malloc_printf(" %s(%u)\n", witness->name, witness->rank); - abort(); -} -witness_lock_error_t *JET_MUTABLE witness_lock_error = witness_lock_error_impl; - -static void -witness_owner_error_impl(const witness_t *witness) { - malloc_printf("<jemalloc>: Should own %s(%u)\n", witness->name, - witness->rank); - abort(); -} -witness_owner_error_t *JET_MUTABLE witness_owner_error = - witness_owner_error_impl; - -static void -witness_not_owner_error_impl(const witness_t *witness) { - malloc_printf("<jemalloc>: Should not own %s(%u)\n", witness->name, - witness->rank); - abort(); -} -witness_not_owner_error_t *JET_MUTABLE witness_not_owner_error = - witness_not_owner_error_impl; - -static void -witness_depth_error_impl(const witness_list_t *witnesses, - witness_rank_t rank_inclusive, unsigned depth) { - malloc_printf("<jemalloc>: Should own %u lock%s of rank >= %u:", depth, - (depth != 1) ? "s" : "", rank_inclusive); - witness_print_witnesses(witnesses); - malloc_printf("\n"); - abort(); -} -witness_depth_error_t *JET_MUTABLE witness_depth_error = - witness_depth_error_impl; - -void -witnesses_cleanup(witness_tsd_t *witness_tsd) { - witness_assert_lockless(witness_tsd_tsdn(witness_tsd)); - - /* Do nothing. */ -} - -void -witness_prefork(witness_tsd_t *witness_tsd) { - if (!config_debug) { - return; - } - witness_tsd->forking = true; -} - -void -witness_postfork_parent(witness_tsd_t *witness_tsd) { - if (!config_debug) { - return; - } - witness_tsd->forking = false; -} - -void -witness_postfork_child(witness_tsd_t *witness_tsd) { - if (!config_debug) { - return; - } -#ifndef JEMALLOC_MUTEX_INIT_CB - witness_list_t *witnesses; - - witnesses = &witness_tsd->witnesses; - ql_new(witnesses); -#endif - witness_tsd->forking = false; -} diff --git a/fluent-bit/lib/jemalloc-5.3.0/src/zone.c b/fluent-bit/lib/jemalloc-5.3.0/src/zone.c deleted file mode 100644 index 23dfdd04..00000000 --- a/fluent-bit/lib/jemalloc-5.3.0/src/zone.c +++ /dev/null @@ -1,469 +0,0 @@ -#include "jemalloc/internal/jemalloc_preamble.h" -#include "jemalloc/internal/jemalloc_internal_includes.h" - -#include "jemalloc/internal/assert.h" - -#ifndef JEMALLOC_ZONE -# error "This source file is for zones on Darwin (OS X)." -#endif - -/* Definitions of the following structs in malloc/malloc.h might be too old - * for the built binary to run on newer versions of OSX. So use the newest - * possible version of those structs. - */ -typedef struct _malloc_zone_t { - void *reserved1; - void *reserved2; - size_t (*size)(struct _malloc_zone_t *, const void *); - void *(*malloc)(struct _malloc_zone_t *, size_t); - void *(*calloc)(struct _malloc_zone_t *, size_t, size_t); - void *(*valloc)(struct _malloc_zone_t *, size_t); - void (*free)(struct _malloc_zone_t *, void *); - void *(*realloc)(struct _malloc_zone_t *, void *, size_t); - void (*destroy)(struct _malloc_zone_t *); - const char *zone_name; - unsigned (*batch_malloc)(struct _malloc_zone_t *, size_t, void **, unsigned); - void (*batch_free)(struct _malloc_zone_t *, void **, unsigned); - struct malloc_introspection_t *introspect; - unsigned version; - void *(*memalign)(struct _malloc_zone_t *, size_t, size_t); - void (*free_definite_size)(struct _malloc_zone_t *, void *, size_t); - size_t (*pressure_relief)(struct _malloc_zone_t *, size_t); -} malloc_zone_t; - -typedef struct { - vm_address_t address; - vm_size_t size; -} vm_range_t; - -typedef struct malloc_statistics_t { - unsigned blocks_in_use; - size_t size_in_use; - size_t max_size_in_use; - size_t size_allocated; -} malloc_statistics_t; - -typedef kern_return_t memory_reader_t(task_t, vm_address_t, vm_size_t, void **); - -typedef void vm_range_recorder_t(task_t, void *, unsigned type, vm_range_t *, unsigned); - -typedef struct malloc_introspection_t { - kern_return_t (*enumerator)(task_t, void *, unsigned, vm_address_t, memory_reader_t, vm_range_recorder_t); - size_t (*good_size)(malloc_zone_t *, size_t); - boolean_t (*check)(malloc_zone_t *); - void (*print)(malloc_zone_t *, boolean_t); - void (*log)(malloc_zone_t *, void *); - void (*force_lock)(malloc_zone_t *); - void (*force_unlock)(malloc_zone_t *); - void (*statistics)(malloc_zone_t *, malloc_statistics_t *); - boolean_t (*zone_locked)(malloc_zone_t *); - boolean_t (*enable_discharge_checking)(malloc_zone_t *); - boolean_t (*disable_discharge_checking)(malloc_zone_t *); - void (*discharge)(malloc_zone_t *, void *); -#ifdef __BLOCKS__ - void (*enumerate_discharged_pointers)(malloc_zone_t *, void (^)(void *, void *)); -#else - void *enumerate_unavailable_without_blocks; -#endif - void (*reinit_lock)(malloc_zone_t *); -} malloc_introspection_t; - -extern kern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *); - -extern malloc_zone_t *malloc_default_zone(void); - -extern void malloc_zone_register(malloc_zone_t *zone); - -extern void malloc_zone_unregister(malloc_zone_t *zone); - -/* - * The malloc_default_purgeable_zone() function is only available on >= 10.6. - * We need to check whether it is present at runtime, thus the weak_import. - */ -extern malloc_zone_t *malloc_default_purgeable_zone(void) -JEMALLOC_ATTR(weak_import); - -/******************************************************************************/ -/* Data. */ - -static malloc_zone_t *default_zone, *purgeable_zone; -static malloc_zone_t jemalloc_zone; -static struct malloc_introspection_t jemalloc_zone_introspect; -static pid_t zone_force_lock_pid = -1; - -/******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static size_t zone_size(malloc_zone_t *zone, const void *ptr); -static void *zone_malloc(malloc_zone_t *zone, size_t size); -static void *zone_calloc(malloc_zone_t *zone, size_t num, size_t size); -static void *zone_valloc(malloc_zone_t *zone, size_t size); -static void zone_free(malloc_zone_t *zone, void *ptr); -static void *zone_realloc(malloc_zone_t *zone, void *ptr, size_t size); -static void *zone_memalign(malloc_zone_t *zone, size_t alignment, - size_t size); -static void zone_free_definite_size(malloc_zone_t *zone, void *ptr, - size_t size); -static void zone_destroy(malloc_zone_t *zone); -static unsigned zone_batch_malloc(struct _malloc_zone_t *zone, size_t size, - void **results, unsigned num_requested); -static void zone_batch_free(struct _malloc_zone_t *zone, - void **to_be_freed, unsigned num_to_be_freed); -static size_t zone_pressure_relief(struct _malloc_zone_t *zone, size_t goal); -static size_t zone_good_size(malloc_zone_t *zone, size_t size); -static kern_return_t zone_enumerator(task_t task, void *data, unsigned type_mask, - vm_address_t zone_address, memory_reader_t reader, - vm_range_recorder_t recorder); -static boolean_t zone_check(malloc_zone_t *zone); -static void zone_print(malloc_zone_t *zone, boolean_t verbose); -static void zone_log(malloc_zone_t *zone, void *address); -static void zone_force_lock(malloc_zone_t *zone); -static void zone_force_unlock(malloc_zone_t *zone); -static void zone_statistics(malloc_zone_t *zone, - malloc_statistics_t *stats); -static boolean_t zone_locked(malloc_zone_t *zone); -static void zone_reinit_lock(malloc_zone_t *zone); - -/******************************************************************************/ -/* - * Functions. - */ - -static size_t -zone_size(malloc_zone_t *zone, const void *ptr) { - /* - * There appear to be places within Darwin (such as setenv(3)) that - * cause calls to this function with pointers that *no* zone owns. If - * we knew that all pointers were owned by *some* zone, we could split - * our zone into two parts, and use one as the default allocator and - * the other as the default deallocator/reallocator. Since that will - * not work in practice, we must check all pointers to assure that they - * reside within a mapped extent before determining size. - */ - return ivsalloc(tsdn_fetch(), ptr); -} - -static void * -zone_malloc(malloc_zone_t *zone, size_t size) { - return je_malloc(size); -} - -static void * -zone_calloc(malloc_zone_t *zone, size_t num, size_t size) { - return je_calloc(num, size); -} - -static void * -zone_valloc(malloc_zone_t *zone, size_t size) { - void *ret = NULL; /* Assignment avoids useless compiler warning. */ - - je_posix_memalign(&ret, PAGE, size); - - return ret; -} - -static void -zone_free(malloc_zone_t *zone, void *ptr) { - if (ivsalloc(tsdn_fetch(), ptr) != 0) { - je_free(ptr); - return; - } - - free(ptr); -} - -static void * -zone_realloc(malloc_zone_t *zone, void *ptr, size_t size) { - if (ivsalloc(tsdn_fetch(), ptr) != 0) { - return je_realloc(ptr, size); - } - - return realloc(ptr, size); -} - -static void * -zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) { - void *ret = NULL; /* Assignment avoids useless compiler warning. */ - - je_posix_memalign(&ret, alignment, size); - - return ret; -} - -static void -zone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) { - size_t alloc_size; - - alloc_size = ivsalloc(tsdn_fetch(), ptr); - if (alloc_size != 0) { - assert(alloc_size == size); - je_free(ptr); - return; - } - - free(ptr); -} - -static void -zone_destroy(malloc_zone_t *zone) { - /* This function should never be called. */ - not_reached(); -} - -static unsigned -zone_batch_malloc(struct _malloc_zone_t *zone, size_t size, void **results, - unsigned num_requested) { - unsigned i; - - for (i = 0; i < num_requested; i++) { - results[i] = je_malloc(size); - if (!results[i]) - break; - } - - return i; -} - -static void -zone_batch_free(struct _malloc_zone_t *zone, void **to_be_freed, - unsigned num_to_be_freed) { - unsigned i; - - for (i = 0; i < num_to_be_freed; i++) { - zone_free(zone, to_be_freed[i]); - to_be_freed[i] = NULL; - } -} - -static size_t -zone_pressure_relief(struct _malloc_zone_t *zone, size_t goal) { - return 0; -} - -static size_t -zone_good_size(malloc_zone_t *zone, size_t size) { - if (size == 0) { - size = 1; - } - return sz_s2u(size); -} - -static kern_return_t -zone_enumerator(task_t task, void *data, unsigned type_mask, - vm_address_t zone_address, memory_reader_t reader, - vm_range_recorder_t recorder) { - return KERN_SUCCESS; -} - -static boolean_t -zone_check(malloc_zone_t *zone) { - return true; -} - -static void -zone_print(malloc_zone_t *zone, boolean_t verbose) { -} - -static void -zone_log(malloc_zone_t *zone, void *address) { -} - -static void -zone_force_lock(malloc_zone_t *zone) { - if (isthreaded) { - /* - * See the note in zone_force_unlock, below, to see why we need - * this. - */ - assert(zone_force_lock_pid == -1); - zone_force_lock_pid = getpid(); - jemalloc_prefork(); - } -} - -static void -zone_force_unlock(malloc_zone_t *zone) { - /* - * zone_force_lock and zone_force_unlock are the entry points to the - * forking machinery on OS X. The tricky thing is, the child is not - * allowed to unlock mutexes locked in the parent, even if owned by the - * forking thread (and the mutex type we use in OS X will fail an assert - * if we try). In the child, we can get away with reinitializing all - * the mutexes, which has the effect of unlocking them. In the parent, - * doing this would mean we wouldn't wake any waiters blocked on the - * mutexes we unlock. So, we record the pid of the current thread in - * zone_force_lock, and use that to detect if we're in the parent or - * child here, to decide which unlock logic we need. - */ - if (isthreaded) { - assert(zone_force_lock_pid != -1); - if (getpid() == zone_force_lock_pid) { - jemalloc_postfork_parent(); - } else { - jemalloc_postfork_child(); - } - zone_force_lock_pid = -1; - } -} - -static void -zone_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { - /* We make no effort to actually fill the values */ - stats->blocks_in_use = 0; - stats->size_in_use = 0; - stats->max_size_in_use = 0; - stats->size_allocated = 0; -} - -static boolean_t -zone_locked(malloc_zone_t *zone) { - /* Pretend no lock is being held */ - return false; -} - -static void -zone_reinit_lock(malloc_zone_t *zone) { - /* As of OSX 10.12, this function is only used when force_unlock would - * be used if the zone version were < 9. So just use force_unlock. */ - zone_force_unlock(zone); -} - -static void -zone_init(void) { - jemalloc_zone.size = zone_size; - jemalloc_zone.malloc = zone_malloc; - jemalloc_zone.calloc = zone_calloc; - jemalloc_zone.valloc = zone_valloc; - jemalloc_zone.free = zone_free; - jemalloc_zone.realloc = zone_realloc; - jemalloc_zone.destroy = zone_destroy; - jemalloc_zone.zone_name = "jemalloc_zone"; - jemalloc_zone.batch_malloc = zone_batch_malloc; - jemalloc_zone.batch_free = zone_batch_free; - jemalloc_zone.introspect = &jemalloc_zone_introspect; - jemalloc_zone.version = 9; - jemalloc_zone.memalign = zone_memalign; - jemalloc_zone.free_definite_size = zone_free_definite_size; - jemalloc_zone.pressure_relief = zone_pressure_relief; - - jemalloc_zone_introspect.enumerator = zone_enumerator; - jemalloc_zone_introspect.good_size = zone_good_size; - jemalloc_zone_introspect.check = zone_check; - jemalloc_zone_introspect.print = zone_print; - jemalloc_zone_introspect.log = zone_log; - jemalloc_zone_introspect.force_lock = zone_force_lock; - jemalloc_zone_introspect.force_unlock = zone_force_unlock; - jemalloc_zone_introspect.statistics = zone_statistics; - jemalloc_zone_introspect.zone_locked = zone_locked; - jemalloc_zone_introspect.enable_discharge_checking = NULL; - jemalloc_zone_introspect.disable_discharge_checking = NULL; - jemalloc_zone_introspect.discharge = NULL; -#ifdef __BLOCKS__ - jemalloc_zone_introspect.enumerate_discharged_pointers = NULL; -#else - jemalloc_zone_introspect.enumerate_unavailable_without_blocks = NULL; -#endif - jemalloc_zone_introspect.reinit_lock = zone_reinit_lock; -} - -static malloc_zone_t * -zone_default_get(void) { - malloc_zone_t **zones = NULL; - unsigned int num_zones = 0; - - /* - * On OSX 10.12, malloc_default_zone returns a special zone that is not - * present in the list of registered zones. That zone uses a "lite zone" - * if one is present (apparently enabled when malloc stack logging is - * enabled), or the first registered zone otherwise. In practice this - * means unless malloc stack logging is enabled, the first registered - * zone is the default. So get the list of zones to get the first one, - * instead of relying on malloc_default_zone. - */ - if (KERN_SUCCESS != malloc_get_all_zones(0, NULL, - (vm_address_t**)&zones, &num_zones)) { - /* - * Reset the value in case the failure happened after it was - * set. - */ - num_zones = 0; - } - - if (num_zones) { - return zones[0]; - } - - return malloc_default_zone(); -} - -/* As written, this function can only promote jemalloc_zone. */ -static void -zone_promote(void) { - malloc_zone_t *zone; - - do { - /* - * Unregister and reregister the default zone. On OSX >= 10.6, - * unregistering takes the last registered zone and places it - * at the location of the specified zone. Unregistering the - * default zone thus makes the last registered one the default. - * On OSX < 10.6, unregistering shifts all registered zones. - * The first registered zone then becomes the default. - */ - malloc_zone_unregister(default_zone); - malloc_zone_register(default_zone); - - /* - * On OSX 10.6, having the default purgeable zone appear before - * the default zone makes some things crash because it thinks it - * owns the default zone allocated pointers. We thus - * unregister/re-register it in order to ensure it's always - * after the default zone. On OSX < 10.6, there is no purgeable - * zone, so this does nothing. On OSX >= 10.6, unregistering - * replaces the purgeable zone with the last registered zone - * above, i.e. the default zone. Registering it again then puts - * it at the end, obviously after the default zone. - */ - if (purgeable_zone != NULL) { - malloc_zone_unregister(purgeable_zone); - malloc_zone_register(purgeable_zone); - } - - zone = zone_default_get(); - } while (zone != &jemalloc_zone); -} - -JEMALLOC_ATTR(constructor) -void -zone_register(void) { - /* - * If something else replaced the system default zone allocator, don't - * register jemalloc's. - */ - default_zone = zone_default_get(); - if (!default_zone->zone_name || strcmp(default_zone->zone_name, - "DefaultMallocZone") != 0) { - return; - } - - /* - * The default purgeable zone is created lazily by OSX's libc. It uses - * the default zone when it is created for "small" allocations - * (< 15 KiB), but assumes the default zone is a scalable_zone. This - * obviously fails when the default zone is the jemalloc zone, so - * malloc_default_purgeable_zone() is called beforehand so that the - * default purgeable zone is created when the default zone is still - * a scalable_zone. As purgeable zones only exist on >= 10.6, we need - * to check for the existence of malloc_default_purgeable_zone() at - * run time. - */ - purgeable_zone = (malloc_default_purgeable_zone == NULL) ? NULL : - malloc_default_purgeable_zone(); - - /* Register the custom zone. At this point it won't be the default. */ - zone_init(); - malloc_zone_register(&jemalloc_zone); - - /* Promote the custom zone to be default. */ - zone_promote(); -} |