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