YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/memory/arena-test.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
#include <memory>
33
#include <thread>
34
#include <vector>
35
36
#include <glog/logging.h>
37
#include <gtest/gtest.h>
38
39
#include "yb/util/mem_tracker.h"
40
#include "yb/util/memory/arena.h"
41
#include "yb/util/memory/mc_types.h"
42
#include "yb/util/memory/memory.h"
43
44
DEFINE_int32(num_threads, 16, "Number of threads to test");
45
DEFINE_int32(allocs_per_thread, 10000, "Number of allocations each thread should do");
46
DEFINE_int32(alloc_size, 4, "number of bytes in each allocation");
47
48
namespace yb {
49
50
using std::shared_ptr;
51
52
namespace {
53
54
// Updates external counter when object is created/destroyed.
55
// So one could check whether all objects is destoyed.
56
class Trackable {
57
 public:
58
  explicit Trackable(int* counter)
59
7
    : counter_(counter) {
60
7
    ++*counter_;
61
7
  }
62
63
  Trackable(const Trackable& rhs)
64
2
    : counter_(rhs.counter_) {
65
2
    ++*counter_;
66
2
  }
67
68
0
  Trackable& operator=(const Trackable& rhs) {
69
0
    --*counter_;
70
0
    counter_ = rhs.counter_;
71
0
    ++*counter_;
72
0
    return *this;
73
0
  }
74
75
9
  ~Trackable() {
76
9
    --*counter_;
77
9
  }
78
79
 private:
80
  int* counter_;
81
};
82
83
// Checks that counter is zero on destruction, so all objects is destroyed.
84
class CounterHolder {
85
 public:
86
  int counter = 0;
87
88
7
  ~CounterHolder() {
89
7
    CheckCounter();
90
7
  }
91
 private:
92
7
  void CheckCounter() {
93
7
    ASSERT_EQ(0, counter);
94
7
  }
95
};
96
97
class CountedArena : public CounterHolder, public Arena {
98
};
99
100
template<class ArenaType>
101
16
void AllocateThread(ArenaType *arena, uint8_t thread_index) {
102
16
  std::vector<void *> ptrs;
103
16
  ptrs.reserve(FLAGS_allocs_per_thread);
104
105
16
  char buf[FLAGS_alloc_size];
106
16
  memset(buf, thread_index, FLAGS_alloc_size);
107
108
168k
  for (int i = 0; i < FLAGS_allocs_per_thread; i++) {
109
168k
    void *alloced = arena->AllocateBytes(FLAGS_alloc_size);
110
168k
    CHECK(alloced);
111
168k
    memcpy(alloced, buf, FLAGS_alloc_size);
112
168k
    ptrs.push_back(alloced);
113
168k
  }
114
115
161k
  for (void *p : ptrs) {
116
161k
    if (memcmp(buf, p, FLAGS_alloc_size) != 0) {
117
0
      FAIL() << StringPrintf("overwritten pointer at %p", p);
118
0
    }
119
161k
  }
120
16
}
arena-test.cc:_ZN2yb12_GLOBAL__N_114AllocateThreadINS_8internal9ArenaBaseINS2_21ThreadSafeArenaTraitsEEEEEvPT_h
Line
Count
Source
101
15
void AllocateThread(ArenaType *arena, uint8_t thread_index) {
102
15
  std::vector<void *> ptrs;
103
15
  ptrs.reserve(FLAGS_allocs_per_thread);
104
105
15
  char buf[FLAGS_alloc_size];
106
15
  memset(buf, thread_index, FLAGS_alloc_size);
107
108
158k
  for (int i = 0; i < FLAGS_allocs_per_thread; i++) {
109
158k
    void *alloced = arena->AllocateBytes(FLAGS_alloc_size);
110
158k
    CHECK(alloced);
111
158k
    memcpy(alloced, buf, FLAGS_alloc_size);
112
158k
    ptrs.push_back(alloced);
113
158k
  }
114
115
151k
  for (void *p : ptrs) {
116
151k
    if (memcmp(buf, p, FLAGS_alloc_size) != 0) {
117
0
      FAIL() << StringPrintf("overwritten pointer at %p", p);
118
0
    }
119
151k
  }
120
15
}
arena-test.cc:_ZN2yb12_GLOBAL__N_114AllocateThreadINS_8internal9ArenaBaseINS2_11ArenaTraitsEEEEEvPT_h
Line
Count
Source
101
1
void AllocateThread(ArenaType *arena, uint8_t thread_index) {
102
1
  std::vector<void *> ptrs;
103
1
  ptrs.reserve(FLAGS_allocs_per_thread);
104
105
1
  char buf[FLAGS_alloc_size];
106
1
  memset(buf, thread_index, FLAGS_alloc_size);
107
108
10.0k
  for (int i = 0; i < FLAGS_allocs_per_thread; i++) {
109
10.0k
    void *alloced = arena->AllocateBytes(FLAGS_alloc_size);
110
10.0k
    CHECK(alloced);
111
10.0k
    memcpy(alloced, buf, FLAGS_alloc_size);
112
10.0k
    ptrs.push_back(alloced);
113
10.0k
  }
114
115
10.0k
  for (void *p : ptrs) {
116
10.0k
    if (memcmp(buf, p, FLAGS_alloc_size) != 0) {
117
0
      FAIL() << StringPrintf("overwritten pointer at %p", p);
118
0
    }
119
10.0k
  }
120
1
}
121
122
// Non-templated function to forward to above -- simplifies thread creation
123
16
void AllocateThreadTSArena(ThreadSafeArena *arena, uint8_t thread_index) {
124
16
  AllocateThread(arena, thread_index);
125
16
}
126
127
constexpr size_t component_size = sizeof(internal::ArenaComponent<internal::ArenaTraits>);
128
129
} // namespace
130
131
1
TEST(TestArena, TestSingleThreaded) {
132
1
  Arena arena(128, 128);
133
134
1
  AllocateThread(&arena, 0);
135
1
}
136
137
1
TEST(TestArena, TestMultiThreaded) {
138
1
  CHECK_LT(FLAGS_num_threads, 256);
139
140
1
  ThreadSafeArena arena(1024, 1024);
141
142
1
  std::vector<std::thread> threads;
143
17
  for (uint8_t i = 0; i < FLAGS_num_threads; i++) {
144
16
    threads.emplace_back(std::bind(AllocateThreadTSArena, &arena, (uint8_t)i));
145
16
  }
146
147
16
  for (auto& thr : threads) {
148
16
    thr.join();
149
16
  }
150
1
}
151
152
1
TEST(TestArena, TestAlignment) {
153
154
1
  ThreadSafeArena arena(1024, 1024);
155
1.00k
  for (int i = 0; i < 1000; i++) {
156
1.00k
    int alignment = 1 << (1 % 5);
157
158
1.00k
    void *ret = arena.AllocateBytesAligned(5, alignment);
159
2.00k
    ASSERT_EQ(0, (uintptr_t)(ret) % alignment) <<
160
2.00k
      "failed to align on " << alignment << "b boundary: " <<
161
2.00k
      ret;
162
1.00k
  }
163
1
}
164
165
2
void TestAllocations(const shared_ptr<MemTracker>& tracker, Arena* arena) {
166
  // Try some child operations.
167
2
  ASSERT_EQ(256, tracker->consumption());
168
2
  void *allocated = arena->AllocateBytes(256 - component_size);
169
2
  ASSERT_TRUE(allocated);
170
2
  ASSERT_EQ(256, tracker->consumption());
171
2
  allocated = arena->AllocateBytes(256);
172
2
  ASSERT_NE(allocated, nullptr);
173
2
  ASSERT_EQ(768, tracker->consumption());
174
2
}
175
176
// MemTrackers update their ancestors when consuming and releasing memory to compute
177
// usage totals. However, the lifetimes of parent and child trackers can be different.
178
// Validate that child trackers can still correctly update their parent stats even when
179
// the parents go out of scope.
180
1
TEST(TestArena, TestMemoryTrackerParentReferences) {
181
  // Set up a parent and child MemTracker.
182
1
  const string parent_id = "parent-id";
183
1
  const string child_id = "child-id";
184
1
  shared_ptr<MemTracker> child_tracker;
185
1
  {
186
1
    shared_ptr<MemTracker> parent_tracker = MemTracker::CreateTracker(1024, parent_id);
187
1
    child_tracker = MemTracker::CreateTracker(child_id, parent_tracker);
188
    // Parent falls out of scope here. Should still be owned by the child.
189
1
  }
190
1
  shared_ptr<MemoryTrackingBufferAllocator> allocator(
191
1
      new MemoryTrackingBufferAllocator(HeapBufferAllocator::Get(), child_tracker));
192
1
  Arena arena(allocator.get(), 256, 1024);
193
194
1
  TestAllocations(child_tracker, &arena);
195
1
}
196
197
1
TEST(TestArena, TestMemoryTrackingDontEnforce) {
198
1
  shared_ptr<MemTracker> mem_tracker = MemTracker::CreateTracker(1024, "arena-test-tracker");
199
1
  shared_ptr<MemoryTrackingBufferAllocator> allocator(
200
1
      new MemoryTrackingBufferAllocator(HeapBufferAllocator::Get(), mem_tracker));
201
1
  Arena arena(allocator.get(), 256, 1024);
202
1
  TestAllocations(mem_tracker, &arena);
203
204
  // In DEBUG mode after Reset() the last component of an arena is
205
  // cleared, but is then created again; in release mode, the last
206
  // component is not cleared. In either case, after Reset()
207
  // consumption() should equal the size of the last component which
208
  // is 512 bytes.
209
1
  arena.Reset();
210
1
  ASSERT_EQ(512, mem_tracker->consumption());
211
212
  // Allocate beyond allowed consumption. This should still go
213
  // through, since enforce_limit is false.
214
1
  auto allocated = arena.AllocateBytes(1024 - component_size);
215
1
  ASSERT_TRUE(allocated);
216
217
1
  ASSERT_EQ(1536, mem_tracker->consumption());
218
1
}
219
220
1
TEST(TestArena, TestMemoryTrackingEnforced) {
221
1
  shared_ptr<MemTracker> mem_tracker = MemTracker::CreateTracker(1024, "arena-test-tracker");
222
1
  shared_ptr<MemoryTrackingBufferAllocator> allocator(
223
1
      new MemoryTrackingBufferAllocator(HeapBufferAllocator::Get(), mem_tracker,
224
                                        // enforce limit
225
1
                                        true));
226
1
  Arena arena(allocator.get(), 256, 1024);
227
1
  ASSERT_EQ(256, mem_tracker->consumption());
228
1
  void *allocated = arena.AllocateBytes(256 - component_size);
229
1
  ASSERT_TRUE(allocated);
230
1
  ASSERT_EQ(256, mem_tracker->consumption());
231
1
  allocated = arena.AllocateBytes(1024 - component_size);
232
1
  ASSERT_EQ(allocated, nullptr);
233
1
  ASSERT_EQ(256, mem_tracker->consumption());
234
1
}
235
236
1
TEST(TestArena, TestSTLAllocator) {
237
1
  Arena a(256, 256 * 1024);
238
1
  typedef vector<int, ArenaAllocator<int>> ArenaVector;
239
1
  ArenaAllocator<int> alloc(&a);
240
1
  ArenaVector v(alloc);
241
10.0k
  for (int i = 0; i < 10000; i++) {
242
10.0k
    v.push_back(i);
243
10.0k
  }
244
10.0k
  for (int i = 0; i < 10000; i++) {
245
10.0k
    ASSERT_EQ(i, v[i]);
246
10.0k
  }
247
1
}
248
249
1
TEST(TestArena, TestUniquePtr) {
250
1
  CountedArena ca;
251
1
  MCUniPtr<Trackable> trackable(ca.NewObject<Trackable>(&ca.counter));
252
1
  ASSERT_EQ(1, ca.counter);
253
1
}
254
255
1
TEST(TestArena, TestAllocateShared) {
256
1
  CountedArena ca;
257
1
  auto trackable = ca.AllocateShared<Trackable>(&ca.counter);
258
1
  ASSERT_EQ(1, ca.counter);
259
1
}
260
261
1
TEST(TestArena, TestToShared) {
262
1
  CountedArena ca;
263
1
  auto trackable = ca.ToShared(ca.NewObject<Trackable>(&ca.counter));
264
1
  ASSERT_EQ(1, ca.counter);
265
1
}
266
267
1
TEST(TestArena, TestVector) {
268
1
  CountedArena ca;
269
1
  MCVector<Trackable> vector(&ca);
270
1
  vector.emplace_back(&ca.counter);
271
1
  ASSERT_EQ(1, ca.counter);
272
1
}
273
274
1
TEST(TestArena, TestList) {
275
1
  CountedArena ca;
276
1
  MCList<Trackable> list(&ca);
277
1
  list.emplace_back(&ca.counter);
278
1
  ASSERT_EQ(1, ca.counter);
279
1
}
280
281
1
TEST(TestArena, TestMap) {
282
1
  CountedArena ca;
283
1
  MCMap<int, Trackable> map(&ca);
284
1
  map.emplace(1, Trackable(&ca.counter));
285
1
  ASSERT_EQ(1, ca.counter);
286
1
}
287
288
1
TEST(TestArena, TestString) {
289
1
  CountedArena ca;
290
1
  MCMap<MCString, Trackable> map(&ca);
291
1
  MCString one("1", &ca);
292
1
  MCString ten("10", &ca);
293
1
  map.emplace(one, Trackable(&ca.counter));
294
1
  ASSERT_EQ(1, ca.counter);
295
296
  // Check correctness of comparison operators.
297
1
  ASSERT_LT(one, ten);
298
1
  ASSERT_FALSE(one < one);
299
1
  ASSERT_FALSE(ten < one);
300
301
1
  ASSERT_LE(one, ten);
302
1
  ASSERT_LE(one, one);
303
1
  ASSERT_FALSE(ten <= one);
304
305
1
  ASSERT_GE(ten, one);
306
1
  ASSERT_GE(one, one);
307
1
  ASSERT_FALSE(one >= ten);
308
309
1
  ASSERT_GT(ten, one);
310
1
  ASSERT_FALSE(one > one);
311
1
  ASSERT_FALSE(one > ten);
312
1
}
313
314
} // namespace yb