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