1 /**
2 * Copyright (c) 2021-2025 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 "gtest/gtest.h"
17 #include "iostream"
18 #include "runtime/class_linker_context.h"
19 #include "runtime/include/runtime.h"
20 #include "runtime/include/panda_vm.h"
21 #include "runtime/mem/vm_handle.h"
22 #include "runtime/handle_base-inl.h"
23 #include "runtime/handle_scope-inl.h"
24 #include "runtime/mem/mem_stats.h"
25 #include "runtime/mem/mem_stats_default.h"
26
27 namespace ark::mem::test {
28
29 class MemStatsGCTest : public testing::Test {
30 public:
SetupRuntime(const std::string & gcType)31 void SetupRuntime(const std::string &gcType)
32 {
33 RuntimeOptions options;
34 options.SetShouldLoadBootPandaFiles(false);
35 options.SetShouldInitializeIntrinsics(false);
36 options.SetUseTlabForAllocations(false);
37 options.SetGcType(gcType);
38 options.SetRunGcInPlace(true);
39 options.SetExplicitConcurrentGcEnabled(false);
40 bool success = Runtime::Create(options);
41 ASSERT_TRUE(success) << "Cannot create Runtime";
42 thread_ = ark::MTManagedThread::GetCurrent();
43 thread_->ManagedCodeBegin();
44 }
45
46 template <uint64_t OBJECT_COUNT>
47 void MemStatsTest(uint64_t tries, size_t objectSize);
48
TearDown()49 void TearDown() override
50 {
51 thread_->ManagedCodeEnd();
52 bool success = Runtime::Destroy();
53 ASSERT_TRUE(success) << "Cannot destroy Runtime";
54 }
55
56 private:
57 void SetAligment(size_t allocSize, size_t objectMaxSize, size_t &aligmentSize, size_t &aligmentDiff);
58 void SetAllocatedStats(uint64_t &allocatedObjects, uint64_t &allocatedBytes, size_t allocSize,
59 uint64_t objectCount);
60
61 ark::MTManagedThread *thread_ {};
62 };
63
SetAligment(size_t allocSize,size_t objectMaxSize,size_t & aligmentSize,size_t & aligmentDiff)64 void MemStatsGCTest::SetAligment(size_t allocSize, size_t objectMaxSize, size_t &aligmentSize, size_t &aligmentDiff)
65 {
66 if (allocSize < objectMaxSize) {
67 aligmentSize = 1UL << RunSlots<>::ConvertToPowerOfTwoUnsafe(allocSize);
68 aligmentDiff = aligmentSize - allocSize;
69 } else {
70 aligmentSize = AlignUp(allocSize, GetAlignmentInBytes(FREELIST_DEFAULT_ALIGNMENT));
71 aligmentDiff = 2U * (aligmentSize - allocSize);
72 }
73 }
74
SetAllocatedStats(uint64_t & allocatedObjects,uint64_t & allocatedBytes,size_t allocSize,uint64_t objectCount)75 void MemStatsGCTest::SetAllocatedStats(uint64_t &allocatedObjects, uint64_t &allocatedBytes, size_t allocSize,
76 uint64_t objectCount)
77 {
78 allocatedObjects += objectCount;
79 allocatedBytes += objectCount * allocSize;
80 }
81
82 template <uint64_t OBJECT_COUNT>
MemStatsTest(uint64_t tries,size_t objectSize)83 void MemStatsGCTest::MemStatsTest(uint64_t tries, size_t objectSize)
84 {
85 ASSERT(objectSize >= sizeof(coretypes::String));
86 mem::MemStatsType *stats = thread_->GetVM()->GetMemStats();
87 ASSERT_NE(stats, nullptr);
88
89 auto classLinker = Runtime::GetCurrent()->GetClassLinker();
90 ASSERT_NE(classLinker, nullptr);
91 auto allocator = classLinker->GetAllocator();
92
93 std::string simpleString;
94 for (size_t j = 0; j < objectSize - sizeof(coretypes::String); j++) {
95 simpleString.append("x");
96 }
97 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
98 auto objectAllocator = thread_->GetVM()->GetGC()->GetObjectAllocator();
99 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
100
101 size_t allocSize = simpleString.size() + sizeof(coretypes::String);
102 size_t aligmentSize = 0;
103 size_t aligmentDiff = 0;
104 SetAligment(allocSize, objectAllocator->GetRegularObjectMaxSize(), aligmentSize, aligmentDiff);
105
106 uint64_t allocatedObjects = stats->GetTotalObjectsAllocated();
107 uint64_t allocatedBytes = stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT);
108 uint64_t freedObjects = stats->GetTotalObjectsFreed();
109 uint64_t freedBytes = stats->GetFreed(SpaceType::SPACE_TYPE_OBJECT);
110 uint64_t diffTotal = 0;
111 std::array<VMHandle<coretypes::String> *, OBJECT_COUNT> handlers {};
112 for (size_t i = 0; i < tries; i++) {
113 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread_);
114 for (uint64_t j = 0; j < OBJECT_COUNT; j++) {
115 coretypes::String *stringObj =
116 coretypes::String::CreateFromMUtf8(reinterpret_cast<const uint8_t *>(&simpleString[0]),
117 simpleString.length(), ctx, Runtime::GetCurrent()->GetPandaVM());
118 ASSERT_NE(stringObj, nullptr);
119 handlers[j] = allocator->New<VMHandle<coretypes::String>>(thread_, stringObj);
120 }
121
122 SetAllocatedStats(allocatedObjects, allocatedBytes, allocSize, OBJECT_COUNT);
123 diffTotal += OBJECT_COUNT * aligmentDiff;
124 ASSERT_EQ(allocatedObjects, stats->GetTotalObjectsAllocated());
125 ASSERT_LE(allocatedBytes, stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT));
126 ASSERT_GE(allocatedBytes + diffTotal, stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT));
127
128 // run GC
129 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
130 ASSERT_EQ(allocatedObjects, stats->GetTotalObjectsAllocated());
131 ASSERT_LE(allocatedBytes, stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT));
132 ASSERT_GE(allocatedBytes + diffTotal, stats->GetAllocated(SpaceType::SPACE_TYPE_OBJECT));
133 ASSERT_EQ(freedObjects, stats->GetTotalObjectsFreed());
134 ASSERT_LE(freedBytes, stats->GetFreed(SpaceType::SPACE_TYPE_OBJECT));
135 ASSERT_GE(freedBytes + diffTotal, stats->GetFreed(SpaceType::SPACE_TYPE_OBJECT));
136
137 for (uint64_t j = 0; j < OBJECT_COUNT; j++) {
138 allocator->Delete(handlers[j]);
139 }
140 freedObjects += OBJECT_COUNT;
141 freedBytes += OBJECT_COUNT * allocSize;
142 }
143 }
144
145 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
146 constexpr size_t OBJECTS_SIZE[] = {
147 32, // RunSlots: aligned & object_size = RunSlot size
148 72, // RunSlots: aligned & object_size != RunSlot size
149 129, // RunSlots: not aligned
150 512, // FreeList: aligned
151 1025 // FreeList: not aligned
152 };
153
TEST_F(MemStatsGCTest,StwGcTest)154 TEST_F(MemStatsGCTest, StwGcTest)
155 {
156 constexpr uint64_t OBJECTS_COUNT = 500;
157 constexpr uint64_t TRIES = 10;
158
159 SetupRuntime("stw");
160 for (size_t objectSize : OBJECTS_SIZE) {
161 MemStatsTest<OBJECTS_COUNT>(TRIES, objectSize);
162 }
163 }
164
165 } // namespace ark::mem::test
166