Unreviewed, rolling out r234489.
[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 <limits>
31 #include <string.h>
32 #include <wtf/DataLog.h>
33
34 #if OS(WINDOWS)
35 #include <windows.h>
36 #else
37 #include <pthread.h>
38 #if HAVE(RESOURCE_H)
39 #include <sys/resource.h>
40 #endif // HAVE(RESOURCE_H)
41 #endif
42
43 #if OS(DARWIN)
44 #include <mach/mach_init.h>
45 #include <malloc/malloc.h>
46 #endif
47
48 namespace WTF {
49
50 #if !defined(NDEBUG)
51 namespace {
52 // We do not use std::numeric_limits<size_t>::max() here due to the edge case in VC++.
53 // https://bugs.webkit.org/show_bug.cgi?id=173720
54 static size_t maxSingleAllocationSize = SIZE_MAX;
55 };
56
57 void fastSetMaxSingleAllocationSize(size_t size)
58 {
59     maxSingleAllocationSize = size;
60 }
61
62 #define ASSERT_IS_WITHIN_LIMIT(size) do { \
63         size_t size__ = (size); \
64         ASSERT_WITH_MESSAGE((size__) <= maxSingleAllocationSize, "Requested size (%zu) exceeds max single allocation size set for testing (%zu)", (size__), maxSingleAllocationSize); \
65     } while (false)
66
67 #define FAIL_IF_EXCEEDS_LIMIT(size) do { \
68         if (UNLIKELY((size) > maxSingleAllocationSize)) \
69             return nullptr; \
70     } while (false)
71
72 #else // !defined(NDEBUG)
73
74 #define ASSERT_IS_WITHIN_LIMIT(size)
75 #define FAIL_IF_EXCEEDS_LIMIT(size)
76
77 #endif // !defined(NDEBUG)
78
79 void* fastZeroedMalloc(size_t n) 
80 {
81     void* result = fastMalloc(n);
82     memset(result, 0, n);
83     return result;
84 }
85
86 char* fastStrDup(const char* src)
87 {
88     size_t len = strlen(src) + 1;
89     char* dup = static_cast<char*>(fastMalloc(len));
90     memcpy(dup, src, len);
91     return dup;
92 }
93
94 TryMallocReturnValue tryFastZeroedMalloc(size_t n) 
95 {
96     void* result;
97     if (!tryFastMalloc(n).getValue(result))
98         return 0;
99     memset(result, 0, n);
100     return result;
101 }
102
103 } // namespace WTF
104
105 #if defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC
106
107 #include <wtf/OSAllocator.h>
108
109 #if OS(WINDOWS)
110 #include <malloc.h>
111 #endif
112
113 namespace WTF {
114
115 bool isFastMallocEnabled()
116 {
117     return false;
118 }
119
120 size_t fastMallocGoodSize(size_t bytes)
121 {
122 #if OS(DARWIN)
123     return malloc_good_size(bytes);
124 #else
125     return bytes;
126 #endif
127 }
128
129 #if OS(WINDOWS)
130
131 void* fastAlignedMalloc(size_t alignment, size_t size) 
132 {
133     ASSERT_IS_WITHIN_LIMIT(size);
134     void* p = _aligned_malloc(size, alignment);
135     if (UNLIKELY(!p))
136         CRASH();
137     return p;
138 }
139
140 void* tryFastAlignedMalloc(size_t alignment, size_t size) 
141 {
142     FAIL_IF_EXCEEDS_LIMIT(size);
143     return _aligned_malloc(size, alignment);
144 }
145
146 void fastAlignedFree(void* p) 
147 {
148     _aligned_free(p);
149 }
150
151 #else
152
153 void* fastAlignedMalloc(size_t alignment, size_t size) 
154 {
155     ASSERT_IS_WITHIN_LIMIT(size);
156     void* p = nullptr;
157     posix_memalign(&p, alignment, size);
158     if (UNLIKELY(!p))
159         CRASH();
160     return p;
161 }
162
163 void* tryFastAlignedMalloc(size_t alignment, size_t size) 
164 {
165     FAIL_IF_EXCEEDS_LIMIT(size);
166     void* p = nullptr;
167     posix_memalign(&p, alignment, size);
168     return p;
169 }
170
171 void fastAlignedFree(void* p) 
172 {
173     free(p);
174 }
175
176 #endif // OS(WINDOWS)
177
178 TryMallocReturnValue tryFastMalloc(size_t n) 
179 {
180     FAIL_IF_EXCEEDS_LIMIT(n);
181     return malloc(n);
182 }
183
184 void* fastMalloc(size_t n) 
185 {
186     ASSERT_IS_WITHIN_LIMIT(n);
187     void* result = malloc(n);
188     if (!result)
189         CRASH();
190
191     return result;
192 }
193
194 TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size)
195 {
196     FAIL_IF_EXCEEDS_LIMIT(n_elements * element_size);
197     return calloc(n_elements, element_size);
198 }
199
200 void* fastCalloc(size_t n_elements, size_t element_size)
201 {
202     ASSERT_IS_WITHIN_LIMIT(n_elements * element_size);
203     void* result = calloc(n_elements, element_size);
204     if (!result)
205         CRASH();
206
207     return result;
208 }
209
210 void fastFree(void* p)
211 {
212     free(p);
213 }
214
215 void* fastRealloc(void* p, size_t n)
216 {
217     ASSERT_IS_WITHIN_LIMIT(n);
218     void* result = realloc(p, n);
219     if (!result)
220         CRASH();
221     return result;
222 }
223
224 void releaseFastMallocFreeMemory() { }
225 void releaseFastMallocFreeMemoryForThisThread() { }
226     
227 FastMallocStatistics fastMallocStatistics()
228 {
229     FastMallocStatistics statistics = { 0, 0, 0 };
230     return statistics;
231 }
232
233 size_t fastMallocSize(const void* p)
234 {
235 #if OS(DARWIN)
236     return malloc_size(p);
237 #elif OS(WINDOWS)
238     return _msize(const_cast<void*>(p));
239 #else
240     UNUSED_PARAM(p);
241     return 1;
242 #endif
243 }
244
245 void fastCommitAlignedMemory(void* ptr, size_t size)
246 {
247     OSAllocator::commit(ptr, size, true, false);
248 }
249
250 void fastDecommitAlignedMemory(void* ptr, size_t size)
251 {
252     OSAllocator::decommit(ptr, size);
253 }
254
255 void fastEnableMiniMode() { }
256
257 } // namespace WTF
258
259 #else // defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC
260
261 #include <bmalloc/bmalloc.h>
262
263 namespace WTF {
264
265 bool isFastMallocEnabled()
266 {
267     return bmalloc::api::isEnabled();
268 }
269
270 void* fastMalloc(size_t size)
271 {
272     ASSERT_IS_WITHIN_LIMIT(size);
273     return bmalloc::api::malloc(size);
274 }
275
276 void* fastCalloc(size_t numElements, size_t elementSize)
277 {
278     ASSERT_IS_WITHIN_LIMIT(numElements * elementSize);
279     Checked<size_t> checkedSize = elementSize;
280     checkedSize *= numElements;
281     void* result = fastZeroedMalloc(checkedSize.unsafeGet());
282     if (!result)
283         CRASH();
284     return result;
285 }
286
287 void* fastRealloc(void* object, size_t size)
288 {
289     ASSERT_IS_WITHIN_LIMIT(size);
290     return bmalloc::api::realloc(object, size);
291 }
292
293 void fastFree(void* object)
294 {
295     bmalloc::api::free(object);
296 }
297
298 size_t fastMallocSize(const void*)
299 {
300     // FIXME: This is incorrect; best fix is probably to remove this function.
301     // Caller currently are all using this for assertion, not to actually check
302     // the size of the allocation, so maybe we can come up with something for that.
303     return 1;
304 }
305
306 size_t fastMallocGoodSize(size_t size)
307 {
308     return size;
309 }
310
311 void* fastAlignedMalloc(size_t alignment, size_t size) 
312 {
313     ASSERT_IS_WITHIN_LIMIT(size);
314     return bmalloc::api::memalign(alignment, size);
315 }
316
317 void* tryFastAlignedMalloc(size_t alignment, size_t size) 
318 {
319     FAIL_IF_EXCEEDS_LIMIT(size);
320     return bmalloc::api::tryMemalign(alignment, size);
321 }
322
323 void fastAlignedFree(void* p) 
324 {
325     bmalloc::api::free(p);
326 }
327
328 TryMallocReturnValue tryFastMalloc(size_t size)
329 {
330     FAIL_IF_EXCEEDS_LIMIT(size);
331     return bmalloc::api::tryMalloc(size);
332 }
333     
334 TryMallocReturnValue tryFastCalloc(size_t numElements, size_t elementSize)
335 {
336     FAIL_IF_EXCEEDS_LIMIT(numElements * elementSize);
337     Checked<size_t, RecordOverflow> checkedSize = elementSize;
338     checkedSize *= numElements;
339     if (checkedSize.hasOverflowed())
340         return nullptr;
341     return tryFastZeroedMalloc(checkedSize.unsafeGet());
342 }
343     
344 void releaseFastMallocFreeMemoryForThisThread()
345 {
346     bmalloc::api::scavengeThisThread();
347 }
348
349 void releaseFastMallocFreeMemory()
350 {
351     bmalloc::api::scavenge();
352 }
353
354 FastMallocStatistics fastMallocStatistics()
355 {
356
357     // FIXME: Can bmalloc itself report the stats instead of relying on the OS?
358     FastMallocStatistics statistics;
359     statistics.freeListBytes = 0;
360     statistics.reservedVMBytes = 0;
361
362 #if OS(WINDOWS)
363     PROCESS_MEMORY_COUNTERS resourceUsage;
364     GetProcessMemoryInfo(GetCurrentProcess(), &resourceUsage, sizeof(resourceUsage));
365     statistics.committedVMBytes = resourceUsage.PeakWorkingSetSize;
366 #elif HAVE(RESOURCE_H)
367     struct rusage resourceUsage;
368     getrusage(RUSAGE_SELF, &resourceUsage);
369
370 #if OS(DARWIN)
371     statistics.committedVMBytes = resourceUsage.ru_maxrss;
372 #else
373     statistics.committedVMBytes = resourceUsage.ru_maxrss * 1024;
374 #endif // OS(DARWIN)
375
376 #endif // OS(WINDOWS)
377     return statistics;
378 }
379
380 void fastCommitAlignedMemory(void* ptr, size_t size)
381 {
382     bmalloc::api::commitAlignedPhysical(ptr, size);
383 }
384
385 void fastDecommitAlignedMemory(void* ptr, size_t size)
386 {
387     bmalloc::api::decommitAlignedPhysical(ptr, size);
388 }
389
390 void fastEnableMiniMode()
391 {
392     bmalloc::api::enableMiniMode();
393 }
394
395 } // namespace WTF
396
397 #endif // defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC