xref: /aosp_15_r20/external/abseil-cpp/absl/base/internal/low_level_alloc_test.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
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.
CheckBlockDesc(const BlockDesc & d)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.
RandomizeBlockDesc(BlockDesc * d)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.
Test(bool use_new_arena,bool call_malloc_hook,int n)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 {
BeforeMainabsl::base_internal::__anon3e5fdcc30111::BeforeMain149   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 
main(int argc,char * argv[])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 }
181