bmalloc should support strictly type-segregated isolated heaps
[WebKit-https.git] / Source / bmalloc / test / testbmalloc.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
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 <bmalloc/bmalloc.h>
27 #include <bmalloc/IsoHeapInlines.h>
28 #include <cmath>
29 #include <cstdlib>
30 #include <set>
31 #include <vector>
32
33 using namespace bmalloc;
34 using namespace bmalloc::api;
35
36 // We don't have a NO_RETURN_DUE_TO_EXIT, nor should we. That's ridiculous.
37 static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
38
39 static void usage()
40 {
41     puts("Usage: testb3 [<filter>]");
42     if (hiddenTruthBecauseNoReturnIsStupid())
43         exit(1);
44 }
45
46 #define RUN(test) do {                          \
47         if (!shouldRun(#test))                  \
48             break;                              \
49         puts(#test "...");                      \
50         test;                                   \
51         puts(#test ": OK!");                    \
52     } while (false)
53
54 // Nothing fancy for now; we just use the existing WTF assertion machinery.
55 #define CHECK(x) do {                                                   \
56         if (!!(x))                                                      \
57             break;                                                      \
58         fprintf(stderr, "%s:%d: in %s: assertion %s failed.\n",         \
59             __FILE__, __LINE__, __PRETTY_FUNCTION__, #x);               \
60         abort();                                                        \
61     } while (false)
62
63 static std::set<void*> toptrset(const std::vector<void*>& ptrs)
64 {
65     std::set<void*> result;
66     for (void* ptr : ptrs) {
67         if (ptr)
68             result.insert(ptr);
69     }
70     return result;
71 }
72
73 static void assertEmptyPointerSet(const std::set<void*>& pointers)
74 {
75     if (pointers.empty())
76         return;
77     printf("Pointer set not empty!\n");
78     printf("Pointers:");
79     for (void* ptr : pointers)
80         printf(" %p", ptr);
81     printf("\n");
82     CHECK(pointers.empty());
83 }
84
85 template<typename heapType>
86 static void assertHasObjects(IsoHeap<heapType>& heap, std::set<void*> pointers)
87 {
88     auto& impl = heap.impl();
89     std::lock_guard<Mutex> locker(impl.lock);
90     impl.forEachLiveObject(
91         [&] (void* object) {
92             pointers.erase(object);
93         });
94     assertEmptyPointerSet(pointers);
95 }
96
97 template<typename heapType>
98 static void assertHasOnlyObjects(IsoHeap<heapType>& heap, std::set<void*> pointers)
99 {
100     auto& impl = heap.impl();
101     std::lock_guard<Mutex> locker(impl.lock);
102     impl.forEachLiveObject(
103         [&] (void* object) {
104             CHECK(pointers.erase(object) == 1);
105         });
106     assertEmptyPointerSet(pointers);
107 }
108
109 template<typename heapType>
110 static void assertClean(IsoHeap<heapType>& heap)
111 {
112     scavengeThisThread();
113     auto& impl = heap.impl();
114     {
115         std::lock_guard<Mutex> locker(impl.lock);
116         CHECK(!impl.numLiveObjects());
117     }
118     heap.scavenge();
119     {
120         std::lock_guard<Mutex> locker(impl.lock);
121         CHECK(!impl.numCommittedPages());
122     }
123 }
124
125 static void testIsoSimple()
126 {
127     static IsoHeap<double> heap;
128     void* ptr1 = heap.allocate();
129     CHECK(ptr1);
130     void* ptr2 = heap.allocate();
131     CHECK(ptr2);
132     CHECK(ptr1 != ptr2);
133     CHECK(std::abs(static_cast<char*>(ptr1) - static_cast<char*>(ptr2)) >= 8);
134     assertHasObjects(heap, {ptr1, ptr2});
135     heap.deallocate(ptr1);
136     heap.deallocate(ptr2);
137     assertClean(heap);
138 }
139
140 static void testIsoSimpleScavengeBeforeDealloc()
141 {
142     static IsoHeap<double> heap;
143     void* ptr1 = heap.allocate();
144     CHECK(ptr1);
145     void* ptr2 = heap.allocate();
146     CHECK(ptr2);
147     CHECK(ptr1 != ptr2);
148     CHECK(std::abs(static_cast<char*>(ptr1) - static_cast<char*>(ptr2)) >= 8);
149     scavengeThisThread();
150     assertHasOnlyObjects(heap, {ptr1, ptr2});
151     heap.deallocate(ptr1);
152     heap.deallocate(ptr2);
153     assertClean(heap);
154 }
155
156 static void testIsoFlipFlopFragmentedPages()
157 {
158     static IsoHeap<double> heap;
159     std::vector<void*> ptrs;
160     for (unsigned i = 100000; i--;) {
161         void* ptr = heap.allocate();
162         CHECK(ptr);
163         ptrs.push_back(ptr);
164     }
165     for (unsigned i = 0; i < ptrs.size(); i += 2) {
166         heap.deallocate(ptrs[i]);
167         ptrs[i] = nullptr;
168     }
169     for (unsigned i = ptrs.size() / 2; i--;)
170         ptrs.push_back(heap.allocate());
171     for (void* ptr : ptrs)
172         heap.deallocate(ptr);
173     assertClean(heap);
174 }
175
176 static void testIsoFlipFlopFragmentedPagesScavengeInMiddle()
177 {
178     static IsoHeap<double> heap;
179     std::vector<void*> ptrs;
180     for (unsigned i = 100000; i--;) {
181         void* ptr = heap.allocate();
182         CHECK(ptr);
183         ptrs.push_back(ptr);
184     }
185     CHECK(toptrset(ptrs).size() == ptrs.size());
186     for (unsigned i = 0; i < ptrs.size(); i += 2) {
187         heap.deallocate(ptrs[i]);
188         ptrs[i] = nullptr;
189     }
190     heap.scavenge();
191     unsigned numCommittedPagesBefore;
192     auto& impl = heap.impl();
193     {
194         std::lock_guard<Mutex> locker(impl.lock);
195         numCommittedPagesBefore = impl.numCommittedPages();
196     }
197     assertHasOnlyObjects(heap, toptrset(ptrs));
198     for (unsigned i = ptrs.size() / 2; i--;)
199         ptrs.push_back(heap.allocate());
200     {
201         std::lock_guard<Mutex> locker(impl.lock);
202         CHECK(numCommittedPagesBefore == impl.numCommittedPages());
203     }
204     for (void* ptr : ptrs)
205         heap.deallocate(ptr);
206     assertClean(heap);
207 }
208
209 static void testIsoFlipFlopFragmentedPagesScavengeInMiddle288()
210 {
211     static IsoHeap<char[288]> heap;
212     std::vector<void*> ptrs;
213     for (unsigned i = 100000; i--;) {
214         void* ptr = heap.allocate();
215         CHECK(ptr);
216         ptrs.push_back(ptr);
217     }
218     CHECK(toptrset(ptrs).size() == ptrs.size());
219     for (unsigned i = 0; i < ptrs.size(); i += 2) {
220         heap.deallocate(ptrs[i]);
221         ptrs[i] = nullptr;
222     }
223     heap.scavenge();
224     unsigned numCommittedPagesBefore;
225     auto& impl = heap.impl();
226     {
227         std::lock_guard<Mutex> locker(impl.lock);
228         numCommittedPagesBefore = impl.numCommittedPages();
229     }
230     assertHasOnlyObjects(heap, toptrset(ptrs));
231     for (unsigned i = ptrs.size() / 2; i--;)
232         ptrs.push_back(heap.allocate());
233     {
234         std::lock_guard<Mutex> locker(impl.lock);
235         CHECK(numCommittedPagesBefore == impl.numCommittedPages());
236     }
237     for (void* ptr : ptrs)
238         heap.deallocate(ptr);
239     assertClean(heap);
240 }
241
242 class BisoMalloced {
243     MAKE_BISO_MALLOCED(BisoMalloced);
244 public:
245     BisoMalloced(int x, float y)
246         : x(x)
247         , y(y)
248     {
249     }
250     
251     int x;
252     float y;
253 };
254
255 MAKE_BISO_MALLOCED_IMPL(BisoMalloced);
256
257 static void testBisoMalloced()
258 {
259     BisoMalloced* ptr = new BisoMalloced(4, 5);
260     assertHasObjects(BisoMalloced::bisoHeap(), { ptr });
261     delete ptr;
262     assertClean(BisoMalloced::bisoHeap());
263 }
264
265 class BisoMallocedInline {
266     MAKE_BISO_MALLOCED_INLINE(BisoMalloced);
267 public:
268     BisoMallocedInline(int x, float y)
269         : x(x)
270         , y(y)
271     {
272     }
273     
274     int x;
275     float y;
276 };
277
278 static void testBisoMallocedInline()
279 {
280     BisoMallocedInline* ptr = new BisoMallocedInline(4, 5);
281     assertHasObjects(BisoMallocedInline::bisoHeap(), { ptr });
282     delete ptr;
283     assertClean(BisoMallocedInline::bisoHeap());
284 }
285
286 static void run(const char* filter)
287 {
288     auto shouldRun = [&] (const char* testName) -> bool {
289         return !filter || !!strcasestr(testName, filter);
290     };
291     
292     RUN(testIsoSimple());
293     RUN(testIsoSimpleScavengeBeforeDealloc());
294     RUN(testIsoFlipFlopFragmentedPages());
295     RUN(testIsoFlipFlopFragmentedPagesScavengeInMiddle());
296     RUN(testIsoFlipFlopFragmentedPagesScavengeInMiddle288());
297     RUN(testBisoMalloced());
298     RUN(testBisoMallocedInline());
299     
300     puts("Success!");
301 }
302
303 int main(int argc, char** argv)
304 {
305     const char* filter = nullptr;
306     switch (argc) {
307     case 1:
308         break;
309     case 2:
310         filter = argv[1];
311         break;
312     default:
313         usage();
314         break;
315     }
316     
317     run(filter);
318     return 0;
319 }
320