1*1208bc7eSAndroid Build Coastguard Worker #include "test/jemalloc_test.h"
2*1208bc7eSAndroid Build Coastguard Worker
3*1208bc7eSAndroid Build Coastguard Worker #include "jemalloc/internal/spin.h"
4*1208bc7eSAndroid Build Coastguard Worker
5*1208bc7eSAndroid Build Coastguard Worker static unsigned arena_ind;
6*1208bc7eSAndroid Build Coastguard Worker static size_t sz;
7*1208bc7eSAndroid Build Coastguard Worker static size_t esz;
8*1208bc7eSAndroid Build Coastguard Worker #define NEPOCHS 8
9*1208bc7eSAndroid Build Coastguard Worker #define PER_THD_NALLOCS 1
10*1208bc7eSAndroid Build Coastguard Worker static atomic_u_t epoch;
11*1208bc7eSAndroid Build Coastguard Worker static atomic_u_t nfinished;
12*1208bc7eSAndroid Build Coastguard Worker
13*1208bc7eSAndroid Build Coastguard Worker static unsigned
do_arena_create(extent_hooks_t * h)14*1208bc7eSAndroid Build Coastguard Worker do_arena_create(extent_hooks_t *h) {
15*1208bc7eSAndroid Build Coastguard Worker unsigned arena_ind;
16*1208bc7eSAndroid Build Coastguard Worker size_t sz = sizeof(unsigned);
17*1208bc7eSAndroid Build Coastguard Worker assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
18*1208bc7eSAndroid Build Coastguard Worker (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,
19*1208bc7eSAndroid Build Coastguard Worker "Unexpected mallctl() failure");
20*1208bc7eSAndroid Build Coastguard Worker return arena_ind;
21*1208bc7eSAndroid Build Coastguard Worker }
22*1208bc7eSAndroid Build Coastguard Worker
23*1208bc7eSAndroid Build Coastguard Worker static void
do_arena_destroy(unsigned arena_ind)24*1208bc7eSAndroid Build Coastguard Worker do_arena_destroy(unsigned arena_ind) {
25*1208bc7eSAndroid Build Coastguard Worker size_t mib[3];
26*1208bc7eSAndroid Build Coastguard Worker size_t miblen;
27*1208bc7eSAndroid Build Coastguard Worker
28*1208bc7eSAndroid Build Coastguard Worker miblen = sizeof(mib)/sizeof(size_t);
29*1208bc7eSAndroid Build Coastguard Worker assert_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0,
30*1208bc7eSAndroid Build Coastguard Worker "Unexpected mallctlnametomib() failure");
31*1208bc7eSAndroid Build Coastguard Worker mib[1] = (size_t)arena_ind;
32*1208bc7eSAndroid Build Coastguard Worker assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
33*1208bc7eSAndroid Build Coastguard Worker "Unexpected mallctlbymib() failure");
34*1208bc7eSAndroid Build Coastguard Worker }
35*1208bc7eSAndroid Build Coastguard Worker
36*1208bc7eSAndroid Build Coastguard Worker static void
do_refresh(void)37*1208bc7eSAndroid Build Coastguard Worker do_refresh(void) {
38*1208bc7eSAndroid Build Coastguard Worker uint64_t epoch = 1;
39*1208bc7eSAndroid Build Coastguard Worker assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
40*1208bc7eSAndroid Build Coastguard Worker sizeof(epoch)), 0, "Unexpected mallctl() failure");
41*1208bc7eSAndroid Build Coastguard Worker }
42*1208bc7eSAndroid Build Coastguard Worker
43*1208bc7eSAndroid Build Coastguard Worker static size_t
do_get_size_impl(const char * cmd,unsigned arena_ind)44*1208bc7eSAndroid Build Coastguard Worker do_get_size_impl(const char *cmd, unsigned arena_ind) {
45*1208bc7eSAndroid Build Coastguard Worker size_t mib[4];
46*1208bc7eSAndroid Build Coastguard Worker size_t miblen = sizeof(mib) / sizeof(size_t);
47*1208bc7eSAndroid Build Coastguard Worker size_t z = sizeof(size_t);
48*1208bc7eSAndroid Build Coastguard Worker
49*1208bc7eSAndroid Build Coastguard Worker assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
50*1208bc7eSAndroid Build Coastguard Worker 0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
51*1208bc7eSAndroid Build Coastguard Worker mib[2] = arena_ind;
52*1208bc7eSAndroid Build Coastguard Worker size_t size;
53*1208bc7eSAndroid Build Coastguard Worker assert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &z, NULL, 0),
54*1208bc7eSAndroid Build Coastguard Worker 0, "Unexpected mallctlbymib([\"%s\"], ...) failure", cmd);
55*1208bc7eSAndroid Build Coastguard Worker
56*1208bc7eSAndroid Build Coastguard Worker return size;
57*1208bc7eSAndroid Build Coastguard Worker }
58*1208bc7eSAndroid Build Coastguard Worker
59*1208bc7eSAndroid Build Coastguard Worker static size_t
do_get_active(unsigned arena_ind)60*1208bc7eSAndroid Build Coastguard Worker do_get_active(unsigned arena_ind) {
61*1208bc7eSAndroid Build Coastguard Worker return do_get_size_impl("stats.arenas.0.pactive", arena_ind) * PAGE;
62*1208bc7eSAndroid Build Coastguard Worker }
63*1208bc7eSAndroid Build Coastguard Worker
64*1208bc7eSAndroid Build Coastguard Worker static size_t
do_get_mapped(unsigned arena_ind)65*1208bc7eSAndroid Build Coastguard Worker do_get_mapped(unsigned arena_ind) {
66*1208bc7eSAndroid Build Coastguard Worker return do_get_size_impl("stats.arenas.0.mapped", arena_ind);
67*1208bc7eSAndroid Build Coastguard Worker }
68*1208bc7eSAndroid Build Coastguard Worker
69*1208bc7eSAndroid Build Coastguard Worker static void *
thd_start(void * arg)70*1208bc7eSAndroid Build Coastguard Worker thd_start(void *arg) {
71*1208bc7eSAndroid Build Coastguard Worker for (unsigned next_epoch = 1; next_epoch < NEPOCHS; next_epoch++) {
72*1208bc7eSAndroid Build Coastguard Worker /* Busy-wait for next epoch. */
73*1208bc7eSAndroid Build Coastguard Worker unsigned cur_epoch;
74*1208bc7eSAndroid Build Coastguard Worker spin_t spinner = SPIN_INITIALIZER;
75*1208bc7eSAndroid Build Coastguard Worker while ((cur_epoch = atomic_load_u(&epoch, ATOMIC_ACQUIRE)) !=
76*1208bc7eSAndroid Build Coastguard Worker next_epoch) {
77*1208bc7eSAndroid Build Coastguard Worker spin_adaptive(&spinner);
78*1208bc7eSAndroid Build Coastguard Worker }
79*1208bc7eSAndroid Build Coastguard Worker assert_u_eq(cur_epoch, next_epoch, "Unexpected epoch");
80*1208bc7eSAndroid Build Coastguard Worker
81*1208bc7eSAndroid Build Coastguard Worker /*
82*1208bc7eSAndroid Build Coastguard Worker * Allocate. The main thread will reset the arena, so there's
83*1208bc7eSAndroid Build Coastguard Worker * no need to deallocate.
84*1208bc7eSAndroid Build Coastguard Worker */
85*1208bc7eSAndroid Build Coastguard Worker for (unsigned i = 0; i < PER_THD_NALLOCS; i++) {
86*1208bc7eSAndroid Build Coastguard Worker void *p = mallocx(sz, MALLOCX_ARENA(arena_ind) |
87*1208bc7eSAndroid Build Coastguard Worker MALLOCX_TCACHE_NONE
88*1208bc7eSAndroid Build Coastguard Worker );
89*1208bc7eSAndroid Build Coastguard Worker assert_ptr_not_null(p,
90*1208bc7eSAndroid Build Coastguard Worker "Unexpected mallocx() failure\n");
91*1208bc7eSAndroid Build Coastguard Worker }
92*1208bc7eSAndroid Build Coastguard Worker
93*1208bc7eSAndroid Build Coastguard Worker /* Let the main thread know we've finished this iteration. */
94*1208bc7eSAndroid Build Coastguard Worker atomic_fetch_add_u(&nfinished, 1, ATOMIC_RELEASE);
95*1208bc7eSAndroid Build Coastguard Worker }
96*1208bc7eSAndroid Build Coastguard Worker
97*1208bc7eSAndroid Build Coastguard Worker return NULL;
98*1208bc7eSAndroid Build Coastguard Worker }
99*1208bc7eSAndroid Build Coastguard Worker
TEST_BEGIN(test_retained)100*1208bc7eSAndroid Build Coastguard Worker TEST_BEGIN(test_retained) {
101*1208bc7eSAndroid Build Coastguard Worker test_skip_if(!config_stats);
102*1208bc7eSAndroid Build Coastguard Worker
103*1208bc7eSAndroid Build Coastguard Worker arena_ind = do_arena_create(NULL);
104*1208bc7eSAndroid Build Coastguard Worker sz = nallocx(HUGEPAGE, 0);
105*1208bc7eSAndroid Build Coastguard Worker esz = sz + sz_large_pad;
106*1208bc7eSAndroid Build Coastguard Worker
107*1208bc7eSAndroid Build Coastguard Worker atomic_store_u(&epoch, 0, ATOMIC_RELAXED);
108*1208bc7eSAndroid Build Coastguard Worker
109*1208bc7eSAndroid Build Coastguard Worker unsigned nthreads = ncpus * 2;
110*1208bc7eSAndroid Build Coastguard Worker VARIABLE_ARRAY(thd_t, threads, nthreads);
111*1208bc7eSAndroid Build Coastguard Worker for (unsigned i = 0; i < nthreads; i++) {
112*1208bc7eSAndroid Build Coastguard Worker thd_create(&threads[i], thd_start, NULL);
113*1208bc7eSAndroid Build Coastguard Worker }
114*1208bc7eSAndroid Build Coastguard Worker
115*1208bc7eSAndroid Build Coastguard Worker for (unsigned e = 1; e < NEPOCHS; e++) {
116*1208bc7eSAndroid Build Coastguard Worker atomic_store_u(&nfinished, 0, ATOMIC_RELEASE);
117*1208bc7eSAndroid Build Coastguard Worker atomic_store_u(&epoch, e, ATOMIC_RELEASE);
118*1208bc7eSAndroid Build Coastguard Worker
119*1208bc7eSAndroid Build Coastguard Worker /* Wait for threads to finish allocating. */
120*1208bc7eSAndroid Build Coastguard Worker spin_t spinner = SPIN_INITIALIZER;
121*1208bc7eSAndroid Build Coastguard Worker while (atomic_load_u(&nfinished, ATOMIC_ACQUIRE) < nthreads) {
122*1208bc7eSAndroid Build Coastguard Worker spin_adaptive(&spinner);
123*1208bc7eSAndroid Build Coastguard Worker }
124*1208bc7eSAndroid Build Coastguard Worker
125*1208bc7eSAndroid Build Coastguard Worker /*
126*1208bc7eSAndroid Build Coastguard Worker * Assert that retained is no more than the sum of size classes
127*1208bc7eSAndroid Build Coastguard Worker * that should have been used to satisfy the worker threads'
128*1208bc7eSAndroid Build Coastguard Worker * requests, discounting per growth fragmentation.
129*1208bc7eSAndroid Build Coastguard Worker */
130*1208bc7eSAndroid Build Coastguard Worker do_refresh();
131*1208bc7eSAndroid Build Coastguard Worker
132*1208bc7eSAndroid Build Coastguard Worker size_t allocated = esz * nthreads * PER_THD_NALLOCS;
133*1208bc7eSAndroid Build Coastguard Worker size_t active = do_get_active(arena_ind);
134*1208bc7eSAndroid Build Coastguard Worker assert_zu_le(allocated, active, "Unexpected active memory");
135*1208bc7eSAndroid Build Coastguard Worker size_t mapped = do_get_mapped(arena_ind);
136*1208bc7eSAndroid Build Coastguard Worker assert_zu_le(active, mapped, "Unexpected mapped memory");
137*1208bc7eSAndroid Build Coastguard Worker
138*1208bc7eSAndroid Build Coastguard Worker arena_t *arena = arena_get(tsdn_fetch(), arena_ind, false);
139*1208bc7eSAndroid Build Coastguard Worker size_t usable = 0;
140*1208bc7eSAndroid Build Coastguard Worker size_t fragmented = 0;
141*1208bc7eSAndroid Build Coastguard Worker for (pszind_t pind = sz_psz2ind(HUGEPAGE); pind <
142*1208bc7eSAndroid Build Coastguard Worker arena->extent_grow_next; pind++) {
143*1208bc7eSAndroid Build Coastguard Worker size_t psz = sz_pind2sz(pind);
144*1208bc7eSAndroid Build Coastguard Worker size_t psz_fragmented = psz % esz;
145*1208bc7eSAndroid Build Coastguard Worker size_t psz_usable = psz - psz_fragmented;
146*1208bc7eSAndroid Build Coastguard Worker /*
147*1208bc7eSAndroid Build Coastguard Worker * Only consider size classes that wouldn't be skipped.
148*1208bc7eSAndroid Build Coastguard Worker */
149*1208bc7eSAndroid Build Coastguard Worker if (psz_usable > 0) {
150*1208bc7eSAndroid Build Coastguard Worker assert_zu_lt(usable, allocated,
151*1208bc7eSAndroid Build Coastguard Worker "Excessive retained memory "
152*1208bc7eSAndroid Build Coastguard Worker "(%#zx[+%#zx] > %#zx)", usable, psz_usable,
153*1208bc7eSAndroid Build Coastguard Worker allocated);
154*1208bc7eSAndroid Build Coastguard Worker fragmented += psz_fragmented;
155*1208bc7eSAndroid Build Coastguard Worker usable += psz_usable;
156*1208bc7eSAndroid Build Coastguard Worker }
157*1208bc7eSAndroid Build Coastguard Worker }
158*1208bc7eSAndroid Build Coastguard Worker
159*1208bc7eSAndroid Build Coastguard Worker /*
160*1208bc7eSAndroid Build Coastguard Worker * Clean up arena. Destroying and recreating the arena
161*1208bc7eSAndroid Build Coastguard Worker * is simpler that specifying extent hooks that deallocate
162*1208bc7eSAndroid Build Coastguard Worker * (rather than retaining) during reset.
163*1208bc7eSAndroid Build Coastguard Worker */
164*1208bc7eSAndroid Build Coastguard Worker do_arena_destroy(arena_ind);
165*1208bc7eSAndroid Build Coastguard Worker assert_u_eq(do_arena_create(NULL), arena_ind,
166*1208bc7eSAndroid Build Coastguard Worker "Unexpected arena index");
167*1208bc7eSAndroid Build Coastguard Worker }
168*1208bc7eSAndroid Build Coastguard Worker
169*1208bc7eSAndroid Build Coastguard Worker for (unsigned i = 0; i < nthreads; i++) {
170*1208bc7eSAndroid Build Coastguard Worker thd_join(threads[i], NULL);
171*1208bc7eSAndroid Build Coastguard Worker }
172*1208bc7eSAndroid Build Coastguard Worker
173*1208bc7eSAndroid Build Coastguard Worker do_arena_destroy(arena_ind);
174*1208bc7eSAndroid Build Coastguard Worker }
175*1208bc7eSAndroid Build Coastguard Worker TEST_END
176*1208bc7eSAndroid Build Coastguard Worker
177*1208bc7eSAndroid Build Coastguard Worker int
main(void)178*1208bc7eSAndroid Build Coastguard Worker main(void) {
179*1208bc7eSAndroid Build Coastguard Worker return test(
180*1208bc7eSAndroid Build Coastguard Worker test_retained);
181*1208bc7eSAndroid Build Coastguard Worker }
182