1/*
2 * Copyright (c) 2005, 2007, Google Inc. All rights reserved.
3 * Copyright (C) 2005-2018 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 <wtf/FastMalloc.h>
28
29#include <limits>
30#include <string.h>
31#include <wtf/CheckedArithmetic.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
48namespace WTF {
49
50#if !defined(NDEBUG)
51namespace {
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
54static size_t maxSingleAllocationSize = SIZE_MAX;
55};
56
57void 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
79void* fastZeroedMalloc(size_t n)
80{
81 void* result = fastMalloc(n);
82 memset(result, 0, n);
83 return result;
84}
85
86char* 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
94TryMallocReturnValue 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
113namespace WTF {
114
115bool isFastMallocEnabled()
116{
117 return false;
118}
119
120size_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
131void* 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
140void* tryFastAlignedMalloc(size_t alignment, size_t size)
141{
142 FAIL_IF_EXCEEDS_LIMIT(size);
143 return _aligned_malloc(size, alignment);
144}
145
146void fastAlignedFree(void* p)
147{
148 _aligned_free(p);
149}
150
151#else
152
153void* 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
163void* 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
171void fastAlignedFree(void* p)
172{
173 free(p);
174}
175
176#endif // OS(WINDOWS)
177
178TryMallocReturnValue tryFastMalloc(size_t n)
179{
180 FAIL_IF_EXCEEDS_LIMIT(n);
181 return malloc(n);
182}
183
184void* 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
194TryMallocReturnValue 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
200void* 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
210void fastFree(void* p)
211{
212 free(p);
213}
214
215void* 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
224TryMallocReturnValue tryFastRealloc(void* p, size_t n)
225{
226 FAIL_IF_EXCEEDS_LIMIT(n);
227 return realloc(p, n);
228}
229
230void releaseFastMallocFreeMemory() { }
231void releaseFastMallocFreeMemoryForThisThread() { }
232
233FastMallocStatistics fastMallocStatistics()
234{
235 FastMallocStatistics statistics = { 0, 0, 0 };
236 return statistics;
237}
238
239size_t fastMallocSize(const void* p)
240{
241#if OS(DARWIN)
242 return malloc_size(p);
243#elif OS(WINDOWS)
244 return _msize(const_cast<void*>(p));
245#else
246 UNUSED_PARAM(p);
247 return 1;
248#endif
249}
250
251void fastCommitAlignedMemory(void* ptr, size_t size)
252{
253 OSAllocator::commit(ptr, size, true, false);
254}
255
256void fastDecommitAlignedMemory(void* ptr, size_t size)
257{
258 OSAllocator::decommit(ptr, size);
259}
260
261void fastEnableMiniMode() { }
262
263} // namespace WTF
264
265#else // defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC
266
267#include <bmalloc/bmalloc.h>
268
269namespace WTF {
270
271bool isFastMallocEnabled()
272{
273 return bmalloc::api::isEnabled();
274}
275
276void* fastMalloc(size_t size)
277{
278 ASSERT_IS_WITHIN_LIMIT(size);
279 return bmalloc::api::malloc(size);
280}
281
282void* fastCalloc(size_t numElements, size_t elementSize)
283{
284 ASSERT_IS_WITHIN_LIMIT(numElements * elementSize);
285 Checked<size_t> checkedSize = elementSize;
286 checkedSize *= numElements;
287 void* result = fastZeroedMalloc(checkedSize.unsafeGet());
288 if (!result)
289 CRASH();
290 return result;
291}
292
293void* fastRealloc(void* object, size_t size)
294{
295 ASSERT_IS_WITHIN_LIMIT(size);
296 return bmalloc::api::realloc(object, size);
297}
298
299void fastFree(void* object)
300{
301 bmalloc::api::free(object);
302}
303
304size_t fastMallocSize(const void*)
305{
306 // FIXME: This is incorrect; best fix is probably to remove this function.
307 // Caller currently are all using this for assertion, not to actually check
308 // the size of the allocation, so maybe we can come up with something for that.
309 return 1;
310}
311
312size_t fastMallocGoodSize(size_t size)
313{
314 return size;
315}
316
317void* fastAlignedMalloc(size_t alignment, size_t size)
318{
319 ASSERT_IS_WITHIN_LIMIT(size);
320 return bmalloc::api::memalign(alignment, size);
321}
322
323void* tryFastAlignedMalloc(size_t alignment, size_t size)
324{
325 FAIL_IF_EXCEEDS_LIMIT(size);
326 return bmalloc::api::tryMemalign(alignment, size);
327}
328
329void fastAlignedFree(void* p)
330{
331 bmalloc::api::free(p);
332}
333
334TryMallocReturnValue tryFastMalloc(size_t size)
335{
336 FAIL_IF_EXCEEDS_LIMIT(size);
337 return bmalloc::api::tryMalloc(size);
338}
339
340TryMallocReturnValue tryFastCalloc(size_t numElements, size_t elementSize)
341{
342 FAIL_IF_EXCEEDS_LIMIT(numElements * elementSize);
343 Checked<size_t, RecordOverflow> checkedSize = elementSize;
344 checkedSize *= numElements;
345 if (checkedSize.hasOverflowed())
346 return nullptr;
347 return tryFastZeroedMalloc(checkedSize.unsafeGet());
348}
349
350TryMallocReturnValue tryFastRealloc(void* object, size_t newSize)
351{
352 FAIL_IF_EXCEEDS_LIMIT(newSize);
353 return bmalloc::api::tryRealloc(object, newSize);
354}
355
356void releaseFastMallocFreeMemoryForThisThread()
357{
358 bmalloc::api::scavengeThisThread();
359}
360
361void releaseFastMallocFreeMemory()
362{
363 bmalloc::api::scavenge();
364}
365
366FastMallocStatistics fastMallocStatistics()
367{
368
369 // FIXME: Can bmalloc itself report the stats instead of relying on the OS?
370 FastMallocStatistics statistics;
371 statistics.freeListBytes = 0;
372 statistics.reservedVMBytes = 0;
373
374#if OS(WINDOWS)
375 PROCESS_MEMORY_COUNTERS resourceUsage;
376 GetProcessMemoryInfo(GetCurrentProcess(), &resourceUsage, sizeof(resourceUsage));
377 statistics.committedVMBytes = resourceUsage.PeakWorkingSetSize;
378#elif HAVE(RESOURCE_H)
379 struct rusage resourceUsage;
380 getrusage(RUSAGE_SELF, &resourceUsage);
381
382#if OS(DARWIN)
383 statistics.committedVMBytes = resourceUsage.ru_maxrss;
384#else
385 statistics.committedVMBytes = resourceUsage.ru_maxrss * 1024;
386#endif // OS(DARWIN)
387
388#endif // OS(WINDOWS)
389 return statistics;
390}
391
392void fastCommitAlignedMemory(void* ptr, size_t size)
393{
394 bmalloc::api::commitAlignedPhysical(ptr, size);
395}
396
397void fastDecommitAlignedMemory(void* ptr, size_t size)
398{
399 bmalloc::api::decommitAlignedPhysical(ptr, size);
400}
401
402void fastEnableMiniMode()
403{
404 bmalloc::api::enableMiniMode();
405}
406
407} // namespace WTF
408
409#endif // defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC
410