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