1 /*
2 * Copyright (c) 2022-2023 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 "ecmascript/dfx/hprof/heap_profiler_interface.h"
17 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
18 #include "ecmascript/dfx/vmstat/runtime_stat.h"
19 #include "ecmascript/mem/heap-inl.h"
20 #include "ecmascript/mem/concurrent_marker.h"
21 #include "ecmascript/mem/concurrent_sweeper.h"
22 #include "ecmascript/napi/include/dfx_jsnapi.h"
23 #include "ecmascript/tests/test_helper.h"
24
25 using namespace panda;
26 using namespace panda::ecmascript;
27
28 namespace panda::test {
29 class DFXJSNApiTests : public testing::Test {
30 public:
SetUpTestCase()31 static void SetUpTestCase()
32 {
33 GTEST_LOG_(INFO) << "SetUpTestCase";
34 }
35
TearDownTestCase()36 static void TearDownTestCase()
37 {
38 GTEST_LOG_(INFO) << "TearDownCase";
39 }
40
SetUp()41 void SetUp() override
42 {
43 TestHelper::CreateEcmaVMWithScope(vm_, thread_, scope_);
44 vm_->GetJSThread()->GetCurrentEcmaContext()->SetRuntimeStatEnable(true);
45 vm_->SetEnableForceGC(false);
46 }
47
TearDown()48 void TearDown() override
49 {
50 TestHelper::DestroyEcmaVMWithScope(vm_, scope_);
51 }
52
53 protected:
54 EcmaVM *vm_ {nullptr};
55 JSThread *thread_ = {nullptr};
56 EcmaHandleScope *scope_ {nullptr};
57 };
58
MatchJSONLineHeader(std::fstream & fs,const std::string filePath,int lineNum,CString lineContent)59 bool MatchJSONLineHeader(std::fstream &fs, const std::string filePath, int lineNum, CString lineContent)
60 {
61 CString tempLineContent = "";
62 int lineCount = 1;
63 fs.open(filePath.c_str(), std::ios::in);
64 while (getline(fs, tempLineContent)) {
65 if (lineNum == lineCount && tempLineContent.find(lineContent) != CString::npos) {
66 fs.close();
67 fs.clear();
68 return true;
69 }
70 lineCount++;
71 }
72 fs.close();
73 fs.clear();
74 return false;
75 }
76
HWTEST_F_L0(DFXJSNApiTests,DumpHeapSnapshot_001)77 HWTEST_F_L0(DFXJSNApiTests, DumpHeapSnapshot_001)
78 {
79 const int dumpFormat = static_cast<int>(ecmascript::DumpFormat::JSON);
80 const std::string filePath = "DFXJSNApiTests_json_001.heapsnapshot";
81 std::fstream outputString(filePath, std::ios::out);
82 outputString.close();
83 outputString.clear();
84
85 bool isVmMode = true;
86 bool isPrivate = false;
87 bool captureNumericValue = false;
88 std::fstream inputFile {};
89 EXPECT_TRUE(inputFile.good());
90
91 DFXJSNApi::DumpHeapSnapshot(vm_, dumpFormat, filePath, isVmMode, isPrivate, captureNumericValue);
92 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 1, "{\"snapshot\":"));
93 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 2, "{\"meta\":"));
94 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 3, "{\"node_fields\":"));
95 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 4, "\"node_types\":"));
96 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 5, "\"edge_fields\":"));
97 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 6, "\"edge_types\":"));
98 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 7, "\"trace_function_info_fields\":"));
99 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 8, "\"trace_node_fields\":"));
100 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 9, "\"sample_fields\":"));
101 EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 10, "\"location_fields\":"));
102 std::remove(filePath.c_str());
103 }
104
HWTEST_F_L0(DFXJSNApiTests,DumpHeapSnapshot_002)105 HWTEST_F_L0(DFXJSNApiTests, DumpHeapSnapshot_002)
106 {
107 const int dumpFormat = static_cast<int>(ecmascript::DumpFormat::JSON);
108 const std::string filePath = "DFXJSNApiTests_json_002.heapsnapshot";
109 std::fstream outputString(filePath, std::ios::out);
110 outputString.close();
111 outputString.clear();
112
113 ecmascript::FileStream stream(filePath);
114 EXPECT_TRUE(stream.Good());
115
116 ecmascript::Progress *progress = nullptr;
117 bool isVmMode = true;
118 bool isPrivate = false;
119 bool captureNumericValue = false;
120 std::fstream fStream {};
121 EXPECT_TRUE(fStream.good());
122
123 DFXJSNApi::DumpHeapSnapshot(vm_, dumpFormat, &stream, progress, isVmMode, isPrivate, captureNumericValue);
124 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 1, "{\"snapshot\":"));
125 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 2, "{\"meta\":"));
126 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 3, "{\"node_fields\":"));
127 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 4, "\"node_types\":"));
128 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 5, "\"edge_fields\":"));
129 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 6, "\"edge_types\":"));
130 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 7, "\"trace_function_info_fields\":"));
131 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 8, "\"trace_node_fields\":"));
132 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 9, "\"sample_fields\":"));
133 EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 10, "\"location_fields\":"));
134 std::remove(filePath.c_str());
135 }
136
HWTEST_F_L0(DFXJSNApiTests,BuildNativeAndJsStackTrace)137 HWTEST_F_L0(DFXJSNApiTests, BuildNativeAndJsStackTrace)
138 {
139 bool result = false;
140 std::string stackTraceStr = "stack_trace_str";
141 result = DFXJSNApi::BuildNativeAndJsStackTrace(vm_, stackTraceStr);
142 #if defined(ENABLE_EXCEPTION_BACKTRACE)
143 EXPECT_FALSE(stackTraceStr.empty());
144 EXPECT_TRUE(result);
145 #else
146 EXPECT_TRUE(stackTraceStr.empty());
147 EXPECT_FALSE(result);
148 #endif
149 }
150
HWTEST_F_L0(DFXJSNApiTests,BuildJsStackTrace)151 HWTEST_F_L0(DFXJSNApiTests, BuildJsStackTrace)
152 {
153 std::string stackTraceStr = "stack_trace_str";
154 bool result = DFXJSNApi::BuildJsStackTrace(vm_, stackTraceStr);
155 #if defined(ENABLE_EXCEPTION_BACKTRACE)
156 EXPECT_FALSE(stackTraceStr.empty());
157 EXPECT_TRUE(result);
158 #else
159 EXPECT_TRUE(stackTraceStr.empty());
160 EXPECT_FALSE(result);
161 #endif
162 }
163
HWTEST_F_L0(DFXJSNApiTests,Start_Stop_HeapTracking_001)164 HWTEST_F_L0(DFXJSNApiTests, Start_Stop_HeapTracking_001)
165 {
166 [[maybe_unused]] EcmaHandleScope handleScope(thread_);
167 vm_->SetEnableForceGC(false);
168
169 auto factory = vm_->GetFactory();
170 bool isVmMode = true;
171 bool traceAllocation = false;
172 double timeInterval = 50; // 50 : time interval 50 ms
173 ecmascript::FileStream *stream = nullptr;
174 bool startResult = false;
175 startResult = DFXJSNApi::StartHeapTracking(vm_, timeInterval, isVmMode, stream, traceAllocation);
176 EXPECT_TRUE(startResult);
177
178 sleep(1);
179 int count = 300;
180 while (count-- > 0) {
181 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
182 JSHandle<EcmaString> string = factory->NewFromASCII("Start_Stop_HeapTracking_001_TestString");
183 factory->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
184 }
185 const std::string filePath = "Start_Stop_HeapTracking_001.heaptimeline";
186 std::fstream outputString(filePath, std::ios::out);
187 outputString.close();
188 outputString.clear();
189
190 bool stopResult = DFXJSNApi::StopHeapTracking(vm_, filePath);
191 EXPECT_TRUE(stopResult);
192
193 std::fstream inputStream(filePath, std::ios::in);
194 std::string line;
195 std::string emptySample = "\"samples\":";
196 std::string firstSample = "\"samples\":[0, ";
197 bool isFind = false;
198 while (getline(inputStream, line)) {
199 if (line.substr(0U, emptySample.size()) == emptySample) {
200 EXPECT_TRUE(line.substr(0, firstSample.size()) == firstSample);
201 isFind = true;
202 }
203 }
204 EXPECT_TRUE(isFind);
205
206 inputStream.close();
207 inputStream.clear();
208 std::remove(filePath.c_str());
209 vm_->SetEnableForceGC(true);
210 }
211
HWTEST_F_L0(DFXJSNApiTests,Start_Stop_HeapTracking_002)212 HWTEST_F_L0(DFXJSNApiTests, Start_Stop_HeapTracking_002)
213 {
214 [[maybe_unused]] EcmaHandleScope handleScope(thread_);
215 vm_->SetEnableForceGC(false);
216
217 auto factory = vm_->GetFactory();
218 bool isVmMode = true;
219 bool traceAllocation = false;
220 double timeInterval = 50; // 50 : time interval 50 ms
221 ecmascript::FileStream *stream = nullptr;
222 bool startResult = false;
223 startResult = DFXJSNApi::StartHeapTracking(vm_, timeInterval, isVmMode, stream, traceAllocation);
224 EXPECT_TRUE(startResult);
225
226 sleep(1);
227 int count = 300;
228 while (count-- > 0) {
229 factory->NewJSAsyncFuncObject();
230 factory->NewJSSymbol();
231 }
232 const std::string filePath = "Start_Stop_HeapTracking_002.heaptimeline";
233 std::fstream outputString(filePath, std::ios::out);
234 outputString.close();
235 outputString.clear();
236
237 ecmascript::FileStream fileStream(filePath);
238 bool stopResult = DFXJSNApi::StopHeapTracking(vm_, &fileStream);
239 EXPECT_TRUE(stopResult);
240
241 std::fstream inputStream(filePath, std::ios::in);
242 std::string line;
243 std::string emptySample = "\"samples\":";
244 std::string firstSample = "\"samples\":[0, ";
245 bool isFind = false;
246 while (getline(inputStream, line)) {
247 if (line.substr(0U, emptySample.size()) == emptySample) {
248 EXPECT_TRUE(line.substr(0, firstSample.size()) == firstSample);
249 isFind = true;
250 }
251 }
252 EXPECT_TRUE(isFind);
253
254 inputStream.close();
255 inputStream.clear();
256 std::remove(filePath.c_str());
257 vm_->SetEnableForceGC(true);
258 }
259
HWTEST_F_L0(DFXJSNApiTests,Start_Stop_RuntimeStat)260 HWTEST_F_L0(DFXJSNApiTests, Start_Stop_RuntimeStat)
261 {
262 EcmaRuntimeStat *ecmaRuntimeStat = vm_->GetJSThread()->GetCurrentEcmaContext()->GetRuntimeStat();
263 EXPECT_TRUE(ecmaRuntimeStat != nullptr);
264
265 ecmaRuntimeStat->SetRuntimeStatEnabled(false);
266 EXPECT_TRUE(!ecmaRuntimeStat->IsRuntimeStatEnabled());
267
268 DFXJSNApi::StartRuntimeStat(vm_);
269 EXPECT_TRUE(ecmaRuntimeStat->IsRuntimeStatEnabled());
270
271 DFXJSNApi::StopRuntimeStat(vm_);
272 EXPECT_TRUE(!ecmaRuntimeStat->IsRuntimeStatEnabled());
273 }
274
HWTEST_F_L0(DFXJSNApiTests,GetArrayBufferSize_GetHeapTotalSize_GetHeapUsedSize)275 HWTEST_F_L0(DFXJSNApiTests, GetArrayBufferSize_GetHeapTotalSize_GetHeapUsedSize)
276 {
277 auto heap = vm_->GetHeap();
278 size_t arrayBufferSize = DFXJSNApi::GetArrayBufferSize(vm_);
279 size_t expectArrayBufferSize = heap->GetArrayBufferSize();
280 EXPECT_EQ(arrayBufferSize, expectArrayBufferSize);
281
282 size_t heapTotalSize = DFXJSNApi::GetHeapTotalSize(vm_);
283 size_t expectHeapTotalSize = heap->GetCommittedSize();
284 EXPECT_EQ(heapTotalSize, expectHeapTotalSize);
285
286 size_t heapUsedSize = DFXJSNApi::GetHeapUsedSize(vm_);
287 size_t expectHeapUsedSize = heap->GetLiveObjectSize();
288 EXPECT_EQ(heapUsedSize, expectHeapUsedSize);
289
290 size_t heapObjectSize = DFXJSNApi::GetHeapObjectSize(vm_);
291 size_t expectHeapObjectSize = heap->GetHeapObjectSize();
292 EXPECT_EQ(heapObjectSize, expectHeapObjectSize);
293 }
294
HWTEST_F_L0(DFXJSNApiTests,NotifyApplicationState)295 HWTEST_F_L0(DFXJSNApiTests, NotifyApplicationState)
296 {
297 auto heap = vm_->GetHeap();
298 auto concurrentMarker = heap->GetConcurrentMarker();
299 auto sweeper = heap->GetSweeper();
300
301 DFXJSNApi::NotifyApplicationState(vm_, false);
302 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
303 EXPECT_TRUE(!concurrentMarker->IsDisabled());
304 #endif
305 EXPECT_TRUE(!sweeper->IsDisabled());
306
307 const_cast<ecmascript::Heap *>(heap)->CollectGarbage(TriggerGCType::OLD_GC, GCReason::OTHER);
308 DFXJSNApi::NotifyApplicationState(vm_, true);
309 EXPECT_TRUE(concurrentMarker->IsDisabled());
310 EXPECT_TRUE(sweeper->IsRequestDisabled() || sweeper->IsDisabled());
311 }
312
HWTEST_F_L0(DFXJSNApiTests,NotifyMemoryPressure)313 HWTEST_F_L0(DFXJSNApiTests, NotifyMemoryPressure)
314 {
315 auto heap = vm_->GetHeap();
316 bool inHighMemoryPressure = true;
317 DFXJSNApi::NotifyMemoryPressure(vm_, inHighMemoryPressure);
318 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::PRESSURE);
319
320 inHighMemoryPressure = false;
321 DFXJSNApi::NotifyMemoryPressure(vm_, inHighMemoryPressure);
322 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::CONSERVATIVE);
323 }
324
HWTEST_F_L0(DFXJSNApiTests,BuildJsStackInfoList)325 HWTEST_F_L0(DFXJSNApiTests, BuildJsStackInfoList)
326 {
327 uint32_t hostTid = vm_->GetJSThread()->GetThreadId();
328 std::vector<ecmascript::JsFrameInfo> jsFrameInfo;
329 bool result = DFXJSNApi::BuildJsStackInfoList(vm_, hostTid, jsFrameInfo);
330 EXPECT_FALSE(result);
331 }
332
HWTEST_F_L0(DFXJSNApiTests,StartSampling)333 HWTEST_F_L0(DFXJSNApiTests, StartSampling)
334 {
335 uint64_t samplingInterval = 32768;
336 bool result = DFXJSNApi::StartSampling(vm_, samplingInterval);
337 EXPECT_TRUE(result);
338 result = DFXJSNApi::StartSampling(vm_, samplingInterval);
339 EXPECT_FALSE(result);
340 }
341
HWTEST_F_L0(DFXJSNApiTests,StopSampling)342 HWTEST_F_L0(DFXJSNApiTests, StopSampling)
343 {
344 uint64_t samplingInterval = 32768;
345 bool result = DFXJSNApi::StartSampling(vm_, samplingInterval);
346 EXPECT_TRUE(result);
347 DFXJSNApi::StopSampling(vm_);
348 result = DFXJSNApi::StartSampling(vm_, samplingInterval);
349 EXPECT_TRUE(result);
350 }
351
HWTEST_F_L0(DFXJSNApiTests,GetAllocationProfile)352 HWTEST_F_L0(DFXJSNApiTests, GetAllocationProfile)
353 {
354 const SamplingInfo *result = DFXJSNApi::GetAllocationProfile(vm_);
355 EXPECT_TRUE(result == nullptr);
356 uint64_t samplingInterval = 32768;
357 DFXJSNApi::StartSampling(vm_, samplingInterval);
358 result = DFXJSNApi::GetAllocationProfile(vm_);
359 EXPECT_TRUE(result != nullptr);
360 }
361 } // namespace panda::test
362