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