9a0f08ba8f92fb81a58b440f185301e63503d966
[WebKit-https.git] / Source / WTF / wtf / FastMalloc.cpp
1 /*
2  * Copyright (c) 2005, 2007, Google Inc. All rights reserved.
3  * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "FastMalloc.h"
28
29 #include "CheckedArithmetic.h"
30 #include "CurrentTime.h"
31 #include <limits>
32 #include <string.h>
33 #include <wtf/DataLog.h>
34
35 #if OS(WINDOWS)
36 #include <windows.h>
37 #else
38 #include <pthread.h>
39 #include <sys/resource.h>
40 #endif
41
42 #if OS(DARWIN)
43 #include <mach/mach_init.h>
44 #include <malloc/malloc.h>
45 #endif
46
47 namespace WTF {
48
49 #if !defined(NDEBUG)
50 namespace {
51 // We do not use std::numeric_limits<size_t>::max() here due to the edge case in VC++.
52 // https://bugs.webkit.org/show_bug.cgi?id=173720
53 static size_t maxSingleAllocationSize = SIZE_MAX;
54 };
55
56 void fastSetMaxSingleAllocationSize(size_t size)
57 {
58     maxSingleAllocationSize = size;
59 }
60
61 #define ASSERT_IS_WITHIN_LIMIT(size) do { \
62         size_t size__ = (size); \
63         ASSERT_WITH_MESSAGE((size__) <= maxSingleAllocationSize, "Requested size (%zu) exceeds max single allocation size set for testing (%zu)", (size__), maxSingleAllocationSize); \
64     } while (false)
65
66 #define FAIL_IF_EXCEEDS_LIMIT(size) do { \
67         if (UNLIKELY((size) > maxSingleAllocationSize)) \
68             return nullptr; \
69     } while (false)
70
71 #else // !defined(NDEBUG)
72
73 #define ASSERT_IS_WITHIN_LIMIT(size)
74 #define FAIL_IF_EXCEEDS_LIMIT(size)
75
76 #endif // !defined(NDEBUG)
77
78 void* fastZeroedMalloc(size_t n) 
79 {
80     void* result = fastMalloc(n);
81     memset(result, 0, n);
82     return result;
83 }
84
85 char* fastStrDup(const char* src)
86 {
87     size_t len = strlen(src) + 1;
88     char* dup = static_cast<char*>(fastMalloc(len));
89     memcpy(dup, src, len);
90     return dup;
91 }
92
93 TryMallocReturnValue tryFastZeroedMalloc(size_t n) 
94 {
95     void* result;
96     if (!tryFastMalloc(n).getValue(result))
97         return 0;
98     memset(result, 0, n);
99     return result;
100 }
101
102 } // namespace WTF
103
104 #if defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC
105
106 #if OS(WINDOWS)
107 #include <malloc.h>
108 #endif
109
110 namespace WTF {
111
112 bool isFastMallocEnabled()
113 {
114     return false;
115 }
116
117 size_t fastMallocGoodSize(size_t bytes)
118 {
119 #if OS(DARWIN)
120     return malloc_good_size(bytes);
121 #else
122     return bytes;
123 #endif
124 }
125
126 #if OS(WINDOWS)
127
128 void* fastAlignedMalloc(size_t alignment, size_t size) 
129 {
130     ASSERT_IS_WITHIN_LIMIT(size);
131     void* p = _aligned_malloc(size, alignment);
132     if (UNLIKELY(!p))
133         CRASH();
134     return p;
135 }
136
137 void* tryFastAlignedMalloc(size_t alignment, size_t size) 
138 {
139     FAIL_IF_EXCEEDS_LIMIT(size);
140     return _aligned_malloc(size, alignment);
141 }
142
143 void fastAlignedFree(void* p) 
144 {
145     _aligned_free(p);
146 }
147
148 #else
149
150 void* fastAlignedMalloc(size_t alignment, size_t size) 
151 {
152     ASSERT_IS_WITHIN_LIMIT(size);
153     void* p = nullptr;
154     posix_memalign(&p, alignment, size);
155     if (UNLIKELY(!p))
156         CRASH();
157     return p;
158 }
159
160 void* tryFastAlignedMalloc(size_t alignment, size_t size) 
161 {
162     FAIL_IF_EXCEEDS_LIMIT(size);
163     void* p = nullptr;
164     posix_memalign(&p, alignment, size);
165     return p;
166 }
167
168 void fastAlignedFree(void* p) 
169 {
170     free(p);
171 }
172
173 #endif // OS(WINDOWS)
174
175 TryMallocReturnValue tryFastMalloc(size_t n) 
176 {
177     FAIL_IF_EXCEEDS_LIMIT(n);
178     return malloc(n);
179 }
180
181 void* fastMalloc(size_t n) 
182 {
183     ASSERT_IS_WITHIN_LIMIT(n);
184     void* result = malloc(n);
185     if (!result)
186         CRASH();
187
188     return result;
189 }
190
191 TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size)
192 {
193     FAIL_IF_EXCEEDS_LIMIT(n_elements * element_size);
194     return calloc(n_elements, element_size);
195 }
196
197 void* fastCalloc(size_t n_elements, size_t element_size)
198 {
199     ASSERT_IS_WITHIN_LIMIT(n_elements * element_size);
200     void* result = calloc(n_elements, element_size);
201     if (!result)
202         CRASH();
203
204     return result;
205 }
206
207 void fastFree(void* p)
208 {
209     free(p);
210 }
211
212 void* fastRealloc(void* p, size_t n)
213 {
214     ASSERT_IS_WITHIN_LIMIT(n);
215     void* result = realloc(p, n);
216     if (!result)
217         CRASH();
218     return result;
219 }
220
221 void releaseFastMallocFreeMemory() { }
222 void releaseFastMallocFreeMemoryForThisThread() { }
223     
224 FastMallocStatistics fastMallocStatistics()
225 {
226     FastMallocStatistics statistics = { 0, 0, 0 };
227     return statistics;
228 }
229
230 size_t fastMallocSize(const void* p)
231 {
232 #if OS(DARWIN)
233     return malloc_size(p);
234 #elif OS(WINDOWS)
235     return _msize(const_cast<void*>(p));
236 #else
237     UNUSED_PARAM(p);
238     return 1;
239 #endif
240 }
241
242 } // namespace WTF
243
244 #else // defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC
245
246 #include <bmalloc/bmalloc.h>
247
248 namespace WTF {
249
250 bool isFastMallocEnabled()
251 {
252     return bmalloc::api::isEnabled();
253 }
254
255 void* fastMalloc(size_t size)
256 {
257     ASSERT_IS_WITHIN_LIMIT(size);
258     return bmalloc::api::malloc(size);
259 }
260
261 void* fastCalloc(size_t numElements, size_t elementSize)
262 {
263     ASSERT_IS_WITHIN_LIMIT(numElements * elementSize);
264     Checked<size_t> checkedSize = elementSize;
265     checkedSize *= numElements;
266     void* result = fastZeroedMalloc(checkedSize.unsafeGet());
267     if (!result)
268         CRASH();
269     return result;
270 }
271
272 void* fastRealloc(void* object, size_t size)
273 {
274     ASSERT_IS_WITHIN_LIMIT(size);
275     return bmalloc::api::realloc(object, size);
276 }
277
278 void fastFree(void* object)
279 {
280     bmalloc::api::free(object);
281 }
282
283 size_t fastMallocSize(const void*)
284 {
285     // FIXME: This is incorrect; best fix is probably to remove this function.
286     // Caller currently are all using this for assertion, not to actually check
287     // the size of the allocation, so maybe we can come up with something for that.
288     return 1;
289 }
290
291 size_t fastMallocGoodSize(size_t size)
292 {
293     return size;
294 }
295
296 void* fastAlignedMalloc(size_t alignment, size_t size) 
297 {
298     ASSERT_IS_WITHIN_LIMIT(size);
299     return bmalloc::api::memalign(alignment, size);
300 }
301
302 void* tryFastAlignedMalloc(size_t alignment, size_t size) 
303 {
304     FAIL_IF_EXCEEDS_LIMIT(size);
305     return bmalloc::api::tryMemalign(alignment, size);
306 }
307
308 void fastAlignedFree(void* p) 
309 {
310     bmalloc::api::free(p);
311 }
312
313 TryMallocReturnValue tryFastMalloc(size_t size)
314 {
315     FAIL_IF_EXCEEDS_LIMIT(size);
316     return bmalloc::api::tryMalloc(size);
317 }
318     
319 TryMallocReturnValue tryFastCalloc(size_t numElements, size_t elementSize)
320 {
321     FAIL_IF_EXCEEDS_LIMIT(numElements * elementSize);
322     Checked<size_t, RecordOverflow> checkedSize = elementSize;
323     checkedSize *= numElements;
324     if (checkedSize.hasOverflowed())
325         return nullptr;
326     return tryFastZeroedMalloc(checkedSize.unsafeGet());
327 }
328     
329 void releaseFastMallocFreeMemoryForThisThread()
330 {
331     bmalloc::api::scavengeThisThread();
332 }
333
334 void releaseFastMallocFreeMemory()
335 {
336     bmalloc::api::scavenge();
337 }
338
339 FastMallocStatistics fastMallocStatistics()
340 {
341
342     // FIXME: Can bmalloc itself report the stats instead of relying on the OS?
343     FastMallocStatistics statistics;
344     statistics.freeListBytes = 0;
345     statistics.reservedVMBytes = 0;
346
347 #if OS(WINDOWS)
348     PROCESS_MEMORY_COUNTERS resourceUsage;
349     GetProcessMemoryInfo(GetCurrentProcess(), &resourceUsage, sizeof(resourceUsage));
350     statistics.committedVMBytes = resourceUsage.PeakWorkingSetSize;
351 #else
352     struct rusage resourceUsage;
353     getrusage(RUSAGE_SELF, &resourceUsage);
354
355 #if OS(DARWIN)
356     statistics.committedVMBytes = resourceUsage.ru_maxrss;
357 #else
358     statistics.committedVMBytes = resourceUsage.ru_maxrss * 1024;
359 #endif // OS(DARWIN)
360
361 #endif // OS(WINDOWS)
362     return statistics;
363 }
364
365 } // namespace WTF
366
367 #endif // defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC