• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/panda_vm.h"
21 #include "runtime/include/class_linker.h"
22 #include "runtime/include/thread_scopes.h"
23 #include "runtime/mem/vm_handle.h"
24 #include "runtime/handle_scope-inl.h"
25 #include "runtime/include/coretypes/array.h"
26 #include "runtime/include/coretypes/string.h"
27 #include "runtime/mem/gc/card_table.h"
28 #include "runtime/mem/gc/g1/g1-allocator.h"
29 #include "runtime/mem/rem_set-inl.h"
30 #include "runtime/mem/region_space.h"
31 #include "runtime/tests/test_utils.h"
32 
33 #include "runtime/mem/object_helpers.h"
34 
35 namespace panda::mem {
36 class GCTestLog : public testing::Test {
37 public:
GCTestLog()38     GCTestLog() {}
39 
~GCTestLog()40     ~GCTestLog()
41     {
42         [[maybe_unused]] bool success = Runtime::Destroy();
43         ASSERT(success);
44         Logger::Destroy();
45     }
46 
SetupRuntime(const std::string & gc_type,bool small_heap_and_young_spaces=false,size_t promotion_region_alive_rate=100) const47     void SetupRuntime(const std::string &gc_type, bool small_heap_and_young_spaces = false,
48                       size_t promotion_region_alive_rate = 100) const
49     {
50         panda::Logger::ComponentMask component_mask;
51         component_mask.set(Logger::Component::GC);
52 
53         Logger::InitializeStdLogging(Logger::Level::INFO, component_mask);
54         EXPECT_TRUE(Logger::IsLoggingOn(Logger::Level::INFO, Logger::Component::GC));
55 
56         RuntimeOptions options;
57         options.SetBootClassSpaces({"core"});
58         options.SetRuntimeType("core");
59         options.SetGcType(gc_type);
60         options.SetRunGcInPlace(true);
61         options.SetCompilerEnableJit(false);
62         options.SetGcWorkersCount(0);
63         options.SetG1PromotionRegionAliveRate(promotion_region_alive_rate);
64         options.SetGcTriggerType("debug-never");
65         options.SetShouldLoadBootPandaFiles(false);
66         options.SetShouldInitializeIntrinsics(false);
67         if (small_heap_and_young_spaces) {
68             options.SetYoungSpaceSize(2_MB);
69             options.SetHeapSizeLimit(15_MB);
70         }
71         [[maybe_unused]] bool success = Runtime::Create(options);
72         ASSERT(success);
73     }
74 
GetGCCounter(GC * gc)75     size_t GetGCCounter(GC *gc)
76     {
77         return gc->gc_counter_;
78     }
79 
CounterLogTest(const std::string & gc_type)80     void CounterLogTest(const std::string &gc_type)
81     {
82         SetupRuntime(gc_type);
83 
84         Runtime *runtime = Runtime::GetCurrent();
85         GC *gc = runtime->GetPandaVM()->GetGC();
86         GCTask task(GCTaskCause::YOUNG_GC_CAUSE);
87         const unsigned iterations = 100;
88 
89         ASSERT(GetGCCounter(gc) == 0);
90 
91         for (size_t i = 1; i < iterations; i++) {
92             testing::internal::CaptureStderr();
93             task.Run(*gc);
94             expected_log = '[' + std::to_string(GetGCCounter(gc)) + ']';
95             log = testing::internal::GetCapturedStderr();
96             ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
97             ASSERT(GetGCCounter(gc) == i);
98             task.reason_ = static_cast<GCTaskCause>(i % number_of_GC_causes == 0 ? i % number_of_GC_causes + 1
99                                                                                  : i % number_of_GC_causes);
100         }
101     }
102 
FullLogTest(const std::string & gc_type)103     void FullLogTest(const std::string &gc_type)
104     {
105         bool is_stw = gc_type == "stw";
106 
107         SetupRuntime(gc_type);
108 
109         Runtime *runtime = Runtime::GetCurrent();
110         GC *gc = runtime->GetPandaVM()->GetGC();
111         GCTask task(GCTaskCause::YOUNG_GC_CAUSE);
112 
113         testing::internal::CaptureStderr();
114         task.reason_ = GCTaskCause::YOUNG_GC_CAUSE;
115         task.Run(*gc);
116         expected_log = is_stw ? "[FULL (Young)]" : "[YOUNG (Young)]";
117         log = testing::internal::GetCapturedStderr();
118         ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
119 
120         testing::internal::CaptureStderr();
121         task.reason_ = GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE;
122         task.Run(*gc);
123         expected_log = is_stw ? "[FULL (Threshold)]" : "[TENURED (Threshold)]";
124         log = testing::internal::GetCapturedStderr();
125         ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
126 
127         testing::internal::CaptureStderr();
128         task.reason_ = GCTaskCause::OOM_CAUSE;
129         task.Run(*gc);
130         expected_log = "[FULL (OOM)]";
131         log = testing::internal::GetCapturedStderr();
132         ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
133     }
134 
135     // GCCollectionType order is important
136     static_assert(GCCollectionType::NONE < GCCollectionType::YOUNG);
137     static_assert(GCCollectionType::YOUNG < GCCollectionType::MIXED);
138     static_assert(GCCollectionType::MIXED < GCCollectionType::TENURED);
139     static_assert(GCCollectionType::TENURED < GCCollectionType::FULL);
140 
141 protected:
142     std::string expected_log;
143     std::string log;
144 
145 private:
146     const size_t number_of_GC_causes = 8;
147 };
148 
TEST_F(GCTestLog,StwFullLogTest)149 TEST_F(GCTestLog, StwFullLogTest)
150 {
151     FullLogTest("stw");
152 }
153 
TEST_F(GCTestLog,GenGCFullLogTest)154 TEST_F(GCTestLog, GenGCFullLogTest)
155 {
156     FullLogTest("gen-gc");
157 }
158 
TEST_F(GCTestLog,GenGCYoungCauseFullCollectionLogTest)159 TEST_F(GCTestLog, GenGCYoungCauseFullCollectionLogTest)
160 {
161     SetupRuntime("gen-gc", true);
162 
163     Runtime *runtime = Runtime::GetCurrent();
164     GC *gc = runtime->GetPandaVM()->GetGC();
165     MTManagedThread *thread = MTManagedThread::GetCurrent();
166     {
167         ScopedManagedCodeThread s(thread);
168         [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
169         ObjectAllocator object_allocator;
170 
171         uint32_t garbage_rate = Runtime::GetOptions().GetG1RegionGarbageRateThreshold();
172         size_t string_len = garbage_rate * DEFAULT_REGION_SIZE / 100 + sizeof(coretypes::String);
173 
174         VMHandle<coretypes::Array> arrays[3];
175         {
176             arrays[0] =
177                 VMHandle<coretypes::Array>(thread, object_allocator.AllocArray(2, ClassRoot::ARRAY_STRING, false));
178             arrays[0]->Set(0, object_allocator.AllocString(string_len));
179         }
180     }
181 
182     GCTask task(GCTaskCause::YOUNG_GC_CAUSE);
183     testing::internal::CaptureStderr();
184     task.Run(*gc);
185     expected_log = "[FULL (Young)]";
186     log = testing::internal::GetCapturedStderr();
187     ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
188 }
189 
TEST_F(GCTestLog,G1GCIdleLaunchLogTest)190 TEST_F(GCTestLog, G1GCIdleLaunchLogTest)
191 {
192     FullLogTest("g1-gc");
193 }
194 
TEST_F(GCTestLog,G1GCMixedCollectionLogTest)195 TEST_F(GCTestLog, G1GCMixedCollectionLogTest)
196 {
197     SetupRuntime("g1-gc");
198 
199     uint32_t garbage_rate = Runtime::GetOptions().GetG1RegionGarbageRateThreshold();
200     size_t big_string_len = garbage_rate * DEFAULT_REGION_SIZE / 100 + sizeof(coretypes::String);
201     size_t big_string_len1 = (garbage_rate + 1) * DEFAULT_REGION_SIZE / 100 + sizeof(coretypes::String);
202     size_t big_string_len2 = (garbage_rate + 2) * DEFAULT_REGION_SIZE / 100 + sizeof(coretypes::String);
203     size_t small_len = DEFAULT_REGION_SIZE / 2 + sizeof(coretypes::String);
204 
205     Runtime *runtime = Runtime::GetCurrent();
206     GC *gc = runtime->GetPandaVM()->GetGC();
207     MTManagedThread *thread = MTManagedThread::GetCurrent();
208     ScopedManagedCodeThread s(thread);
209     [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
210 
211     ObjectAllocator object_allocator;
212 
213     testing::internal::CaptureStderr();
214 
215     VMHandle<coretypes::Array> holder;
216     VMHandle<ObjectHeader> young;
217     holder = VMHandle<coretypes::Array>(thread, object_allocator.AllocArray(4, ClassRoot::ARRAY_STRING, false));
218     holder->Set(0, object_allocator.AllocString(big_string_len));
219     holder->Set(1, object_allocator.AllocString(big_string_len1));
220     holder->Set(2, object_allocator.AllocString(big_string_len2));
221     holder->Set(3, object_allocator.AllocString(small_len));
222 
223     {
224         ScopedNativeCodeThread sn(thread);
225         GCTask task(GCTaskCause::YOUNG_GC_CAUSE);
226         task.Run(*gc);
227     }
228     expected_log = "[YOUNG (Young)]";
229     log = testing::internal::GetCapturedStderr();
230     ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
231     testing::internal::CaptureStderr();
232 
233     VMHandle<ObjectHeader> current;
234     current = VMHandle<ObjectHeader>(thread, object_allocator.AllocArray(small_len, ClassRoot::ARRAY_U8, false));
235 
236     holder->Set(0, static_cast<ObjectHeader *>(nullptr));
237     holder->Set(1, static_cast<ObjectHeader *>(nullptr));
238     holder->Set(2, static_cast<ObjectHeader *>(nullptr));
239     holder->Set(3, static_cast<ObjectHeader *>(nullptr));
240 
241     {
242         ScopedNativeCodeThread sn(thread);
243         GCTask task1(GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE);
244         task1.Run(*gc);
245     }
246 
247     young = VMHandle<ObjectHeader>(thread, object_allocator.AllocObjectInYoung());
248     {
249         ScopedNativeCodeThread sn(thread);
250         GCTask task2(GCTaskCause::YOUNG_GC_CAUSE);
251         task2.Run(*gc);
252     }
253     expected_log = "[MIXED (Young)]";
254     log = testing::internal::GetCapturedStderr();
255     ASSERT_NE(log.find(expected_log), std::string::npos) << "Expected:\n" << expected_log << "\nLog:\n" << log;
256 }
257 
TEST_F(GCTestLog,StwGCCounterLogTest)258 TEST_F(GCTestLog, StwGCCounterLogTest)
259 {
260     CounterLogTest("stw");
261 }
262 
TEST_F(GCTestLog,GenGCCounterLogTest)263 TEST_F(GCTestLog, GenGCCounterLogTest)
264 {
265     CounterLogTest("gen-gc");
266 }
267 
TEST_F(GCTestLog,G1GCCounterLogTest)268 TEST_F(GCTestLog, G1GCCounterLogTest)
269 {
270     CounterLogTest("g1-gc");
271 }
272 }  // namespace panda::mem