DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (fddffdeab170)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
/* 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);
}