1 /**
2 * Copyright (c) 2021-2024 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 <string>
18
19 #include "runtime/include/runtime.h"
20 #include "runtime/include/runtime_options.h"
21 #include "runtime/include/panda_vm.h"
22 #include "runtime/include/class_linker.h"
23 #include "runtime/include/thread_scopes.h"
24 #include "runtime/mem/vm_handle.h"
25 #include "runtime/handle_scope-inl.h"
26 #include "runtime/include/coretypes/array.h"
27 #include "runtime/include/coretypes/string.h"
28 #include "runtime/mem/gc/card_table.h"
29 #include "runtime/mem/gc/g1/g1-allocator.h"
30 #include "runtime/mem/rem_set-inl.h"
31 #include "runtime/mem/region_space.h"
32 #include "runtime/include/gc_task.h"
33 #include "runtime/mem/gc/g1/g1-gc.h"
34
35 #include "runtime/tests/test_utils.h"
36 #include "runtime/mem/object_helpers.h"
37
38 namespace ark::mem {
39
40 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,-warnings-as-errors)
41 class ExplicitGC : public testing::Test {
42 public:
43 ExplicitGC() = default;
44
~ExplicitGC()45 ~ExplicitGC() override
46 {
47 [[maybe_unused]] bool success = Runtime::Destroy();
48 ASSERT(success);
49 Logger::Destroy();
50 }
51
52 NO_COPY_SEMANTIC(ExplicitGC);
53 NO_MOVE_SEMANTIC(ExplicitGC);
54
SetupRuntime(const std::string & gcType,bool isExplicitFull) const55 void SetupRuntime(const std::string &gcType, bool isExplicitFull) const
56 {
57 ark::Logger::ComponentMask componentMask;
58 componentMask.set(Logger::Component::GC);
59
60 Logger::InitializeStdLogging(Logger::Level::INFO, componentMask);
61 EXPECT_TRUE(Logger::IsLoggingOn(Logger::Level::INFO, Logger::Component::GC));
62
63 RuntimeOptions options;
64 options.SetLoadRuntimes({"core"});
65 options.SetGcType(gcType);
66 options.SetRunGcInPlace(true);
67 options.SetCompilerEnableJit(false);
68 options.SetGcWorkersCount(0);
69 options.SetGcTriggerType("debug-never");
70 options.SetShouldLoadBootPandaFiles(false);
71 options.SetShouldInitializeIntrinsics(false);
72 options.SetExplicitConcurrentGcEnabled(isExplicitFull);
73 [[maybe_unused]] bool success = Runtime::Create(options);
74 ASSERT(success);
75 }
76 };
77
TEST_F(ExplicitGC,TestG1GCPhases)78 TEST_F(ExplicitGC, TestG1GCPhases)
79 {
80 SetupRuntime("g1-gc", true);
81
82 uint32_t garbageRate = Runtime::GetOptions().GetG1RegionGarbageRateThreshold();
83 // NOLINTNEXTLINE(readability-magic-numbers,-warnings-as-errors)
84 size_t bigLen = garbageRate * DEFAULT_REGION_SIZE / 100U + sizeof(coretypes::String);
85
86 Runtime *runtime = Runtime::GetCurrent();
87 GC *gc = runtime->GetPandaVM()->GetGC();
88 ObjectAllocator allocator;
89 MTManagedThread *thread = MTManagedThread::GetCurrent();
90 ScopedManagedCodeThread s(thread);
91 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
92
93 VMHandle<coretypes::Array> holder;
94 std::string expectedLog;
95 std::string log;
96
97 holder = VMHandle<coretypes::Array>(thread, allocator.AllocArray(2U, ClassRoot::ARRAY_STRING, false));
98 holder->Set(0, allocator.AllocString(bigLen));
99 holder->Set(1, allocator.AllocString(bigLen));
100
101 {
102 ScopedNativeCodeThread sn(thread);
103 testing::internal::CaptureStderr();
104 GCTask task(GCTaskCause::EXPLICIT_CAUSE);
105 task.Run(*gc); // run young
106 expectedLog = "[YOUNG (Explicit)]";
107 log = testing::internal::GetCapturedStderr();
108 ASSERT_NE(log.find(expectedLog), std::string::npos) << "Expected:\n" << expectedLog << "\nLog:\n" << log;
109 }
110
111 ASSERT_TRUE(ObjectToRegion(holder->Get<ObjectHeader *>(1))->HasFlag(RegionFlag::IS_OLD));
112 ASSERT_TRUE(ObjectToRegion(holder->Get<ObjectHeader *>(0))->HasFlag(RegionFlag::IS_OLD));
113
114 holder->Set(0, static_cast<ObjectHeader *>(nullptr));
115 holder->Set(1, static_cast<ObjectHeader *>(nullptr));
116
117 {
118 ScopedNativeCodeThread sn(thread);
119 testing::internal::CaptureStderr();
120 GCTask task(GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE);
121 task.Run(*gc); // prepare for mix
122 expectedLog = "[TENURED (Threshold)]";
123 log = testing::internal::GetCapturedStderr();
124 ASSERT_NE(log.find(expectedLog), std::string::npos) << "Expected:\n" << expectedLog << "\nLog:\n" << log;
125 }
126
127 {
128 ScopedNativeCodeThread sn(thread);
129 testing::internal::CaptureStderr();
130 GCTask task(GCTaskCause::EXPLICIT_CAUSE);
131 task.Run(*gc); // run mixed gc
132 expectedLog = "[MIXED (Explicit)]";
133 log = testing::internal::GetCapturedStderr();
134 ASSERT_NE(log.find(expectedLog), std::string::npos) << "Expected:\n" << expectedLog << "\nLog:\n" << log;
135 }
136 }
137
TEST_F(ExplicitGC,TestGenGCPhases)138 TEST_F(ExplicitGC, TestGenGCPhases)
139 {
140 SetupRuntime("gen-gc", true);
141
142 Runtime *runtime = Runtime::GetCurrent();
143 GC *gc = runtime->GetPandaVM()->GetGC();
144 MTManagedThread *thread = MTManagedThread::GetCurrent();
145 ScopedManagedCodeThread s(thread);
146 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
147 ObjectAllocator objectAllocator;
148
149 std::string expectedLog;
150 std::string log;
151
152 VMHandle<ObjectHeader> obj;
153 obj = VMHandle<ObjectHeader>(thread, objectAllocator.AllocObjectInYoung());
154
155 {
156 ScopedNativeCodeThread sn(thread);
157 testing::internal::CaptureStderr();
158 GCTask task(GCTaskCause::EXPLICIT_CAUSE);
159 task.Run(*gc); // run young
160 expectedLog = "[YOUNG (Explicit)]";
161 log = testing::internal::GetCapturedStderr();
162 ASSERT_NE(log.find(expectedLog), std::string::npos) << "Expected:\n" << expectedLog << "\nLog:\n" << log;
163 }
164 }
165
TEST_F(ExplicitGC,TestG1GCWithFullExplicit)166 TEST_F(ExplicitGC, TestG1GCWithFullExplicit)
167 {
168 SetupRuntime("g1-gc", false);
169
170 Runtime *runtime = Runtime::GetCurrent();
171 GC *gc = runtime->GetPandaVM()->GetGC();
172 MTManagedThread *thread = MTManagedThread::GetCurrent();
173 ScopedManagedCodeThread s(thread);
174 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
175 ObjectAllocator objectAllocator;
176
177 std::string expectedLog;
178 std::string log;
179
180 VMHandle<ObjectHeader> obj;
181 obj = VMHandle<ObjectHeader>(thread, objectAllocator.AllocObjectInYoung());
182
183 {
184 ScopedNativeCodeThread sn(thread);
185 testing::internal::CaptureStderr();
186 GCTask task(GCTaskCause::EXPLICIT_CAUSE);
187 task.Run(*gc); // run full
188 expectedLog = "[FULL (Explicit)]";
189 log = testing::internal::GetCapturedStderr();
190 ASSERT_NE(log.find(expectedLog), std::string::npos) << "Expected:\n" << expectedLog << "\nLog:\n" << log;
191 }
192 }
193
TEST_F(ExplicitGC,TestGenGCWithFullExplicit)194 TEST_F(ExplicitGC, TestGenGCWithFullExplicit)
195 {
196 SetupRuntime("gen-gc", false);
197
198 Runtime *runtime = Runtime::GetCurrent();
199 GC *gc = runtime->GetPandaVM()->GetGC();
200 MTManagedThread *thread = MTManagedThread::GetCurrent();
201 ScopedManagedCodeThread s(thread);
202 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
203 ObjectAllocator objectAllocator;
204
205 std::string expectedLog;
206 std::string log;
207
208 VMHandle<ObjectHeader> obj;
209 obj = VMHandle<ObjectHeader>(thread, objectAllocator.AllocObjectInYoung());
210
211 {
212 ScopedNativeCodeThread sn(thread);
213 testing::internal::CaptureStderr();
214 GCTask task(GCTaskCause::EXPLICIT_CAUSE);
215 task.Run(*gc); // run full
216 expectedLog = "[FULL (Explicit)]";
217 log = testing::internal::GetCapturedStderr();
218 ASSERT_NE(log.find(expectedLog), std::string::npos) << "Expected:\n" << expectedLog << "\nLog:\n" << log;
219 }
220 }
221
222 } // namespace ark::mem
223