low_level_alloc_test.cc (5608B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/base/internal/low_level_alloc.h" 16 17 #include <stdint.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <thread> // NOLINT(build/c++11) 21 #include <unordered_map> 22 #include <utility> 23 24 #ifdef __EMSCRIPTEN__ 25 #include <emscripten.h> 26 #endif 27 28 #include "absl/container/node_hash_map.h" 29 30 namespace absl { 31 ABSL_NAMESPACE_BEGIN 32 namespace base_internal { 33 namespace { 34 35 // This test doesn't use gtest since it needs to test that everything 36 // works before main(). 37 #define TEST_ASSERT(x) \ 38 if (!(x)) { \ 39 printf("TEST_ASSERT(%s) FAILED ON LINE %d\n", #x, __LINE__); \ 40 abort(); \ 41 } 42 43 // a block of memory obtained from the allocator 44 struct BlockDesc { 45 char *ptr; // pointer to memory 46 int len; // number of bytes 47 int fill; // filled with data starting with this 48 }; 49 50 // Check that the pattern placed in the block d 51 // by RandomizeBlockDesc is still there. 52 static void CheckBlockDesc(const BlockDesc &d) { 53 for (int i = 0; i != d.len; i++) { 54 TEST_ASSERT((d.ptr[i] & 0xff) == ((d.fill + i) & 0xff)); 55 } 56 } 57 58 // Fill the block "*d" with a pattern 59 // starting with a random byte. 60 static void RandomizeBlockDesc(BlockDesc *d) { 61 d->fill = rand() & 0xff; 62 for (int i = 0; i != d->len; i++) { 63 d->ptr[i] = (d->fill + i) & 0xff; 64 } 65 } 66 67 // Use to indicate to the malloc hooks that 68 // this calls is from LowLevelAlloc. 69 static bool using_low_level_alloc = false; 70 71 // n times, toss a coin, and based on the outcome 72 // either allocate a new block or deallocate an old block. 73 // New blocks are placed in a std::unordered_map with a random key 74 // and initialized with RandomizeBlockDesc(). 75 // If keys conflict, the older block is freed. 76 // Old blocks are always checked with CheckBlockDesc() 77 // before being freed. At the end of the run, 78 // all remaining allocated blocks are freed. 79 // If use_new_arena is true, use a fresh arena, and then delete it. 80 // If call_malloc_hook is true and user_arena is true, 81 // allocations and deallocations are reported via the MallocHook 82 // interface. 83 static void Test(bool use_new_arena, bool call_malloc_hook, int n) { 84 typedef absl::node_hash_map<int, BlockDesc> AllocMap; 85 AllocMap allocated; 86 AllocMap::iterator it; 87 BlockDesc block_desc; 88 int rnd; 89 LowLevelAlloc::Arena *arena = nullptr; 90 if (use_new_arena) { 91 int32_t flags = call_malloc_hook ? LowLevelAlloc::kCallMallocHook : 0; 92 arena = LowLevelAlloc::NewArena(flags); 93 } 94 for (int i = 0; i != n; i++) { 95 if (i != 0 && i % 10000 == 0) { 96 printf("."); 97 fflush(stdout); 98 } 99 100 switch (rand() & 1) { // toss a coin 101 case 0: // coin came up heads: add a block 102 using_low_level_alloc = true; 103 block_desc.len = rand() & 0x3fff; 104 block_desc.ptr = reinterpret_cast<char *>( 105 arena == nullptr 106 ? LowLevelAlloc::Alloc(block_desc.len) 107 : LowLevelAlloc::AllocWithArena(block_desc.len, arena)); 108 using_low_level_alloc = false; 109 RandomizeBlockDesc(&block_desc); 110 rnd = rand(); 111 it = allocated.find(rnd); 112 if (it != allocated.end()) { 113 CheckBlockDesc(it->second); 114 using_low_level_alloc = true; 115 LowLevelAlloc::Free(it->second.ptr); 116 using_low_level_alloc = false; 117 it->second = block_desc; 118 } else { 119 allocated[rnd] = block_desc; 120 } 121 break; 122 case 1: // coin came up tails: remove a block 123 it = allocated.begin(); 124 if (it != allocated.end()) { 125 CheckBlockDesc(it->second); 126 using_low_level_alloc = true; 127 LowLevelAlloc::Free(it->second.ptr); 128 using_low_level_alloc = false; 129 allocated.erase(it); 130 } 131 break; 132 } 133 } 134 // remove all remaining blocks 135 while ((it = allocated.begin()) != allocated.end()) { 136 CheckBlockDesc(it->second); 137 using_low_level_alloc = true; 138 LowLevelAlloc::Free(it->second.ptr); 139 using_low_level_alloc = false; 140 allocated.erase(it); 141 } 142 if (use_new_arena) { 143 TEST_ASSERT(LowLevelAlloc::DeleteArena(arena)); 144 } 145 } 146 147 // LowLevelAlloc is designed to be safe to call before main(). 148 static struct BeforeMain { 149 BeforeMain() { 150 Test(false, false, 50000); 151 Test(true, false, 50000); 152 Test(true, true, 50000); 153 } 154 } before_main; 155 156 } // namespace 157 } // namespace base_internal 158 ABSL_NAMESPACE_END 159 } // namespace absl 160 161 int main(int argc, char *argv[]) { 162 // The actual test runs in the global constructor of `before_main`. 163 printf("PASS\n"); 164 #ifdef __EMSCRIPTEN__ 165 // clang-format off 166 // This is JS here. Don't try to format it. 167 MAIN_THREAD_EM_ASM({ 168 if (ENVIRONMENT_IS_WEB) { 169 if (typeof TEST_FINISH === 'function') { 170 TEST_FINISH($0); 171 } else { 172 console.error('Attempted to exit with status ' + $0); 173 console.error('But TEST_FINSIHED is not a function.'); 174 } 175 } 176 }, 0); 177 // clang-format on 178 #endif 179 return 0; 180 }