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 <malloc.h>
19 #include <sys/param.h>
20 #include <unistd.h>
21
22 #include <private/MallocXmlElem.h>
23
24 #include "jemalloc.h"
25
je_pvalloc(size_t bytes)26 void* je_pvalloc(size_t bytes) {
27 size_t pagesize = getpagesize();
28 size_t size = __BIONIC_ALIGN(bytes, pagesize);
29 if (size < bytes) {
30 return nullptr;
31 }
32 return je_memalign(pagesize, size);
33 }
34
35 #ifdef je_memalign
36 #undef je_memalign
37 #endif
38
39 // The man page for memalign says it fails if boundary is not a power of 2,
40 // but this is not true. Both glibc and dlmalloc round up to the next power
41 // of 2, so we'll do the same.
je_memalign_round_up_boundary(size_t boundary,size_t size)42 void* je_memalign_round_up_boundary(size_t boundary, size_t size) {
43 if (boundary != 0) {
44 if (!powerof2(boundary)) {
45 boundary = BIONIC_ROUND_UP_POWER_OF_2(boundary);
46 }
47 } else {
48 boundary = 1;
49 }
50 return je_memalign(boundary, size);
51 }
52
53 #ifdef je_aligned_alloc
54 #undef je_aligned_alloc
55 #endif
56
57 // The aligned_alloc function requires that size is a multiple of alignment.
58 // jemalloc doesn't enforce this, so add enforcement here.
je_aligned_alloc_wrapper(size_t alignment,size_t size)59 void* je_aligned_alloc_wrapper(size_t alignment, size_t size) {
60 if ((size % alignment) != 0) {
61 errno = EINVAL;
62 return nullptr;
63 }
64 return je_aligned_alloc(alignment, size);
65 }
66
je_mallopt(int param,int value)67 int je_mallopt(int param, int value) {
68 // The only parameter we currently understand is M_DECAY_TIME.
69 if (param == M_DECAY_TIME) {
70 // Only support setting the value to 1 or 0.
71 ssize_t decay_time_ms;
72 if (value) {
73 decay_time_ms = 1000;
74 } else {
75 decay_time_ms = 0;
76 }
77 // First get the total number of arenas.
78 unsigned narenas;
79 size_t sz = sizeof(unsigned);
80 if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
81 return 0;
82 }
83
84 // Set the decay time for any arenas that will be created in the future.
85 if (je_mallctl("arenas.dirty_decay_ms", nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
86 return 0;
87 }
88 if (je_mallctl("arenas.muzzy_decay_ms", nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
89 return 0;
90 }
91
92 // Change the decay on the already existing arenas.
93 char buffer[100];
94 for (unsigned i = 0; i < narenas; i++) {
95 snprintf(buffer, sizeof(buffer), "arena.%d.dirty_decay_ms", i);
96 if (je_mallctl(buffer, nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
97 break;
98 }
99 snprintf(buffer, sizeof(buffer), "arena.%d.muzzy_decay_ms", i);
100 if (je_mallctl(buffer, nullptr, nullptr, &decay_time_ms, sizeof(decay_time_ms)) != 0) {
101 break;
102 }
103 }
104 return 1;
105 } else if (param == M_PURGE) {
106 // Only clear the current thread cache since there is no easy way to
107 // clear the caches of other threads.
108 // This must be done first so that cleared allocations get purged
109 // in the next calls.
110 // Ignore the return call since this will fail if the tcache is disabled.
111 je_mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0);
112
113 unsigned narenas;
114 size_t sz = sizeof(unsigned);
115 if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
116 return 0;
117 }
118 char buffer[100];
119 snprintf(buffer, sizeof(buffer), "arena.%u.purge", narenas);
120 if (je_mallctl(buffer, nullptr, nullptr, nullptr, 0) != 0) {
121 return 0;
122 }
123 return 1;
124 }
125 return 0;
126 }
127
128 __BEGIN_DECLS
129
130 size_t je_mallinfo_narenas();
131 size_t je_mallinfo_nbins();
132 struct mallinfo je_mallinfo_arena_info(size_t);
133 struct mallinfo je_mallinfo_bin_info(size_t, size_t);
134
135 __END_DECLS
136
je_malloc_info(int options,FILE * fp)137 int je_malloc_info(int options, FILE* fp) {
138 if (options != 0) {
139 errno = EINVAL;
140 return -1;
141 }
142
143 fflush(fp);
144 int fd = fileno(fp);
145 MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
146
147 // Dump all of the large allocations in the arenas.
148 for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
149 struct mallinfo mi = je_mallinfo_arena_info(i);
150 if (mi.hblkhd != 0) {
151 MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
152 {
153 MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
154 MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
155 MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
156
157 size_t total = 0;
158 for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
159 struct mallinfo mi = je_mallinfo_bin_info(i, j);
160 if (mi.ordblks != 0) {
161 MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
162 MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
163 MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
164 MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
165 total += mi.ordblks;
166 }
167 }
168 MallocXmlElem(fd, "bins-total").Contents("%zu", total);
169 }
170 }
171 }
172
173 return 0;
174 }
175