1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
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 * http://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
16 #include <array>
17 #include <atomic>
18 #include <chrono>
19 #include <condition_variable>
20 #include <thread>
21
22 #include "gtest/gtest.h"
23 #include "iostream"
24 #include "runtime/include/coretypes/string.h"
25 #include "runtime/include/runtime.h"
26 #include "runtime/include/panda_vm.h"
27 #include "runtime/mem/malloc-proxy-allocator-inl.h"
28 #include "runtime/mem/mem_stats.h"
29 #include "runtime/mem/mem_stats_default.h"
30 #include "runtime/mem/runslots_allocator-inl.h"
31
32 namespace panda::mem::test {
33
34 #ifndef PANDA_NIGHTLY_TEST_ON
35 constexpr uint64_t ITERATION = 256;
36 constexpr size_t NUM_THREADS = 2;
37 #else
38 constexpr uint64_t ITERATION = 1 << 17;
39 constexpr size_t NUM_THREADS = 8;
40 #endif
41
42 using NonObjectAllocator = RunSlotsAllocator<RawMemoryConfig>;
43
44 class MemStatsTest : public testing::Test {
45 public:
MemStatsTest()46 MemStatsTest()
47 {
48 // we need runtime for creating objects
49 RuntimeOptions options;
50 options.SetShouldLoadBootPandaFiles(false);
51 options.SetShouldInitializeIntrinsics(false);
52 options.SetGcType("stw");
53 options.SetRunGcInPlace(true);
54 Runtime::Create(options);
55 thread_ = panda::MTManagedThread::GetCurrent();
56 thread_->ManagedCodeBegin();
57 }
58
~MemStatsTest()59 ~MemStatsTest() override
60 {
61 thread_->ManagedCodeEnd();
62 Runtime::Destroy();
63 }
64
65 protected:
66 panda::MTManagedThread *thread_ {nullptr};
67 };
68
69 using MallocProxyNonObjectAllocator = MallocProxyAllocator<RawMemoryConfig>;
70
71 class RawStatsBeforeTest {
72 size_t raw_bytes_allocated_before_test;
73 size_t raw_bytes_freed_before_test;
74 size_t raw_bytes_footprint_before_rest;
75
76 public:
RawStatsBeforeTest(MemStatsType * stats)77 explicit RawStatsBeforeTest(MemStatsType *stats)
78 : raw_bytes_allocated_before_test(stats->GetAllocated(SpaceType::SPACE_TYPE_INTERNAL)),
79 raw_bytes_freed_before_test(stats->GetFreed(SpaceType::SPACE_TYPE_INTERNAL)),
80 raw_bytes_footprint_before_rest(stats->GetFootprint(SpaceType::SPACE_TYPE_INTERNAL))
81 {
82 }
83
GetRawBytesAllocatedBeforeTest() const84 [[nodiscard]] size_t GetRawBytesAllocatedBeforeTest() const
85 {
86 return raw_bytes_allocated_before_test;
87 }
88
GetRawBytesFreedBeforeTest() const89 [[nodiscard]] size_t GetRawBytesFreedBeforeTest() const
90 {
91 return raw_bytes_freed_before_test;
92 }
93
GetRawBytesFootprintBeforeTest() const94 [[nodiscard]] size_t GetRawBytesFootprintBeforeTest() const
95 {
96 return raw_bytes_footprint_before_rest;
97 }
98 };
99
AssertHeapStats(MemStatsType * stats,size_t bytes_in_heap,size_t heap_bytes_allocated_,size_t heap_bytes_freed_)100 void AssertHeapStats(MemStatsType *stats, size_t bytes_in_heap, size_t heap_bytes_allocated_, size_t heap_bytes_freed_)
101 {
102 ASSERT_EQ(heap_bytes_allocated_, stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT));
103 ASSERT_EQ(heap_bytes_freed_, stats->GetFreed(SpaceType::SPACE_TYPE_OBJECT));
104 ASSERT_EQ(bytes_in_heap, stats->GetFootprint(SpaceType::SPACE_TYPE_OBJECT));
105 }
106
AssertHeapHumongousStats(MemStatsType * stats,size_t bytes_in_heap,size_t heap_bytes_allocated_,size_t heap_bytes_freed_)107 void AssertHeapHumongousStats(MemStatsType *stats, size_t bytes_in_heap, size_t heap_bytes_allocated_,
108 size_t heap_bytes_freed_)
109 {
110 ASSERT_EQ(heap_bytes_allocated_, stats->GetAllocated(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT));
111 ASSERT_EQ(heap_bytes_freed_, stats->GetFreed(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT));
112 ASSERT_EQ(bytes_in_heap, stats->GetFootprint(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT));
113 }
114
AssertHeapObjectsStats(MemStatsType * stats,size_t heap_objects_allocated_,size_t heap_objects_freed_,size_t heap_humungous_objects_allocated_,size_t heap_humungous_objects_freed_)115 void AssertHeapObjectsStats(MemStatsType *stats, size_t heap_objects_allocated_, size_t heap_objects_freed_,
116 size_t heap_humungous_objects_allocated_, size_t heap_humungous_objects_freed_)
117 {
118 ASSERT_EQ(heap_objects_allocated_, stats->GetTotalObjectsAllocated());
119 ASSERT_EQ(heap_objects_freed_, stats->GetTotalObjectsFreed());
120
121 // On arm-32 platform, we should cast the uint64_t(-1) to size_t(-1)
122 ASSERT_EQ(heap_objects_allocated_ - heap_humungous_objects_allocated_,
123 static_cast<size_t>(stats->GetTotalRegularObjectsAllocated()));
124 ASSERT_EQ(heap_objects_freed_ - heap_humungous_objects_freed_,
125 static_cast<size_t>(stats->GetTotalRegularObjectsFreed()));
126
127 ASSERT_EQ(heap_humungous_objects_allocated_, stats->GetTotalHumongousObjectsAllocated());
128 ASSERT_EQ(heap_humungous_objects_freed_, stats->GetTotalHumongousObjectsFreed());
129
130 ASSERT_EQ(heap_objects_allocated_ - heap_objects_freed_, stats->GetObjectsCountAlive());
131 ASSERT_EQ(heap_objects_allocated_ - heap_objects_freed_ + heap_humungous_objects_allocated_ -
132 heap_humungous_objects_freed_,
133 stats->GetRegularObjectsCountAlive());
134 ASSERT_EQ(heap_humungous_objects_allocated_ - heap_humungous_objects_freed_,
135 stats->GetHumonguousObjectsCountAlive());
136 }
137
138 /**
139 * We add bytes which we allocated before tests for our internal structures, but we don't add it to `freed` because
140 * destructors haven't be called yet.
141 */
AssertRawStats(MemStatsType * stats,size_t raw_bytes_allocated,size_t raw_bytes_freed,size_t raw_bytes_footprint,RawStatsBeforeTest & statsBeforeTest)142 void AssertRawStats(MemStatsType *stats, size_t raw_bytes_allocated, size_t raw_bytes_freed, size_t raw_bytes_footprint,
143 RawStatsBeforeTest &statsBeforeTest)
144 {
145 ASSERT_EQ(raw_bytes_allocated + statsBeforeTest.GetRawBytesAllocatedBeforeTest(),
146 stats->GetAllocated(SpaceType::SPACE_TYPE_INTERNAL));
147 ASSERT_EQ(raw_bytes_freed + statsBeforeTest.GetRawBytesFreedBeforeTest(),
148 stats->GetFreed(SpaceType::SPACE_TYPE_INTERNAL));
149 ASSERT_EQ(raw_bytes_footprint + statsBeforeTest.GetRawBytesFootprintBeforeTest(),
150 stats->GetFootprint(SpaceType::SPACE_TYPE_INTERNAL));
151 }
152
TEST_F(MemStatsTest,SimpleTest)153 TEST_F(MemStatsTest, SimpleTest)
154 {
155 static constexpr size_t BYTES_OBJECT1 = 10;
156 static constexpr size_t BYTES_OBJECT2 = 12;
157 static constexpr size_t BYTES_RAW_MEMORY_ALLOC1 = 20;
158 static constexpr size_t BYTES_RAW_MEMORY_ALLOC2 = 30002;
159 static constexpr size_t RAW_MEMORY_FREED = 5;
160
161 auto *stats = thread_->GetVM()->GetMemStats();
162 size_t init_heap_bytes = stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT);
163 size_t init_heap_objects = stats->GetTotalObjectsAllocated();
164 RawStatsBeforeTest raw_stats_before_test(stats);
165 stats->RecordAllocateObject(BYTES_OBJECT1, SpaceType::SPACE_TYPE_OBJECT);
166 stats->RecordAllocateObject(BYTES_OBJECT2, SpaceType::SPACE_TYPE_OBJECT);
167 stats->RecordAllocateRaw(BYTES_RAW_MEMORY_ALLOC1, SpaceType::SPACE_TYPE_INTERNAL);
168 stats->RecordAllocateRaw(BYTES_RAW_MEMORY_ALLOC2, SpaceType::SPACE_TYPE_INTERNAL);
169 stats->RecordFreeRaw(RAW_MEMORY_FREED, SpaceType::SPACE_TYPE_INTERNAL);
170
171 AssertHeapStats(stats, init_heap_bytes + BYTES_OBJECT1 + BYTES_OBJECT2,
172 init_heap_bytes + BYTES_OBJECT1 + BYTES_OBJECT2, 0);
173 AssertHeapObjectsStats(stats, init_heap_objects + 2, 0, 0, 0);
174 ASSERT_EQ(init_heap_bytes + BYTES_OBJECT1 + BYTES_OBJECT2, stats->GetFootprint(SpaceType::SPACE_TYPE_OBJECT));
175 AssertRawStats(stats, BYTES_RAW_MEMORY_ALLOC1 + BYTES_RAW_MEMORY_ALLOC2, RAW_MEMORY_FREED,
176 BYTES_RAW_MEMORY_ALLOC1 + BYTES_RAW_MEMORY_ALLOC2 - RAW_MEMORY_FREED, raw_stats_before_test);
177 stats->RecordFreeRaw(BYTES_RAW_MEMORY_ALLOC1 + BYTES_RAW_MEMORY_ALLOC2 - RAW_MEMORY_FREED,
178 SpaceType::SPACE_TYPE_INTERNAL);
179 }
180
181 // testing MemStats via allocators.
TEST_F(MemStatsTest,NonObjectTestViaMallocAllocator)182 TEST_F(MemStatsTest, NonObjectTestViaMallocAllocator)
183 {
184 static constexpr size_t BYTES_ALLOC1 = 23;
185 static constexpr size_t BYTES_ALLOC2 = 42;
186
187 mem::MemStatsType *stats = thread_->GetVM()->GetMemStats();
188 RawStatsBeforeTest raw_stats_before_test(stats);
189 size_t init_heap_bytes = stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT);
190 size_t init_heap_objects = stats->GetTotalObjectsAllocated();
191 MallocProxyNonObjectAllocator allocator(stats, SpaceType::SPACE_TYPE_INTERNAL);
192
193 void *a1 = allocator.Alloc(BYTES_ALLOC1);
194 allocator.Free(a1);
195 void *a2 = allocator.Alloc(BYTES_ALLOC2);
196
197 AssertHeapStats(stats, init_heap_bytes, init_heap_bytes, 0);
198 AssertHeapObjectsStats(stats, init_heap_objects, 0, 0, 0);
199 AssertRawStats(stats, BYTES_ALLOC1 + BYTES_ALLOC2, BYTES_ALLOC1, BYTES_ALLOC2, raw_stats_before_test);
200 allocator.Free(a2);
201 }
202
203 // testing MemStats via allocators.
TEST_F(MemStatsTest,NonObjectTestViaSlotsAllocator)204 TEST_F(MemStatsTest, NonObjectTestViaSlotsAllocator)
205 {
206 static constexpr uint64_t poolSize = SIZE_1M * 4;
207 static constexpr size_t REAL_BYTES_ALLOC1 = 23;
208 // RunSlotsAllocator uses 32 bytes for allocation 23 bytes
209 static constexpr size_t BYTES_IN_ALLOCATOR_ALLOC1 = 32;
210 static constexpr size_t REAL_BYTES_ALLOC2 = 42;
211 static constexpr size_t BYTES_IN_ALLOCATOR_ALLOC2 = 64;
212
213 mem::MemStatsType *stats = thread_->GetVM()->GetMemStats();
214 size_t init_heap_bytes = stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT);
215 size_t init_heap_objects = stats->GetTotalObjectsAllocated();
216 RawStatsBeforeTest raw_stats_before_test(stats);
217
218 auto *allocator = new NonObjectAllocator(stats, SpaceType::SPACE_TYPE_INTERNAL);
219 void *mem = aligned_alloc(RUNSLOTS_ALIGNMENT_IN_BYTES, poolSize);
220 allocator->AddMemoryPool(mem, poolSize);
221
222 void *a1 = allocator->Alloc(REAL_BYTES_ALLOC1);
223 allocator->Free(a1);
224 void *a2 = allocator->Alloc(REAL_BYTES_ALLOC2);
225
226 AssertHeapStats(stats, init_heap_bytes, init_heap_bytes, 0);
227 AssertHeapObjectsStats(stats, init_heap_objects, 0, 0, 0);
228 AssertRawStats(stats, BYTES_IN_ALLOCATOR_ALLOC1 + BYTES_IN_ALLOCATOR_ALLOC2, BYTES_IN_ALLOCATOR_ALLOC1,
229 BYTES_IN_ALLOCATOR_ALLOC2, raw_stats_before_test);
230 allocator->Free(a2);
231 delete allocator;
232 std::free(mem);
233 }
234
235 // allocate and free small object in the heap
TEST_F(MemStatsTest,SmallObject)236 TEST_F(MemStatsTest, SmallObject)
237 {
238 mem::MemStatsType *stats = thread_->GetVM()->GetMemStats();
239 size_t init_heap_bytes = stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT);
240 size_t init_heap_objects = stats->GetTotalObjectsAllocated();
241 RawStatsBeforeTest raw_stats_before_test(stats);
242 std::string simple_string = "abcdef12345";
243 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
244 [[maybe_unused]] coretypes::String *string_object =
245 coretypes::String::CreateFromMUtf8(reinterpret_cast<const uint8_t *>(&simple_string[0]), simple_string.length(),
246 ctx, Runtime::GetCurrent()->GetPandaVM());
247 ASSERT_TRUE(string_object != nullptr);
248 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
249 size_t alloc_size = simple_string.size() + sizeof(coretypes::String);
250 size_t aligment_size = 1UL << RunSlots<>::ConvertToPowerOfTwoUnsafe(alloc_size);
251 AssertHeapStats(stats, init_heap_bytes, init_heap_bytes + aligment_size, aligment_size);
252 AssertHeapObjectsStats(stats, init_heap_objects + 1, 1, 0, 0);
253 ASSERT_EQ(raw_stats_before_test.GetRawBytesFootprintBeforeTest(),
254 stats->GetFootprint(SpaceType::SPACE_TYPE_INTERNAL));
255 }
256
257 // allocate and free big object in the heap
TEST_F(MemStatsTest,BigObject)258 TEST_F(MemStatsTest, BigObject)
259 {
260 mem::MemStatsType *stats = thread_->GetVM()->GetMemStats();
261 RawStatsBeforeTest raw_stats_before_test(stats);
262 size_t init_heap_bytes = stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT);
263 size_t init_heap_objects = stats->GetTotalObjectsAllocated();
264 std::string simple_string;
265 auto object_allocator = thread_->GetVM()->GetHeapManager()->GetObjectAllocator().AsObjectAllocator();
266 size_t alloc_size = object_allocator->GetRegularObjectMaxSize() + 1;
267 for (size_t j = 0; j < alloc_size; j++) {
268 simple_string.append("x");
269 }
270 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
271 [[maybe_unused]] coretypes::String *string_object =
272 coretypes::String::CreateFromMUtf8(reinterpret_cast<const uint8_t *>(&simple_string[0]), simple_string.length(),
273 ctx, Runtime::GetCurrent()->GetPandaVM());
274 ASSERT_TRUE(string_object != nullptr);
275 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
276 alloc_size += sizeof(coretypes::String);
277 size_t aligment_size = AlignUp(alloc_size, GetAlignmentInBytes(FREELIST_DEFAULT_ALIGNMENT));
278
279 AssertHeapStats(stats, init_heap_bytes, init_heap_bytes + aligment_size, aligment_size);
280 AssertHeapObjectsStats(stats, init_heap_objects + 1, 1, 0, 0);
281 ASSERT_EQ(raw_stats_before_test.GetRawBytesFootprintBeforeTest(),
282 stats->GetFootprint(SpaceType::SPACE_TYPE_INTERNAL));
283 }
284
285 // allocate and free humongous object in the heap
TEST_F(MemStatsTest,HumongousObject)286 TEST_F(MemStatsTest, HumongousObject)
287 {
288 mem::MemStatsType *stats = thread_->GetVM()->GetMemStats();
289 RawStatsBeforeTest raw_stats_before_test(stats);
290 size_t init_heap_bytes = stats->GetAllocated(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT);
291 size_t init_heap_objects = stats->GetTotalObjectsAllocated();
292 std::string simple_string;
293 auto object_allocator = thread_->GetVM()->GetHeapManager()->GetObjectAllocator().AsObjectAllocator();
294 size_t alloc_size = object_allocator->GetLargeObjectMaxSize() + 1;
295 for (size_t j = 0; j < alloc_size; j++) {
296 simple_string.append("x");
297 }
298 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
299 [[maybe_unused]] coretypes::String *string_object =
300 coretypes::String::CreateFromMUtf8(reinterpret_cast<const uint8_t *>(&simple_string[0]), simple_string.length(),
301 ctx, Runtime::GetCurrent()->GetPandaVM());
302 ASSERT_TRUE(string_object != nullptr);
303 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
304 AssertHeapHumongousStats(stats, init_heap_bytes, init_heap_bytes + 2359296, 2359296);
305 AssertHeapObjectsStats(stats, init_heap_objects, 0, 1, 1);
306 ASSERT_EQ(raw_stats_before_test.GetRawBytesFootprintBeforeTest(),
307 stats->GetFootprint(SpaceType::SPACE_TYPE_INTERNAL));
308
309 ASSERT_EQ(2359296, stats->GetAllocated(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT));
310 ASSERT_EQ(2359296, stats->GetFreed(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT));
311 ASSERT_EQ(0, stats->GetFootprint(SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT));
312 }
313
TEST_F(MemStatsTest,TotalFootprint)314 TEST_F(MemStatsTest, TotalFootprint)
315 {
316 static constexpr size_t BYTES_ALLOC1 = 2;
317 static constexpr size_t BYTES_ALLOC2 = 5;
318 static constexpr size_t RAW_ALLOC1 = 15;
319 static constexpr size_t RAW_ALLOC2 = 30;
320
321 MemStatsDefault stats;
322 stats.RecordAllocateObject(BYTES_ALLOC1, SpaceType::SPACE_TYPE_OBJECT);
323 stats.RecordAllocateObject(BYTES_ALLOC2, SpaceType::SPACE_TYPE_OBJECT);
324 stats.RecordAllocateRaw(RAW_ALLOC1, SpaceType::SPACE_TYPE_INTERNAL);
325 stats.RecordAllocateRaw(RAW_ALLOC2, SpaceType::SPACE_TYPE_INTERNAL);
326
327 ASSERT_EQ(BYTES_ALLOC1 + BYTES_ALLOC2, stats.GetFootprint(SpaceType::SPACE_TYPE_OBJECT));
328 ASSERT_EQ(BYTES_ALLOC1 + BYTES_ALLOC2 + RAW_ALLOC1 + RAW_ALLOC2, stats.GetTotalFootprint());
329 ASSERT_EQ(RAW_ALLOC1 + RAW_ALLOC2, stats.GetFootprint(SpaceType::SPACE_TYPE_INTERNAL));
330
331 stats.RecordFreeRaw(RAW_ALLOC1, SpaceType::SPACE_TYPE_INTERNAL);
332
333 ASSERT_EQ(BYTES_ALLOC1 + BYTES_ALLOC2, stats.GetFootprint(SpaceType::SPACE_TYPE_OBJECT));
334 ASSERT_EQ(BYTES_ALLOC1 + BYTES_ALLOC2 + RAW_ALLOC2, stats.GetTotalFootprint());
335 ASSERT_EQ(RAW_ALLOC2, stats.GetFootprint(SpaceType::SPACE_TYPE_INTERNAL));
336 }
337
TEST_F(MemStatsTest,Statistics)338 TEST_F(MemStatsTest, Statistics)
339 {
340 static constexpr size_t BYTES_OBJECT = 10;
341 static constexpr size_t BYTES_ALLOC1 = 23;
342 static constexpr size_t BYTES_ALLOC2 = 42;
343
344 MemStatsDefault stats;
345 stats.RecordAllocateObject(BYTES_OBJECT, SpaceType::SPACE_TYPE_OBJECT);
346 stats.RecordAllocateRaw(BYTES_ALLOC1, SpaceType::SPACE_TYPE_INTERNAL);
347 stats.RecordAllocateRaw(BYTES_ALLOC2, SpaceType::SPACE_TYPE_INTERNAL);
348
349 auto statistics = stats.GetStatistics(thread_->GetVM()->GetHeapManager());
350 ASSERT_TRUE(statistics.find(std::to_string(BYTES_OBJECT)) != std::string::npos);
351 ASSERT_TRUE(statistics.find(std::to_string(BYTES_ALLOC1 + BYTES_ALLOC2)) != std::string::npos);
352 stats.RecordFreeRaw(BYTES_ALLOC1 + BYTES_ALLOC2, SpaceType::SPACE_TYPE_INTERNAL);
353 }
354
FillMemStatsForConcurrency(MemStatsDefault & stats,std::condition_variable & ready_to_start,std::mutex & cv_mutex,std::atomic_size_t & threads_ready,coretypes::String * string_object)355 void FillMemStatsForConcurrency(MemStatsDefault &stats, std::condition_variable &ready_to_start, std::mutex &cv_mutex,
356 std::atomic_size_t &threads_ready, coretypes::String *string_object)
357 {
358 {
359 std::unique_lock<std::mutex> lock_for_ready_to_start(cv_mutex);
360 threads_ready++;
361 if (threads_ready.load() == NUM_THREADS) {
362 // Unlock all threads
363 ready_to_start.notify_all();
364 } else {
365 ready_to_start.wait(lock_for_ready_to_start,
366 [&threads_ready] { return threads_ready.load() == NUM_THREADS; });
367 }
368 }
369 for (size_t i = 1; i <= ITERATION; i++) {
370 for (size_t index = 0; index < SPACE_TYPE_SIZE; index++) {
371 SpaceType type = ToSpaceType(index);
372 if (IsHeapSpace(type)) {
373 stats.RecordAllocateObject(string_object->ObjectSize(), type);
374 } else {
375 stats.RecordAllocateRaw(i * (index + 1), type);
376 }
377 }
378 }
379
380 for (size_t index = 0; index < SPACE_TYPE_SIZE; index++) {
381 SpaceType type = ToSpaceType(index);
382 if (IsHeapSpace(type)) {
383 stats.RecordFreeObject(string_object->ObjectSize(), type);
384 } else {
385 stats.RecordFreeRaw(ITERATION * (index + 1), type);
386 }
387 }
388 }
389
TEST_F(MemStatsTest,TestThreadSafety)390 TEST_F(MemStatsTest, TestThreadSafety)
391 {
392 std::string simple_string = "smallData";
393 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
394 coretypes::String *string_object =
395 coretypes::String::CreateFromMUtf8(reinterpret_cast<const uint8_t *>(&simple_string[0]), simple_string.length(),
396 ctx, Runtime::GetCurrent()->GetPandaVM());
397
398 MemStatsDefault stats;
399
400 std::array<std::thread, NUM_THREADS> threads;
401
402 std::atomic_size_t threads_ready = 0;
403 std::mutex cv_mutex;
404 std::condition_variable ready_to_start;
405 for (size_t i = 0; i < NUM_THREADS; i++) {
406 threads[i] = std::thread(FillMemStatsForConcurrency, std::ref(stats), std::ref(ready_to_start),
407 std::ref(cv_mutex), std::ref(threads_ready), string_object);
408 }
409
410 for (size_t i = 0; i < NUM_THREADS; i++) {
411 threads[i].join();
412 }
413
414 constexpr uint64_t SUM = (ITERATION + 1) * ITERATION / 2U;
415 constexpr uint64_t TOTAL_ITERATION_COUNT = NUM_THREADS * ITERATION;
416
417 for (size_t index = 0; index < SPACE_TYPE_SIZE; index++) {
418 SpaceType type = ToSpaceType(index);
419 if (IsHeapSpace(type)) {
420 ASSERT_EQ(stats.GetAllocated(type), TOTAL_ITERATION_COUNT * string_object->ObjectSize());
421 ASSERT_EQ(stats.GetFreed(type), NUM_THREADS * string_object->ObjectSize());
422 ASSERT_EQ(stats.GetFootprint(type), (TOTAL_ITERATION_COUNT - NUM_THREADS) * string_object->ObjectSize());
423 } else {
424 ASSERT_EQ(stats.GetAllocated(type), SUM * NUM_THREADS * (index + 1));
425 ASSERT_EQ(stats.GetFreed(type), TOTAL_ITERATION_COUNT * (index + 1));
426 ASSERT_EQ(stats.GetFootprint(type), (SUM - ITERATION) * NUM_THREADS * (index + 1));
427 }
428 }
429 }
430
431 // test correct pauses measurment
TEST_F(MemStatsTest,GCPauseTest)432 TEST_F(MemStatsTest, GCPauseTest)
433 {
434 // pauses in milliseconds
435 constexpr uint64_t PAUSES[] = {10, 20, 30, 5, 40, 15, 50, 20, 10, 30};
436 constexpr uint64_t MIN_PAUSE = 5;
437 constexpr uint64_t MAX_PAUSE = 50;
438 constexpr uint64_t TOTAL_PAUSE = 230;
439 constexpr uint PAUSES_COUNT = 10;
440 constexpr uint64_t AVG_PAUSE = TOTAL_PAUSE / PAUSES_COUNT;
441
442 MemStatsDefault stats;
443 for (uint i = 0; i < PAUSES_COUNT; i++) {
444 stats.RecordGCPauseStart();
445 std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<long int>(PAUSES[i])));
446 stats.RecordGCPauseEnd();
447 }
448
449 ASSERT_LE(MIN_PAUSE, stats.GetMinGCPause());
450 ASSERT_LE(MAX_PAUSE, stats.GetMaxGCPause());
451 ASSERT_LE(AVG_PAUSE, stats.GetAverageGCPause());
452 ASSERT_LE(TOTAL_PAUSE, stats.GetTotalGCPause());
453
454 ASSERT_LE(stats.GetMinGCPause(), stats.GetAverageGCPause());
455 ASSERT_LE(stats.GetAverageGCPause(), stats.GetMaxGCPause());
456 ASSERT_LE(stats.GetMaxGCPause(), stats.GetTotalGCPause());
457
458 // test empty case
459 MemStatsDefault stats_empty;
460 ASSERT_EQ(0, stats_empty.GetMaxGCPause());
461 ASSERT_EQ(0, stats_empty.GetMinGCPause());
462 ASSERT_EQ(0, stats_empty.GetAverageGCPause());
463 ASSERT_EQ(0, stats_empty.GetTotalGCPause());
464 }
465
466 } // namespace panda::mem::test
467