low_level_alloc_test.cc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. #include "absl/base/internal/low_level_alloc.h"
  15. #include <stdint.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <thread> // NOLINT(build/c++11)
  19. #include <unordered_map>
  20. #include <utility>
  21. #ifdef __EMSCRIPTEN__
  22. #include <emscripten.h>
  23. #endif
  24. #include "absl/container/node_hash_map.h"
  25. namespace absl {
  26. ABSL_NAMESPACE_BEGIN
  27. namespace base_internal {
  28. namespace {
  29. // This test doesn't use gtest since it needs to test that everything
  30. // works before main().
  31. #define TEST_ASSERT(x) \
  32. if (!(x)) { \
  33. printf("TEST_ASSERT(%s) FAILED ON LINE %d\n", #x, __LINE__); \
  34. abort(); \
  35. }
  36. // a block of memory obtained from the allocator
  37. struct BlockDesc {
  38. char *ptr; // pointer to memory
  39. int len; // number of bytes
  40. int fill; // filled with data starting with this
  41. };
  42. // Check that the pattern placed in the block d
  43. // by RandomizeBlockDesc is still there.
  44. static void CheckBlockDesc(const BlockDesc &d) {
  45. for (int i = 0; i != d.len; i++) {
  46. TEST_ASSERT((d.ptr[i] & 0xff) == ((d.fill + i) & 0xff));
  47. }
  48. }
  49. // Fill the block "*d" with a pattern
  50. // starting with a random byte.
  51. static void RandomizeBlockDesc(BlockDesc *d) {
  52. d->fill = rand() & 0xff;
  53. for (int i = 0; i != d->len; i++) {
  54. d->ptr[i] = (d->fill + i) & 0xff;
  55. }
  56. }
  57. // Use to indicate to the malloc hooks that
  58. // this calls is from LowLevelAlloc.
  59. static bool using_low_level_alloc = false;
  60. // n times, toss a coin, and based on the outcome
  61. // either allocate a new block or deallocate an old block.
  62. // New blocks are placed in a std::unordered_map with a random key
  63. // and initialized with RandomizeBlockDesc().
  64. // If keys conflict, the older block is freed.
  65. // Old blocks are always checked with CheckBlockDesc()
  66. // before being freed. At the end of the run,
  67. // all remaining allocated blocks are freed.
  68. // If use_new_arena is true, use a fresh arena, and then delete it.
  69. // If call_malloc_hook is true and user_arena is true,
  70. // allocations and deallocations are reported via the MallocHook
  71. // interface.
  72. static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
  73. typedef absl::node_hash_map<int, BlockDesc> AllocMap;
  74. AllocMap allocated;
  75. AllocMap::iterator it;
  76. BlockDesc block_desc;
  77. int rnd;
  78. LowLevelAlloc::Arena *arena = 0;
  79. if (use_new_arena) {
  80. int32_t flags = call_malloc_hook ? LowLevelAlloc::kCallMallocHook : 0;
  81. arena = LowLevelAlloc::NewArena(flags);
  82. }
  83. for (int i = 0; i != n; i++) {
  84. if (i != 0 && i % 10000 == 0) {
  85. printf(".");
  86. fflush(stdout);
  87. }
  88. switch (rand() & 1) { // toss a coin
  89. case 0: // coin came up heads: add a block
  90. using_low_level_alloc = true;
  91. block_desc.len = rand() & 0x3fff;
  92. block_desc.ptr =
  93. reinterpret_cast<char *>(
  94. arena == 0
  95. ? LowLevelAlloc::Alloc(block_desc.len)
  96. : LowLevelAlloc::AllocWithArena(block_desc.len, arena));
  97. using_low_level_alloc = false;
  98. RandomizeBlockDesc(&block_desc);
  99. rnd = rand();
  100. it = allocated.find(rnd);
  101. if (it != allocated.end()) {
  102. CheckBlockDesc(it->second);
  103. using_low_level_alloc = true;
  104. LowLevelAlloc::Free(it->second.ptr);
  105. using_low_level_alloc = false;
  106. it->second = block_desc;
  107. } else {
  108. allocated[rnd] = block_desc;
  109. }
  110. break;
  111. case 1: // coin came up tails: remove a block
  112. it = allocated.begin();
  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. allocated.erase(it);
  119. }
  120. break;
  121. }
  122. }
  123. // remove all remaining blocks
  124. while ((it = allocated.begin()) != 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. if (use_new_arena) {
  132. TEST_ASSERT(LowLevelAlloc::DeleteArena(arena));
  133. }
  134. }
  135. // LowLevelAlloc is designed to be safe to call before main().
  136. static struct BeforeMain {
  137. BeforeMain() {
  138. Test(false, false, 50000);
  139. Test(true, false, 50000);
  140. Test(true, true, 50000);
  141. }
  142. } before_main;
  143. } // namespace
  144. } // namespace base_internal
  145. ABSL_NAMESPACE_END
  146. } // namespace absl
  147. int main(int argc, char *argv[]) {
  148. // The actual test runs in the global constructor of `before_main`.
  149. printf("PASS\n");
  150. #ifdef __EMSCRIPTEN__
  151. // clang-format off
  152. // This is JS here. Don't try to format it.
  153. MAIN_THREAD_EM_ASM({
  154. if (ENVIRONMENT_IS_WEB) {
  155. if (typeof TEST_FINISH === 'function') {
  156. TEST_FINISH($0);
  157. } else {
  158. console.error('Attempted to exit with status ' + $0);
  159. console.error('But TEST_FINSIHED is not a function.');
  160. }
  161. }
  162. }, 0);
  163. // clang-format on
  164. #endif
  165. return 0;
  166. }