| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef MOZ_JEMALLOC4 |
| # error Should only compile this file when building with jemalloc 3 |
| #endif |
| |
| #define MOZ_JEMALLOC_IMPL |
| |
| #include "mozmemory_wrap.h" |
| #include "jemalloc_types.h" |
| #include "mozilla/Types.h" |
| |
| #include <stdbool.h> |
| |
| #if defined(MOZ_NATIVE_JEMALLOC) |
| # include MALLOC_H |
| #else |
| # include "jemalloc/jemalloc.h" |
| #endif |
| |
| /* |
| * CTL_* macros are from memory/jemalloc/src/src/stats.c with changes: |
| * - drop `t' argument to avoid redundancy in calculating type size |
| * - require `i' argument for arena number explicitly |
| */ |
| |
| #define CTL_GET(n, v) do { \ |
| size_t sz = sizeof(v); \ |
| je_(mallctl)(n, &v, &sz, NULL, 0); \ |
| } while (0) |
| |
| #define CTL_I_GET(n, v, i) do { \ |
| size_t mib[6]; \ |
| size_t miblen = sizeof(mib) / sizeof(mib[0]); \ |
| size_t sz = sizeof(v); \ |
| je_(mallctlnametomib)(n, mib, &miblen); \ |
| mib[2] = i; \ |
| je_(mallctlbymib)(mib, miblen, &v, &sz, NULL, 0); \ |
| } while (0) |
| |
| #define CTL_IJ_GET(n, v, i, j) do { \ |
| size_t mib[6]; \ |
| size_t miblen = sizeof(mib) / sizeof(mib[0]); \ |
| size_t sz = sizeof(v); \ |
| je_(mallctlnametomib)(n, mib, &miblen); \ |
| mib[2] = i; \ |
| mib[4] = j; \ |
| je_(mallctlbymib)(mib, miblen, &v, &sz, NULL, 0); \ |
| } while (0) |
| |
| /* |
| * VARIABLE_ARRAY is copied from |
| * memory/jemalloc/src/include/jemalloc/internal/jemalloc_internal.h.in |
| */ |
| #if __STDC_VERSION__ < 199901L |
| # ifdef _MSC_VER |
| # include <malloc.h> |
| # define alloca _alloca |
| # else |
| # ifdef HAVE_ALLOCA_H |
| # include <alloca.h> |
| # else |
| # include <stdlib.h> |
| # endif |
| # endif |
| # define VARIABLE_ARRAY(type, name, count) \ |
| type *name = alloca(sizeof(type) * (count)) |
| #else |
| # define VARIABLE_ARRAY(type, name, count) type name[(count)] |
| #endif |
| |
| MOZ_MEMORY_API size_t |
| malloc_good_size_impl(size_t size) |
| { |
| /* je_nallocx crashes when given a size of 0. As |
| * malloc_usable_size(malloc(0)) and malloc_usable_size(malloc(1)) |
| * return the same value, use a size of 1. */ |
| if (size == 0) |
| size = 1; |
| return je_(nallocx)(size, 0); |
| } |
| |
| static void |
| compute_bin_unused_and_bookkeeping(jemalloc_stats_t *stats, unsigned int narenas) |
| { |
| size_t bin_unused = 0; |
| |
| uint32_t nregs; // number of regions per run in the j-th bin |
| size_t reg_size; // size of regions served by the j-th bin |
| size_t curruns; // number of runs belonging to a bin |
| size_t curregs; // number of allocated regions in a bin |
| |
| unsigned int nbins; // number of bins per arena |
| unsigned int i, j; |
| |
| size_t stats_metadata; |
| size_t stats_ametadata = 0; // total internal allocations in all arenas |
| |
| // narenas also counts uninitialized arenas, and initialized arenas |
| // are not guaranteed to be adjacent |
| VARIABLE_ARRAY(bool, initialized, narenas); |
| size_t isz = sizeof(initialized) / sizeof(initialized[0]); |
| |
| je_(mallctl)("arenas.initialized", initialized, &isz, NULL, 0); |
| CTL_GET("arenas.nbins", nbins); |
| |
| for (j = 0; j < nbins; j++) { |
| CTL_I_GET("arenas.bin.0.nregs", nregs, j); |
| CTL_I_GET("arenas.bin.0.size", reg_size, j); |
| |
| for (i = 0; i < narenas; i++) { |
| if (!initialized[i]) { |
| continue; |
| } |
| |
| CTL_IJ_GET("stats.arenas.0.bins.0.curruns", curruns, i, j); |
| CTL_IJ_GET("stats.arenas.0.bins.0.curregs", curregs, i, j); |
| |
| bin_unused += (nregs * curruns - curregs) * reg_size; |
| } |
| } |
| |
| CTL_GET("stats.metadata", stats_metadata); |
| |
| /* get the summation for all arenas, i == narenas */ |
| CTL_I_GET("stats.arenas.0.metadata.allocated", stats_ametadata, narenas); |
| |
| stats->bookkeeping = stats_metadata - stats_ametadata; |
| stats->bin_unused = bin_unused; |
| } |
| |
| MOZ_JEMALLOC_API void |
| jemalloc_stats_impl(jemalloc_stats_t *stats) |
| { |
| unsigned narenas; |
| size_t active, allocated, mapped, page, pdirty; |
| size_t lg_chunk; |
| |
| // Refresh jemalloc's stats by updating its epoch, see ctl_refresh in |
| // src/ctl.c |
| uint64_t epoch = 0; |
| size_t esz = sizeof(epoch); |
| int ret = je_(mallctl)("epoch", &epoch, &esz, &epoch, esz); |
| |
| CTL_GET("arenas.narenas", narenas); |
| CTL_GET("arenas.page", page); |
| CTL_GET("stats.active", active); |
| CTL_GET("stats.allocated", allocated); |
| CTL_GET("stats.mapped", mapped); |
| CTL_GET("opt.lg_chunk", lg_chunk); |
| |
| /* get the summation for all arenas, i == narenas */ |
| CTL_I_GET("stats.arenas.0.pdirty", pdirty, narenas); |
| |
| stats->chunksize = (size_t) 1 << lg_chunk; |
| stats->mapped = mapped; |
| stats->allocated = allocated; |
| stats->waste = active - allocated; |
| stats->page_cache = pdirty * page; |
| compute_bin_unused_and_bookkeeping(stats, narenas); |
| stats->waste -= stats->bin_unused; |
| } |
| |
| MOZ_JEMALLOC_API void |
| jemalloc_purge_freed_pages_impl() |
| { |
| } |
| |
| MOZ_JEMALLOC_API void |
| jemalloc_free_dirty_pages_impl() |
| { |
| unsigned narenas; |
| size_t mib[3]; |
| size_t miblen = sizeof(mib) / sizeof(mib[0]); |
| |
| CTL_GET("arenas.narenas", narenas); |
| je_(mallctlnametomib)("arena.0.purge", mib, &miblen); |
| mib[1] = narenas; |
| je_(mallctlbymib)(mib, miblen, NULL, NULL, NULL, 0); |
| } |