Loading...
tests/MallocBenchTest/BMALLOC/bmalloc/test/testbmalloc.cpp /dev/null libmalloc-283.100.6
--- /dev/null
+++ libmalloc/libmalloc-283.100.6/tests/MallocBenchTest/BMALLOC/bmalloc/test/testbmalloc.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include <bmalloc/bmalloc.h>
+#include <bmalloc/Environment.h>
+#include <bmalloc/IsoHeapInlines.h>
+#include <cmath>
+#include <cstdlib>
+#include <set>
+#include <vector>
+
+using namespace bmalloc;
+using namespace bmalloc::api;
+
+// We don't have a NO_RETURN_DUE_TO_EXIT, nor should we. That's ridiculous.
+static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
+
+static void usage()
+{
+    puts("Usage: testb3 [<filter>]");
+    if (hiddenTruthBecauseNoReturnIsStupid())
+        exit(1);
+}
+
+#define RUN(test) do {                          \
+        if (!shouldRun(#test))                  \
+            break;                              \
+        puts(#test "...");                      \
+        test;                                   \
+        puts(#test ": OK!");                    \
+    } while (false)
+
+// Nothing fancy for now; we just use the existing WTF assertion machinery.
+#define CHECK(x) do {                                                   \
+        if (!!(x))                                                      \
+            break;                                                      \
+        fprintf(stderr, "%s:%d: in %s: assertion %s failed.\n",         \
+            __FILE__, __LINE__, __PRETTY_FUNCTION__, #x);               \
+        abort();                                                        \
+    } while (false)
+
+static std::set<void*> toptrset(const std::vector<void*>& ptrs)
+{
+    std::set<void*> result;
+    for (void* ptr : ptrs) {
+        if (ptr)
+            result.insert(ptr);
+    }
+    return result;
+}
+
+static void assertEmptyPointerSet(const std::set<void*>& pointers)
+{
+    if (PerProcess<Environment>::get()->isDebugHeapEnabled()) {
+        printf("    skipping checks because DebugHeap.\n");
+        return;
+    }
+    if (pointers.empty())
+        return;
+    printf("Pointer set not empty!\n");
+    printf("Pointers:");
+    for (void* ptr : pointers)
+        printf(" %p", ptr);
+    printf("\n");
+    CHECK(pointers.empty());
+}
+
+template<typename heapType>
+static void assertHasObjects(IsoHeap<heapType>& heap, std::set<void*> pointers)
+{
+    if (PerProcess<Environment>::get()->isDebugHeapEnabled()) {
+        printf("    skipping checks because DebugHeap.\n");
+        return;
+    }
+    auto& impl = heap.impl();
+    std::lock_guard<Mutex> locker(impl.lock);
+    impl.forEachLiveObject(
+        [&] (void* object) {
+            pointers.erase(object);
+        });
+    assertEmptyPointerSet(pointers);
+}
+
+template<typename heapType>
+static void assertHasOnlyObjects(IsoHeap<heapType>& heap, std::set<void*> pointers)
+{
+    if (PerProcess<Environment>::get()->isDebugHeapEnabled()) {
+        printf("    skipping checks because DebugHeap.\n");
+        return;
+    }
+    auto& impl = heap.impl();
+    std::lock_guard<Mutex> locker(impl.lock);
+    impl.forEachLiveObject(
+        [&] (void* object) {
+            CHECK(pointers.erase(object) == 1);
+        });
+    assertEmptyPointerSet(pointers);
+}
+
+template<typename heapType>
+static void assertClean(IsoHeap<heapType>& heap)
+{
+    scavengeThisThread();
+    if (!PerProcess<Environment>::get()->isDebugHeapEnabled()) {
+        auto& impl = heap.impl();
+        {
+            std::lock_guard<Mutex> locker(impl.lock);
+            CHECK(!impl.numLiveObjects());
+        }
+    }
+    heap.scavenge();
+    if (!PerProcess<Environment>::get()->isDebugHeapEnabled()) {
+        auto& impl = heap.impl();
+        std::lock_guard<Mutex> locker(impl.lock);
+        CHECK(!impl.numCommittedPages());
+    }
+}
+
+static void testIsoSimple()
+{
+    static IsoHeap<double> heap;
+    void* ptr1 = heap.allocate();
+    CHECK(ptr1);
+    void* ptr2 = heap.allocate();
+    CHECK(ptr2);
+    CHECK(ptr1 != ptr2);
+    CHECK(std::abs(static_cast<char*>(ptr1) - static_cast<char*>(ptr2)) >= 8);
+    assertHasObjects(heap, {ptr1, ptr2});
+    heap.deallocate(ptr1);
+    heap.deallocate(ptr2);
+    assertClean(heap);
+}
+
+static void testIsoSimpleScavengeBeforeDealloc()
+{
+    static IsoHeap<double> heap;
+    void* ptr1 = heap.allocate();
+    CHECK(ptr1);
+    void* ptr2 = heap.allocate();
+    CHECK(ptr2);
+    CHECK(ptr1 != ptr2);
+    CHECK(std::abs(static_cast<char*>(ptr1) - static_cast<char*>(ptr2)) >= 8);
+    scavengeThisThread();
+    assertHasOnlyObjects(heap, {ptr1, ptr2});
+    heap.deallocate(ptr1);
+    heap.deallocate(ptr2);
+    assertClean(heap);
+}
+
+static void testIsoFlipFlopFragmentedPages()
+{
+    static IsoHeap<double> heap;
+    std::vector<void*> ptrs;
+    for (unsigned i = 100000; i--;) {
+        void* ptr = heap.allocate();
+        CHECK(ptr);
+        ptrs.push_back(ptr);
+    }
+    for (unsigned i = 0; i < ptrs.size(); i += 2) {
+        heap.deallocate(ptrs[i]);
+        ptrs[i] = nullptr;
+    }
+    for (unsigned i = ptrs.size() / 2; i--;)
+        ptrs.push_back(heap.allocate());
+    for (void* ptr : ptrs)
+        heap.deallocate(ptr);
+    assertClean(heap);
+}
+
+static void testIsoFlipFlopFragmentedPagesScavengeInMiddle()
+{
+    static IsoHeap<double> heap;
+    std::vector<void*> ptrs;
+    for (unsigned i = 100000; i--;) {
+        void* ptr = heap.allocate();
+        CHECK(ptr);
+        ptrs.push_back(ptr);
+    }
+    CHECK(toptrset(ptrs).size() == ptrs.size());
+    for (unsigned i = 0; i < ptrs.size(); i += 2) {
+        heap.deallocate(ptrs[i]);
+        ptrs[i] = nullptr;
+    }
+    heap.scavenge();
+    unsigned numCommittedPagesBefore;
+    auto& impl = heap.impl();
+    {
+        std::lock_guard<Mutex> locker(impl.lock);
+        numCommittedPagesBefore = impl.numCommittedPages();
+    }
+    assertHasOnlyObjects(heap, toptrset(ptrs));
+    for (unsigned i = ptrs.size() / 2; i--;)
+        ptrs.push_back(heap.allocate());
+    {
+        std::lock_guard<Mutex> locker(impl.lock);
+        CHECK(numCommittedPagesBefore == impl.numCommittedPages());
+    }
+    for (void* ptr : ptrs)
+        heap.deallocate(ptr);
+    assertClean(heap);
+}
+
+static void testIsoFlipFlopFragmentedPagesScavengeInMiddle288()
+{
+    static IsoHeap<char[288]> heap;
+    std::vector<void*> ptrs;
+    for (unsigned i = 100000; i--;) {
+        void* ptr = heap.allocate();
+        CHECK(ptr);
+        ptrs.push_back(ptr);
+    }
+    CHECK(toptrset(ptrs).size() == ptrs.size());
+    for (unsigned i = 0; i < ptrs.size(); i += 2) {
+        heap.deallocate(ptrs[i]);
+        ptrs[i] = nullptr;
+    }
+    heap.scavenge();
+    unsigned numCommittedPagesBefore;
+    auto& impl = heap.impl();
+    {
+        std::lock_guard<Mutex> locker(impl.lock);
+        numCommittedPagesBefore = impl.numCommittedPages();
+    }
+    assertHasOnlyObjects(heap, toptrset(ptrs));
+    for (unsigned i = ptrs.size() / 2; i--;)
+        ptrs.push_back(heap.allocate());
+    {
+        std::lock_guard<Mutex> locker(impl.lock);
+        CHECK(numCommittedPagesBefore == impl.numCommittedPages());
+    }
+    for (void* ptr : ptrs)
+        heap.deallocate(ptr);
+    assertClean(heap);
+}
+
+class BisoMalloced {
+    MAKE_BISO_MALLOCED(BisoMalloced);
+public:
+    BisoMalloced(int x, float y)
+        : x(x)
+        , y(y)
+    {
+    }
+    
+    int x;
+    float y;
+};
+
+MAKE_BISO_MALLOCED_IMPL(BisoMalloced);
+
+static void testBisoMalloced()
+{
+    BisoMalloced* ptr = new BisoMalloced(4, 5);
+    assertHasObjects(BisoMalloced::bisoHeap(), { ptr });
+    delete ptr;
+    assertClean(BisoMalloced::bisoHeap());
+}
+
+class BisoMallocedInline {
+    MAKE_BISO_MALLOCED_INLINE(BisoMalloced);
+public:
+    BisoMallocedInline(int x, float y)
+        : x(x)
+        , y(y)
+    {
+    }
+    
+    int x;
+    float y;
+};
+
+static void testBisoMallocedInline()
+{
+    BisoMallocedInline* ptr = new BisoMallocedInline(4, 5);
+    assertHasObjects(BisoMallocedInline::bisoHeap(), { ptr });
+    delete ptr;
+    assertClean(BisoMallocedInline::bisoHeap());
+}
+
+static void run(const char* filter)
+{
+    auto shouldRun = [&] (const char* testName) -> bool {
+        return !filter || !!strcasestr(testName, filter);
+    };
+    
+    RUN(testIsoSimple());
+    RUN(testIsoSimpleScavengeBeforeDealloc());
+    RUN(testIsoFlipFlopFragmentedPages());
+    RUN(testIsoFlipFlopFragmentedPagesScavengeInMiddle());
+    RUN(testIsoFlipFlopFragmentedPagesScavengeInMiddle288());
+    RUN(testBisoMalloced());
+    RUN(testBisoMallocedInline());
+    
+    puts("Success!");
+}
+
+int main(int argc, char** argv)
+{
+    const char* filter = nullptr;
+    switch (argc) {
+    case 1:
+        break;
+    case 2:
+        filter = argv[1];
+        break;
+    default:
+        usage();
+        break;
+    }
+    
+    run(filter);
+    return 0;
+}
+