UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "util_allocator.h" 30 31 void *cx_malloc_testing(void *d, size_t n) { 32 auto data = reinterpret_cast<CxTestingAllocator *>(d); 33 void *ptr = malloc(n); 34 data->alloc_total++; 35 if (ptr == nullptr) { 36 data->alloc_failed++; 37 } else { 38 data->tracked.insert(ptr); 39 } 40 return ptr; 41 } 42 43 void *cx_realloc_testing(void *d, void *mem, size_t n) { 44 auto data = reinterpret_cast<CxTestingAllocator *>(d); 45 void *ptr = realloc(mem, n); 46 if (ptr == mem) { 47 return ptr; 48 } else { 49 data->alloc_total++; 50 if (ptr == nullptr) { 51 data->alloc_failed++; 52 } else { 53 data->free_total++; 54 if (data->tracked.erase(mem) == 0) { 55 data->free_failed++; 56 } 57 data->tracked.insert(ptr); 58 } 59 return ptr; 60 } 61 } 62 63 void *cx_calloc_testing(void *d, size_t nelem, size_t n) { 64 auto data = reinterpret_cast<CxTestingAllocator *>(d); 65 void *ptr = calloc(nelem, n); 66 data->alloc_total++; 67 if (ptr == nullptr) { 68 data->alloc_failed++; 69 } else { 70 data->tracked.insert(ptr); 71 } 72 return ptr; 73 } 74 75 void cx_free_testing(void *d, void *mem) { 76 auto data = reinterpret_cast<CxTestingAllocator *>(d); 77 data->free_total++; 78 if (data->tracked.erase(mem) == 0) { 79 data->free_failed++; 80 // do not even attempt to free mem, because it is likely to segfault 81 } else { 82 free(mem); 83 } 84 } 85 86 cx_allocator_class cx_testing_allocator_class = { 87 cx_malloc_testing, 88 cx_realloc_testing, 89 cx_calloc_testing, 90 cx_free_testing 91 }; 92 93 CxTestingAllocator::CxTestingAllocator() : CxAllocator() { 94 cl = &cx_testing_allocator_class; 95 data = this; 96 } 97 98 bool CxTestingAllocator::used() const { 99 return alloc_total > 0; 100 } 101 102 bool CxTestingAllocator::verify() const { 103 return tracked.empty() && alloc_failed == 0 && free_failed == 0 && alloc_total == free_total; 104 } 105 106 // SELF-TEST 107 108 #include <gtest/gtest.h> 109 110 TEST(TestingAllocator, ExpectFree) { 111 CxTestingAllocator allocator; 112 113 ASSERT_TRUE(allocator.verify()); 114 EXPECT_FALSE(allocator.used()); 115 auto ptr = cxMalloc(&allocator, 16); 116 EXPECT_TRUE(allocator.used()); 117 ASSERT_NE(ptr, nullptr); 118 EXPECT_FALSE(allocator.verify()); 119 120 cxFree(&allocator, ptr); 121 EXPECT_TRUE(allocator.verify()); 122 } 123 124 TEST(TestingAllocator, DetectDoubleFree) { 125 CxTestingAllocator allocator; 126 127 ASSERT_TRUE(allocator.verify()); 128 auto ptr = cxMalloc(&allocator, 16); 129 ASSERT_NE(ptr, nullptr); 130 131 cxFree(&allocator, ptr); 132 EXPECT_TRUE(allocator.verify()); 133 ASSERT_NO_FATAL_FAILURE(cxFree(&allocator, ptr)); 134 EXPECT_FALSE(allocator.verify()); 135 } 136 137 TEST(TestingAllocator, FreeUntracked) { 138 CxTestingAllocator allocator; 139 140 auto ptr = malloc(16); 141 ASSERT_TRUE(allocator.verify()); 142 ASSERT_NO_FATAL_FAILURE(cxFree(&allocator, ptr)); 143 EXPECT_FALSE(allocator.verify()); 144 ASSERT_NO_FATAL_FAILURE(free(ptr)); 145 } 146 147 TEST(TestingAllocator, FullLifecycleWithRealloc) { 148 CxTestingAllocator allocator; 149 ASSERT_TRUE(allocator.verify()); 150 auto ptr = cxMalloc(&allocator, 16); 151 ASSERT_NE(ptr, nullptr); 152 EXPECT_EQ(allocator.tracked.size(), 1); 153 ptr = cxRealloc(&allocator, ptr, 256); 154 ASSERT_NE(ptr, nullptr); 155 EXPECT_EQ(allocator.tracked.size(), 1); 156 cxFree(&allocator, ptr); 157 EXPECT_TRUE(allocator.verify()); 158 } 159 160 TEST(TestingAllocator, CallocInitializes) { 161 CxTestingAllocator allocator; 162 const char zeros[16] = {0}; 163 auto ptr = cxCalloc(&allocator, 16, 1); 164 EXPECT_EQ(memcmp(ptr, zeros, 16), 0); 165 cxFree(&allocator, ptr); 166 EXPECT_TRUE(allocator.verify()); 167 } 168