blob: 41787037da600108b9ce2e4fee9e9c7ebef6d622 [file] [log] [blame]
Christopher Ferris72bbd422014-05-08 11:14:03 -07001/*
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
Christopher Ferrisa22f5d52019-03-01 16:40:59 -080017#include <errno.h>
Christopher Ferris4a7e1e32024-09-13 15:08:20 -070018#include <inttypes.h>
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070019#include <malloc.h>
Elliott Hughes731da1b2025-07-23 10:52:52 -070020#include <stdbit.h>
Christopher Ferris03eebcb2014-06-13 13:57:51 -070021#include <sys/param.h>
Christopher Ferris72bbd422014-05-08 11:14:03 -070022#include <unistd.h>
23
Christopher Ferrise9a7b812023-05-11 15:36:27 -070024#include <async_safe/log.h>
Christopher Ferris6c619a02019-03-01 17:59:51 -080025#include <private/MallocXmlElem.h>
26
Christopher Ferris72bbd422014-05-08 11:14:03 -070027#include "jemalloc.h"
28
Christopher Ferrise9a7b812023-05-11 15:36:27 -070029__BEGIN_DECLS
30
31size_t je_mallinfo_narenas();
32size_t je_mallinfo_nbins();
33struct mallinfo je_mallinfo_arena_info(size_t);
34struct mallinfo je_mallinfo_bin_info(size_t, size_t);
Christopher Ferris4a7e1e32024-09-13 15:08:20 -070035void je_stats_arena(size_t arena_index, void (*callback)(size_t, size_t, size_t));
Christopher Ferrise9a7b812023-05-11 15:36:27 -070036
37__END_DECLS
38
Christopher Ferris72bbd422014-05-08 11:14:03 -070039void* je_pvalloc(size_t bytes) {
Elliott Hughes91570ce2014-07-10 12:34:23 -070040 size_t pagesize = getpagesize();
Elliott Hughes193b0bc2025-05-14 06:35:50 -070041 size_t size = __builtin_align_up(bytes, pagesize);
Christopher Ferris03eebcb2014-06-13 13:57:51 -070042 if (size < bytes) {
Yi Kong32bc0fc2018-08-02 17:31:13 -070043 return nullptr;
Christopher Ferris03eebcb2014-06-13 13:57:51 -070044 }
45 return je_memalign(pagesize, size);
Christopher Ferris72bbd422014-05-08 11:14:03 -070046}
47
48#ifdef je_memalign
49#undef je_memalign
50#endif
51
Christopher Ferris72bbd422014-05-08 11:14:03 -070052void* je_memalign_round_up_boundary(size_t boundary, size_t size) {
Elliott Hughes731da1b2025-07-23 10:52:52 -070053 // The man page for memalign says it fails if boundary is not a power of 2,
54 // but this is not true. Both glibc and dlmalloc round up to the next power
55 // of 2, so we'll do the same.
56 boundary = stdc_bit_ceil(boundary);
57
Christopher Ferris72bbd422014-05-08 11:14:03 -070058 return je_memalign(boundary, size);
59}
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070060
Christopher Ferrisa22f5d52019-03-01 16:40:59 -080061#ifdef je_aligned_alloc
62#undef je_aligned_alloc
63#endif
64
65// The aligned_alloc function requires that size is a multiple of alignment.
66// jemalloc doesn't enforce this, so add enforcement here.
67void* je_aligned_alloc_wrapper(size_t alignment, size_t size) {
68 if ((size % alignment) != 0) {
69 errno = EINVAL;
70 return nullptr;
71 }
72 return je_aligned_alloc(alignment, size);
73}
74
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070075int je_mallopt(int param, int value) {
76 // The only parameter we currently understand is M_DECAY_TIME.
77 if (param == M_DECAY_TIME) {
Chia-hung Duan6abb4062024-04-17 19:08:48 -070078 // Only support setting the value to -1 or 0 or 1.
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070079 ssize_t decay_time_ms;
Chia-hung Duan6abb4062024-04-17 19:08:48 -070080 if (value < 0) {
81 // Given that SSIZE_MAX may not be supported in jemalloc, set this to a
82 // sufficiently large number that essentially disables the decay timer.
83 decay_time_ms = 10000000;
84 } else if (value) {
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070085 decay_time_ms = 1000;
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070086 } else {
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070087 decay_time_ms = 0;
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070088 }
89 // First get the total number of arenas.
90 unsigned narenas;
91 size_t sz = sizeof(unsigned);
92 if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
93 return 0;
94 }
95
96 // Set the decay time for any arenas that will be created in the future.
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070097 if (je_mallctl("arenas.dirty_decay_ms", nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
98 return 0;
99 }
100 if (je_mallctl("arenas.muzzy_decay_ms", nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700101 return 0;
102 }
103
104 // Change the decay on the already existing arenas.
105 char buffer[100];
106 for (unsigned i = 0; i < narenas; i++) {
Christopher Ferrisd73a49e2018-10-19 16:03:44 -0700107 snprintf(buffer, sizeof(buffer), "arena.%d.dirty_decay_ms", i);
108 if (je_mallctl(buffer, nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
109 break;
110 }
111 snprintf(buffer, sizeof(buffer), "arena.%d.muzzy_decay_ms", i);
112 if (je_mallctl(buffer, nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700113 break;
114 }
115 }
116 return 1;
Christopher Ferrisd86eb862023-02-28 12:45:54 -0800117 } else if (param == M_PURGE || param == M_PURGE_ALL) {
Christopher Ferris0f710fd2019-05-01 13:26:46 -0700118 // Only clear the current thread cache since there is no easy way to
119 // clear the caches of other threads.
120 // This must be done first so that cleared allocations get purged
121 // in the next calls.
Christopher Ferris3d0bafb2019-07-08 14:54:58 -0700122 // Ignore the return call since this will fail if the tcache is disabled.
123 je_mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0);
Christopher Ferris0f710fd2019-05-01 13:26:46 -0700124
Tim Murrayac578f22018-10-15 16:26:56 -0700125 unsigned narenas;
126 size_t sz = sizeof(unsigned);
127 if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
128 return 0;
129 }
130 char buffer[100];
131 snprintf(buffer, sizeof(buffer), "arena.%u.purge", narenas);
132 if (je_mallctl(buffer, nullptr, nullptr, nullptr, 0) != 0) {
133 return 0;
134 }
135 return 1;
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700136 } else if (param == M_LOG_STATS) {
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700137 size_t total_bytes = 0;
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700138 for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
139 struct mallinfo mi = je_mallinfo_arena_info(i);
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700140 size_t arena_bytes = mi.fsmblks + mi.ordblks + mi.uordblks;
141 async_safe_format_log(ANDROID_LOG_INFO, "jemalloc",
142 "Arena %zu: bin bytes=%zu large bytes=%zu total bytes=%zu", i,
143 mi.fsmblks, mi.ordblks, arena_bytes);
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700144
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700145 je_stats_arena(i, [](size_t index, size_t size, size_t allocs) {
146 if (allocs != 0) {
147 async_safe_format_log(ANDROID_LOG_INFO, "jemalloc",
148 " Size Class %zu(%zu bytes): allocs=%zu total bytes=%zu", index,
149 size, allocs, allocs * size);
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700150 }
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700151 });
152 total_bytes += arena_bytes;
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700153 }
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700154 async_safe_format_log(ANDROID_LOG_INFO, "jemalloc", "Total Bytes=%zu", total_bytes);
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700155 return 1;
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700156 }
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700157
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700158 return 0;
159}
Christopher Ferris6c619a02019-03-01 17:59:51 -0800160
Christopher Ferris6c619a02019-03-01 17:59:51 -0800161int je_malloc_info(int options, FILE* fp) {
162 if (options != 0) {
163 errno = EINVAL;
164 return -1;
165 }
166
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800167 fflush(fp);
168 int fd = fileno(fp);
169 MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
Christopher Ferris6c619a02019-03-01 17:59:51 -0800170
171 // Dump all of the large allocations in the arenas.
Christopher Ferrisdb9706a2019-05-02 18:33:11 -0700172 for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
173 struct mallinfo mi = je_mallinfo_arena_info(i);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800174 if (mi.hblkhd != 0) {
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800175 MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800176 {
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800177 MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
178 MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
179 MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800180
181 size_t total = 0;
Christopher Ferrisdb9706a2019-05-02 18:33:11 -0700182 for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
183 struct mallinfo mi = je_mallinfo_bin_info(i, j);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800184 if (mi.ordblks != 0) {
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800185 MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
186 MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
187 MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
188 MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800189 total += mi.ordblks;
190 }
191 }
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800192 MallocXmlElem(fd, "bins-total").Contents("%zu", total);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800193 }
194 }
195 }
196
197 return 0;
198}