xref: /aosp_15_r20/bionic/libc/bionic/jemalloc_wrapper.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker  * Copyright (C) 2014 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker  *
4*8d67ca89SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*8d67ca89SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*8d67ca89SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*8d67ca89SAndroid Build Coastguard Worker  *
8*8d67ca89SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*8d67ca89SAndroid Build Coastguard Worker  *
10*8d67ca89SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*8d67ca89SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*8d67ca89SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*8d67ca89SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*8d67ca89SAndroid Build Coastguard Worker  * limitations under the License.
15*8d67ca89SAndroid Build Coastguard Worker  */
16*8d67ca89SAndroid Build Coastguard Worker 
17*8d67ca89SAndroid Build Coastguard Worker #include <errno.h>
18*8d67ca89SAndroid Build Coastguard Worker #include <inttypes.h>
19*8d67ca89SAndroid Build Coastguard Worker #include <malloc.h>
20*8d67ca89SAndroid Build Coastguard Worker #include <sys/param.h>
21*8d67ca89SAndroid Build Coastguard Worker #include <unistd.h>
22*8d67ca89SAndroid Build Coastguard Worker 
23*8d67ca89SAndroid Build Coastguard Worker #include <async_safe/log.h>
24*8d67ca89SAndroid Build Coastguard Worker #include <private/MallocXmlElem.h>
25*8d67ca89SAndroid Build Coastguard Worker 
26*8d67ca89SAndroid Build Coastguard Worker #include "jemalloc.h"
27*8d67ca89SAndroid Build Coastguard Worker 
28*8d67ca89SAndroid Build Coastguard Worker __BEGIN_DECLS
29*8d67ca89SAndroid Build Coastguard Worker 
30*8d67ca89SAndroid Build Coastguard Worker size_t je_mallinfo_narenas();
31*8d67ca89SAndroid Build Coastguard Worker size_t je_mallinfo_nbins();
32*8d67ca89SAndroid Build Coastguard Worker struct mallinfo je_mallinfo_arena_info(size_t);
33*8d67ca89SAndroid Build Coastguard Worker struct mallinfo je_mallinfo_bin_info(size_t, size_t);
34*8d67ca89SAndroid Build Coastguard Worker void je_stats_arena(size_t arena_index, void (*callback)(size_t, size_t, size_t));
35*8d67ca89SAndroid Build Coastguard Worker 
36*8d67ca89SAndroid Build Coastguard Worker __END_DECLS
37*8d67ca89SAndroid Build Coastguard Worker 
je_pvalloc(size_t bytes)38*8d67ca89SAndroid Build Coastguard Worker void* je_pvalloc(size_t bytes) {
39*8d67ca89SAndroid Build Coastguard Worker   size_t pagesize = getpagesize();
40*8d67ca89SAndroid Build Coastguard Worker   size_t size = __BIONIC_ALIGN(bytes, pagesize);
41*8d67ca89SAndroid Build Coastguard Worker   if (size < bytes) {
42*8d67ca89SAndroid Build Coastguard Worker     return nullptr;
43*8d67ca89SAndroid Build Coastguard Worker   }
44*8d67ca89SAndroid Build Coastguard Worker   return je_memalign(pagesize, size);
45*8d67ca89SAndroid Build Coastguard Worker }
46*8d67ca89SAndroid Build Coastguard Worker 
47*8d67ca89SAndroid Build Coastguard Worker #ifdef je_memalign
48*8d67ca89SAndroid Build Coastguard Worker #undef je_memalign
49*8d67ca89SAndroid Build Coastguard Worker #endif
50*8d67ca89SAndroid Build Coastguard Worker 
51*8d67ca89SAndroid Build Coastguard Worker // The man page for memalign says it fails if boundary is not a power of 2,
52*8d67ca89SAndroid Build Coastguard Worker // but this is not true. Both glibc and dlmalloc round up to the next power
53*8d67ca89SAndroid Build Coastguard Worker // of 2, so we'll do the same.
je_memalign_round_up_boundary(size_t boundary,size_t size)54*8d67ca89SAndroid Build Coastguard Worker void* je_memalign_round_up_boundary(size_t boundary, size_t size) {
55*8d67ca89SAndroid Build Coastguard Worker   if (boundary != 0) {
56*8d67ca89SAndroid Build Coastguard Worker     if (!powerof2(boundary)) {
57*8d67ca89SAndroid Build Coastguard Worker       boundary = BIONIC_ROUND_UP_POWER_OF_2(boundary);
58*8d67ca89SAndroid Build Coastguard Worker     }
59*8d67ca89SAndroid Build Coastguard Worker   } else {
60*8d67ca89SAndroid Build Coastguard Worker     boundary = 1;
61*8d67ca89SAndroid Build Coastguard Worker   }
62*8d67ca89SAndroid Build Coastguard Worker   return je_memalign(boundary, size);
63*8d67ca89SAndroid Build Coastguard Worker }
64*8d67ca89SAndroid Build Coastguard Worker 
65*8d67ca89SAndroid Build Coastguard Worker #ifdef je_aligned_alloc
66*8d67ca89SAndroid Build Coastguard Worker #undef je_aligned_alloc
67*8d67ca89SAndroid Build Coastguard Worker #endif
68*8d67ca89SAndroid Build Coastguard Worker 
69*8d67ca89SAndroid Build Coastguard Worker // The aligned_alloc function requires that size is a multiple of alignment.
70*8d67ca89SAndroid Build Coastguard Worker // jemalloc doesn't enforce this, so add enforcement here.
je_aligned_alloc_wrapper(size_t alignment,size_t size)71*8d67ca89SAndroid Build Coastguard Worker void* je_aligned_alloc_wrapper(size_t alignment, size_t size) {
72*8d67ca89SAndroid Build Coastguard Worker   if ((size % alignment) != 0) {
73*8d67ca89SAndroid Build Coastguard Worker     errno = EINVAL;
74*8d67ca89SAndroid Build Coastguard Worker     return nullptr;
75*8d67ca89SAndroid Build Coastguard Worker   }
76*8d67ca89SAndroid Build Coastguard Worker   return je_aligned_alloc(alignment, size);
77*8d67ca89SAndroid Build Coastguard Worker }
78*8d67ca89SAndroid Build Coastguard Worker 
je_mallopt(int param,int value)79*8d67ca89SAndroid Build Coastguard Worker int je_mallopt(int param, int value) {
80*8d67ca89SAndroid Build Coastguard Worker   // The only parameter we currently understand is M_DECAY_TIME.
81*8d67ca89SAndroid Build Coastguard Worker   if (param == M_DECAY_TIME) {
82*8d67ca89SAndroid Build Coastguard Worker     // Only support setting the value to -1 or 0 or 1.
83*8d67ca89SAndroid Build Coastguard Worker     ssize_t decay_time_ms;
84*8d67ca89SAndroid Build Coastguard Worker     if (value < 0) {
85*8d67ca89SAndroid Build Coastguard Worker       // Given that SSIZE_MAX may not be supported in jemalloc, set this to a
86*8d67ca89SAndroid Build Coastguard Worker       // sufficiently large number that essentially disables the decay timer.
87*8d67ca89SAndroid Build Coastguard Worker       decay_time_ms = 10000000;
88*8d67ca89SAndroid Build Coastguard Worker     } else if (value) {
89*8d67ca89SAndroid Build Coastguard Worker       decay_time_ms = 1000;
90*8d67ca89SAndroid Build Coastguard Worker     } else {
91*8d67ca89SAndroid Build Coastguard Worker       decay_time_ms = 0;
92*8d67ca89SAndroid Build Coastguard Worker     }
93*8d67ca89SAndroid Build Coastguard Worker     // First get the total number of arenas.
94*8d67ca89SAndroid Build Coastguard Worker     unsigned narenas;
95*8d67ca89SAndroid Build Coastguard Worker     size_t sz = sizeof(unsigned);
96*8d67ca89SAndroid Build Coastguard Worker     if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
97*8d67ca89SAndroid Build Coastguard Worker       return 0;
98*8d67ca89SAndroid Build Coastguard Worker     }
99*8d67ca89SAndroid Build Coastguard Worker 
100*8d67ca89SAndroid Build Coastguard Worker     // Set the decay time for any arenas that will be created in the future.
101*8d67ca89SAndroid Build Coastguard Worker     if (je_mallctl("arenas.dirty_decay_ms", nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
102*8d67ca89SAndroid Build Coastguard Worker       return 0;
103*8d67ca89SAndroid Build Coastguard Worker     }
104*8d67ca89SAndroid Build Coastguard Worker     if (je_mallctl("arenas.muzzy_decay_ms", nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
105*8d67ca89SAndroid Build Coastguard Worker       return 0;
106*8d67ca89SAndroid Build Coastguard Worker     }
107*8d67ca89SAndroid Build Coastguard Worker 
108*8d67ca89SAndroid Build Coastguard Worker     // Change the decay on the already existing arenas.
109*8d67ca89SAndroid Build Coastguard Worker     char buffer[100];
110*8d67ca89SAndroid Build Coastguard Worker     for (unsigned i = 0; i < narenas; i++) {
111*8d67ca89SAndroid Build Coastguard Worker       snprintf(buffer, sizeof(buffer), "arena.%d.dirty_decay_ms", i);
112*8d67ca89SAndroid Build Coastguard Worker       if (je_mallctl(buffer, nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
113*8d67ca89SAndroid Build Coastguard Worker         break;
114*8d67ca89SAndroid Build Coastguard Worker       }
115*8d67ca89SAndroid Build Coastguard Worker       snprintf(buffer, sizeof(buffer), "arena.%d.muzzy_decay_ms", i);
116*8d67ca89SAndroid Build Coastguard Worker       if (je_mallctl(buffer, nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
117*8d67ca89SAndroid Build Coastguard Worker         break;
118*8d67ca89SAndroid Build Coastguard Worker       }
119*8d67ca89SAndroid Build Coastguard Worker     }
120*8d67ca89SAndroid Build Coastguard Worker     return 1;
121*8d67ca89SAndroid Build Coastguard Worker   } else if (param == M_PURGE || param == M_PURGE_ALL) {
122*8d67ca89SAndroid Build Coastguard Worker     // Only clear the current thread cache since there is no easy way to
123*8d67ca89SAndroid Build Coastguard Worker     // clear the caches of other threads.
124*8d67ca89SAndroid Build Coastguard Worker     // This must be done first so that cleared allocations get purged
125*8d67ca89SAndroid Build Coastguard Worker     // in the next calls.
126*8d67ca89SAndroid Build Coastguard Worker     // Ignore the return call since this will fail if the tcache is disabled.
127*8d67ca89SAndroid Build Coastguard Worker     je_mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0);
128*8d67ca89SAndroid Build Coastguard Worker 
129*8d67ca89SAndroid Build Coastguard Worker     unsigned narenas;
130*8d67ca89SAndroid Build Coastguard Worker     size_t sz = sizeof(unsigned);
131*8d67ca89SAndroid Build Coastguard Worker     if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
132*8d67ca89SAndroid Build Coastguard Worker       return 0;
133*8d67ca89SAndroid Build Coastguard Worker     }
134*8d67ca89SAndroid Build Coastguard Worker     char buffer[100];
135*8d67ca89SAndroid Build Coastguard Worker     snprintf(buffer, sizeof(buffer), "arena.%u.purge", narenas);
136*8d67ca89SAndroid Build Coastguard Worker     if (je_mallctl(buffer, nullptr, nullptr, nullptr, 0) != 0) {
137*8d67ca89SAndroid Build Coastguard Worker       return 0;
138*8d67ca89SAndroid Build Coastguard Worker     }
139*8d67ca89SAndroid Build Coastguard Worker     return 1;
140*8d67ca89SAndroid Build Coastguard Worker   } else if (param == M_LOG_STATS) {
141*8d67ca89SAndroid Build Coastguard Worker     size_t total_bytes = 0;
142*8d67ca89SAndroid Build Coastguard Worker     for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
143*8d67ca89SAndroid Build Coastguard Worker       struct mallinfo mi = je_mallinfo_arena_info(i);
144*8d67ca89SAndroid Build Coastguard Worker       size_t arena_bytes = mi.fsmblks + mi.ordblks + mi.uordblks;
145*8d67ca89SAndroid Build Coastguard Worker       async_safe_format_log(ANDROID_LOG_INFO, "jemalloc",
146*8d67ca89SAndroid Build Coastguard Worker                             "Arena %zu: bin bytes=%zu large bytes=%zu total bytes=%zu", i,
147*8d67ca89SAndroid Build Coastguard Worker                             mi.fsmblks, mi.ordblks, arena_bytes);
148*8d67ca89SAndroid Build Coastguard Worker 
149*8d67ca89SAndroid Build Coastguard Worker       je_stats_arena(i, [](size_t index, size_t size, size_t allocs) {
150*8d67ca89SAndroid Build Coastguard Worker         if (allocs != 0) {
151*8d67ca89SAndroid Build Coastguard Worker           async_safe_format_log(ANDROID_LOG_INFO, "jemalloc",
152*8d67ca89SAndroid Build Coastguard Worker                                 "  Size Class %zu(%zu bytes): allocs=%zu total bytes=%zu", index,
153*8d67ca89SAndroid Build Coastguard Worker                                 size, allocs, allocs * size);
154*8d67ca89SAndroid Build Coastguard Worker         }
155*8d67ca89SAndroid Build Coastguard Worker       });
156*8d67ca89SAndroid Build Coastguard Worker       total_bytes += arena_bytes;
157*8d67ca89SAndroid Build Coastguard Worker     }
158*8d67ca89SAndroid Build Coastguard Worker     async_safe_format_log(ANDROID_LOG_INFO, "jemalloc", "Total Bytes=%zu", total_bytes);
159*8d67ca89SAndroid Build Coastguard Worker     return 1;
160*8d67ca89SAndroid Build Coastguard Worker   }
161*8d67ca89SAndroid Build Coastguard Worker 
162*8d67ca89SAndroid Build Coastguard Worker   return 0;
163*8d67ca89SAndroid Build Coastguard Worker }
164*8d67ca89SAndroid Build Coastguard Worker 
je_malloc_info(int options,FILE * fp)165*8d67ca89SAndroid Build Coastguard Worker int je_malloc_info(int options, FILE* fp) {
166*8d67ca89SAndroid Build Coastguard Worker   if (options != 0) {
167*8d67ca89SAndroid Build Coastguard Worker     errno = EINVAL;
168*8d67ca89SAndroid Build Coastguard Worker     return -1;
169*8d67ca89SAndroid Build Coastguard Worker   }
170*8d67ca89SAndroid Build Coastguard Worker 
171*8d67ca89SAndroid Build Coastguard Worker   fflush(fp);
172*8d67ca89SAndroid Build Coastguard Worker   int fd = fileno(fp);
173*8d67ca89SAndroid Build Coastguard Worker   MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
174*8d67ca89SAndroid Build Coastguard Worker 
175*8d67ca89SAndroid Build Coastguard Worker   // Dump all of the large allocations in the arenas.
176*8d67ca89SAndroid Build Coastguard Worker   for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
177*8d67ca89SAndroid Build Coastguard Worker     struct mallinfo mi = je_mallinfo_arena_info(i);
178*8d67ca89SAndroid Build Coastguard Worker     if (mi.hblkhd != 0) {
179*8d67ca89SAndroid Build Coastguard Worker       MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
180*8d67ca89SAndroid Build Coastguard Worker       {
181*8d67ca89SAndroid Build Coastguard Worker         MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
182*8d67ca89SAndroid Build Coastguard Worker         MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
183*8d67ca89SAndroid Build Coastguard Worker         MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
184*8d67ca89SAndroid Build Coastguard Worker 
185*8d67ca89SAndroid Build Coastguard Worker         size_t total = 0;
186*8d67ca89SAndroid Build Coastguard Worker         for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
187*8d67ca89SAndroid Build Coastguard Worker           struct mallinfo mi = je_mallinfo_bin_info(i, j);
188*8d67ca89SAndroid Build Coastguard Worker           if (mi.ordblks != 0) {
189*8d67ca89SAndroid Build Coastguard Worker             MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
190*8d67ca89SAndroid Build Coastguard Worker             MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
191*8d67ca89SAndroid Build Coastguard Worker             MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
192*8d67ca89SAndroid Build Coastguard Worker             MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
193*8d67ca89SAndroid Build Coastguard Worker             total += mi.ordblks;
194*8d67ca89SAndroid Build Coastguard Worker           }
195*8d67ca89SAndroid Build Coastguard Worker         }
196*8d67ca89SAndroid Build Coastguard Worker         MallocXmlElem(fd, "bins-total").Contents("%zu", total);
197*8d67ca89SAndroid Build Coastguard Worker       }
198*8d67ca89SAndroid Build Coastguard Worker     }
199*8d67ca89SAndroid Build Coastguard Worker   }
200*8d67ca89SAndroid Build Coastguard Worker 
201*8d67ca89SAndroid Build Coastguard Worker   return 0;
202*8d67ca89SAndroid Build Coastguard Worker }
203