• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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