blob: 63c9fab62c4e5f93df7e0f8a188b6c9829a13245 [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>
Christopher Ferris03eebcb2014-06-13 13:57:51 -070020#include <sys/param.h>
Christopher Ferris72bbd422014-05-08 11:14:03 -070021#include <unistd.h>
22
Christopher Ferrise9a7b812023-05-11 15:36:27 -070023#include <async_safe/log.h>
Christopher Ferris6c619a02019-03-01 17:59:51 -080024#include <private/MallocXmlElem.h>
25
Christopher Ferris72bbd422014-05-08 11:14:03 -070026#include "jemalloc.h"
27
Christopher Ferrise9a7b812023-05-11 15:36:27 -070028__BEGIN_DECLS
29
30size_t je_mallinfo_narenas();
31size_t je_mallinfo_nbins();
32struct mallinfo je_mallinfo_arena_info(size_t);
33struct mallinfo je_mallinfo_bin_info(size_t, size_t);
Christopher Ferris4a7e1e32024-09-13 15:08:20 -070034void je_stats_arena(size_t arena_index, void (*callback)(size_t, size_t, size_t));
Christopher Ferrise9a7b812023-05-11 15:36:27 -070035
36__END_DECLS
37
Christopher Ferris72bbd422014-05-08 11:14:03 -070038void* je_pvalloc(size_t bytes) {
Elliott Hughes91570ce2014-07-10 12:34:23 -070039 size_t pagesize = getpagesize();
Dan Alberta613d0d2017-10-05 16:39:33 -070040 size_t size = __BIONIC_ALIGN(bytes, pagesize);
Christopher Ferris03eebcb2014-06-13 13:57:51 -070041 if (size < bytes) {
Yi Kong32bc0fc2018-08-02 17:31:13 -070042 return nullptr;
Christopher Ferris03eebcb2014-06-13 13:57:51 -070043 }
44 return je_memalign(pagesize, size);
Christopher Ferris72bbd422014-05-08 11:14:03 -070045}
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.
54void* je_memalign_round_up_boundary(size_t boundary, size_t size) {
Christopher Ferris03eebcb2014-06-13 13:57:51 -070055 if (boundary != 0) {
56 if (!powerof2(boundary)) {
57 boundary = BIONIC_ROUND_UP_POWER_OF_2(boundary);
Christopher Ferris72bbd422014-05-08 11:14:03 -070058 }
59 } else {
60 boundary = 1;
61 }
62 return je_memalign(boundary, size);
63}
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070064
Christopher Ferrisa22f5d52019-03-01 16:40:59 -080065#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.
71void* 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
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070079int je_mallopt(int param, int value) {
80 // The only parameter we currently understand is M_DECAY_TIME.
81 if (param == M_DECAY_TIME) {
Chia-hung Duan6abb4062024-04-17 19:08:48 -070082 // Only support setting the value to -1 or 0 or 1.
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070083 ssize_t decay_time_ms;
Chia-hung Duan6abb4062024-04-17 19:08:48 -070084 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) {
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070089 decay_time_ms = 1000;
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070090 } else {
Christopher Ferrisd73a49e2018-10-19 16:03:44 -070091 decay_time_ms = 0;
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -070092 }
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.
Christopher Ferrisd73a49e2018-10-19 16:03:44 -0700101 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) {
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700105 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++) {
Christopher Ferrisd73a49e2018-10-19 16:03:44 -0700111 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) {
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700117 break;
118 }
119 }
120 return 1;
Christopher Ferrisd86eb862023-02-28 12:45:54 -0800121 } else if (param == M_PURGE || param == M_PURGE_ALL) {
Christopher Ferris0f710fd2019-05-01 13:26:46 -0700122 // 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.
Christopher Ferris3d0bafb2019-07-08 14:54:58 -0700126 // Ignore the return call since this will fail if the tcache is disabled.
127 je_mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0);
Christopher Ferris0f710fd2019-05-01 13:26:46 -0700128
Tim Murrayac578f22018-10-15 16:26:56 -0700129 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;
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700140 } else if (param == M_LOG_STATS) {
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700141 size_t total_bytes = 0;
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700142 for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
143 struct mallinfo mi = je_mallinfo_arena_info(i);
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700144 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);
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700148
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700149 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);
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700154 }
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700155 });
156 total_bytes += arena_bytes;
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700157 }
Christopher Ferris4a7e1e32024-09-13 15:08:20 -0700158 async_safe_format_log(ANDROID_LOG_INFO, "jemalloc", "Total Bytes=%zu", total_bytes);
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700159 return 1;
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700160 }
Christopher Ferrise9a7b812023-05-11 15:36:27 -0700161
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700162 return 0;
163}
Christopher Ferris6c619a02019-03-01 17:59:51 -0800164
Christopher Ferris6c619a02019-03-01 17:59:51 -0800165int je_malloc_info(int options, FILE* fp) {
166 if (options != 0) {
167 errno = EINVAL;
168 return -1;
169 }
170
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800171 fflush(fp);
172 int fd = fileno(fp);
173 MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
Christopher Ferris6c619a02019-03-01 17:59:51 -0800174
175 // Dump all of the large allocations in the arenas.
Christopher Ferrisdb9706a2019-05-02 18:33:11 -0700176 for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
177 struct mallinfo mi = je_mallinfo_arena_info(i);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800178 if (mi.hblkhd != 0) {
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800179 MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800180 {
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800181 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);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800184
185 size_t total = 0;
Christopher Ferrisdb9706a2019-05-02 18:33:11 -0700186 for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
187 struct mallinfo mi = je_mallinfo_bin_info(i, j);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800188 if (mi.ordblks != 0) {
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800189 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);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800193 total += mi.ordblks;
194 }
195 }
Christopher Ferrisff88fb02019-11-04 18:40:00 -0800196 MallocXmlElem(fd, "bins-total").Contents("%zu", total);
Christopher Ferris6c619a02019-03-01 17:59:51 -0800197 }
198 }
199 }
200
201 return 0;
202}