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