• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 0;
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 =
105         reinterpret_cast<char *>(
106                         arena == 0
107                         ? LowLevelAlloc::Alloc(block_desc.len)
108                         : LowLevelAlloc::AllocWithArena(block_desc.len, arena));
109       using_low_level_alloc = false;
110       RandomizeBlockDesc(&block_desc);
111       rnd = rand();
112       it = allocated.find(rnd);
113       if (it != allocated.end()) {
114         CheckBlockDesc(it->second);
115         using_low_level_alloc = true;
116         LowLevelAlloc::Free(it->second.ptr);
117         using_low_level_alloc = false;
118         it->second = block_desc;
119       } else {
120         allocated[rnd] = block_desc;
121       }
122       break;
123     case 1:     // coin came up tails: remove a block
124       it = allocated.begin();
125       if (it != allocated.end()) {
126         CheckBlockDesc(it->second);
127         using_low_level_alloc = true;
128         LowLevelAlloc::Free(it->second.ptr);
129         using_low_level_alloc = false;
130         allocated.erase(it);
131       }
132       break;
133     }
134   }
135   // remove all remaining blocks
136   while ((it = allocated.begin()) != allocated.end()) {
137     CheckBlockDesc(it->second);
138     using_low_level_alloc = true;
139     LowLevelAlloc::Free(it->second.ptr);
140     using_low_level_alloc = false;
141     allocated.erase(it);
142   }
143   if (use_new_arena) {
144     TEST_ASSERT(LowLevelAlloc::DeleteArena(arena));
145   }
146 }
147 
148 // LowLevelAlloc is designed to be safe to call before main().
149 static struct BeforeMain {
BeforeMainabsl::base_internal::__anondda617390111::BeforeMain150   BeforeMain() {
151     Test(false, false, 50000);
152     Test(true, false, 50000);
153     Test(true, true, 50000);
154   }
155 } before_main;
156 
157 }  // namespace
158 }  // namespace base_internal
159 ABSL_NAMESPACE_END
160 }  // namespace absl
161 
main(int argc,char * argv[])162 int main(int argc, char *argv[]) {
163   // The actual test runs in the global constructor of `before_main`.
164   printf("PASS\n");
165 #ifdef __EMSCRIPTEN__
166   // clang-format off
167 // This is JS here. Don't try to format it.
168     MAIN_THREAD_EM_ASM({
169       if (ENVIRONMENT_IS_WEB) {
170         if (typeof TEST_FINISH === 'function') {
171           TEST_FINISH($0);
172         } else {
173           console.error('Attempted to exit with status ' + $0);
174           console.error('But TEST_FINSIHED is not a function.');
175         }
176       }
177     }, 0);
178 // clang-format on
179 #endif
180   return 0;
181 }
182