summaryrefslogtreecommitdiffstats
path: root/memory/build/mozjemalloc_compat.c
blob: 6591892c137251729f15a11b2eeaa95f16f10c1e (plain)
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_SYSTEM_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);
  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);
}