1 /*
2 * Copyright (c) 2021 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 <cstdio>
17 #include <fstream>
18 #include <fcntl.h>
19
20 #include "ecmascript/dfx/hprof/heap_profiler_interface.h"
21 #include "ecmascript/dfx/hprof/heap_profiler.h"
22 #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
23 #include "ecmascript/dfx/hprof/heap_snapshot.h"
24 #include "ecmascript/ecma_string.h"
25 #include "ecmascript/global_env.h"
26
27 #include "ecmascript/js_tagged_value.h"
28 #include "ecmascript/js_thread.h"
29 #include "ecmascript/mem/heap.h"
30 #include "ecmascript/tests/test_helper.h"
31 #include "ecmascript/dfx/hprof/file_stream.h"
32
33 using namespace panda::ecmascript;
34
35 namespace panda::ecmascript {
36 class TestProgress : public Progress {
37 public:
38 TestProgress() = default;
39 ~TestProgress() = default;
40
41 void ReportProgress([[maybe_unused]] int32_t done, [[maybe_unused]] int32_t total) override {}
42 };
43
44 class TestStream : public Stream {
45 public:
46 TestStream() = default;
47 ~TestStream() = default;
48
49 void EndOfStream() override {}
50 int GetSize() override
51 {
52 static const int HEAP_PROFILER_CHUNK_SIZE = 100_KB;
53 return HEAP_PROFILER_CHUNK_SIZE;
54 }
55 bool WriteChunk([[maybe_unused]] char *data, [[maybe_unused]] int32_t size) override
56 {
57 return true;
58 }
59 bool WriteBinBlock(char *data, int32_t size) override
60 {
61 return WriteChunk(data, size);
62 }
63 bool Good() override
64 {
65 return testStream_.good();
66 }
67
68 void UpdateHeapStats([[maybe_unused]] HeapStat* updateData, [[maybe_unused]] int32_t count) override
69 {
70 }
71
72 void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId, [[maybe_unused]]int64_t timeStampUs) override
73 {
74 }
75
Clear()76 void Clear()
77 {
78 testStream_.clear(std::ios::badbit);
79 }
80
81 private:
82 std::fstream testStream_;
83 };
84 }
85
86 namespace panda::test {
87 class HeapTrackerTest : public testing::Test {
88 public:
SetUpTestCase()89 static void SetUpTestCase()
90 {
91 GTEST_LOG_(INFO) << "SetUpTestCase";
92 }
93
TearDownTestCase()94 static void TearDownTestCase()
95 {
96 GTEST_LOG_(INFO) << "TearDownCase";
97 }
98
99 void SetUp() override
100 {
101 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
102 instance->SetEnableForceGC(false);
103 }
104
105 void TearDown() override
106 {
107 TestHelper::DestroyEcmaVMWithScope(instance, scope);
108 }
109
110 EcmaVM *instance {nullptr};
111 EcmaHandleScope *scope {nullptr};
112 JSThread *thread {nullptr};
113 };
114
HWTEST_F_L0(HeapTrackerTest, HeapTracker)115 HWTEST_F_L0(HeapTrackerTest, HeapTracker)
116 {
117 [[maybe_unused]] EcmaHandleScope handleScope(thread);
118 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
119 heapProfile->StartHeapTracking(50);
120 sleep(1);
121 int count = 100;
122 while (count-- > 0) {
123 instance->GetFactory()->NewJSAsyncFuncObject();
124 }
125 sleep(1);
126 count = 100;
127 while (count-- > 0) {
128 instance->GetFactory()->NewJSSymbol();
129 }
130 sleep(1);
131 count = 100;
132 while (count-- > 0) {
133 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
134 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
135 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
136 }
137
138 // Create file test.heaptimeline
139 std::string fileName = "test.heaptimeline";
140 fstream outputString(fileName, std::ios::out);
141 outputString.close();
142 outputString.clear();
143
144 FileStream stream(fileName.c_str());
145 heapProfile->StopHeapTracking(&stream, nullptr);
146 HeapProfilerInterface::Destroy(instance);
147
148 // Check
149 fstream inputStream(fileName, std::ios::in);
150 std::string line;
151 std::string emptySample = "\"samples\":";
152 std::string firstSample = "\"samples\":[0, ";
153 uint32_t emptySize = emptySample.size();
154 bool isFind = false;
155 while (getline(inputStream, line)) {
156 if (line.substr(0U, emptySize) == emptySample) {
157 ASSERT_TRUE(line.substr(0, firstSample.size()) == firstSample);
158 isFind = true;
159 }
160 }
161 ASSERT_TRUE(isFind);
162
163 inputStream.close();
164 inputStream.clear();
165 std::remove(fileName.c_str());
166 }
167
HWTEST_F_L0(HeapTrackerTest, HeapTrackerTraceAllocation)168 HWTEST_F_L0(HeapTrackerTest, HeapTrackerTraceAllocation)
169 {
170 [[maybe_unused]] EcmaHandleScope handleScope(thread);
171 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
172 TestStream testStream;
173 heapProfile->StartHeapTracking(50, true, &testStream, true);
174 sleep(1);
175 int count = 100;
176 while (count-- > 0) {
177 instance->GetFactory()->NewJSAsyncFuncObject();
178 }
179 sleep(1);
180 count = 100;
181 while (count-- > 0) {
182 instance->GetFactory()->NewJSSymbol();
183 }
184 sleep(1);
185 count = 100;
186 while (count-- > 0) {
187 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
188 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
189 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
190 }
191
192 // Create file test.heaptimeline
193 std::string fileName = "test.heaptimeline";
194 fstream outputString(fileName, std::ios::out);
195 outputString.close();
196 outputString.clear();
197
198 FileStream stream(fileName.c_str());
199 TestProgress testProgress;
200 heapProfile->StopHeapTracking(&stream, &testProgress);
201 HeapProfilerInterface::Destroy(instance);
202
203 // Check
204 fstream inputStream(fileName, std::ios::in);
205 std::string line;
206 std::string emptyTraceFunctionInfo = "\"trace_function_infos\":[";
207 std::string firstTraceFunctionInfo = "\"trace_function_infos\":[0,";
208 uint32_t emptyTraceFunctionInfoSize = emptyTraceFunctionInfo.size();
209 bool traceFunctionInfoIsFind = false;
210 while (getline(inputStream, line)) {
211 if (line.substr(0U, emptyTraceFunctionInfoSize) == emptyTraceFunctionInfo) {
212 ASSERT_TRUE(line.substr(0, firstTraceFunctionInfo.size()) == firstTraceFunctionInfo);
213 traceFunctionInfoIsFind = true;
214 break;
215 }
216 }
217 ASSERT_TRUE(traceFunctionInfoIsFind);
218
219 inputStream.close();
220 inputStream.clear();
221 std::remove(fileName.c_str());
222 }
223
HWTEST_F_L0(HeapTrackerTest, DumpHeapSnapshot)224 HWTEST_F_L0(HeapTrackerTest, DumpHeapSnapshot)
225 {
226 [[maybe_unused]] EcmaHandleScope handleScope(thread);
227 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
228
229 sleep(1);
230 int count = 100;
231 while (count-- > 0) {
232 instance->GetFactory()->NewJSAsyncFuncObject();
233 }
234 sleep(1);
235 count = 100;
236 while (count-- > 0) {
237 instance->GetFactory()->NewJSSymbol();
238 }
239 sleep(1);
240 count = 100;
241 while (count-- > 0) {
242 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
243 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
244 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
245 }
246
247 // Create file test.heaptimeline
248 std::string fileName = "HeapTrackerTest1.heapsnapshot";
249 fstream outputString(fileName, std::ios::out);
250 outputString.close();
251 outputString.clear();
252
253 FileStream stream(fileName.c_str());
254 DumpSnapShotOption dumpOption = { DumpFormat::JSON, true, true };
255 heapProfile->DumpHeapSnapshot(&stream, dumpOption);
256 HeapProfilerInterface::Destroy(instance);
257
258 // Check
259 fstream inputStream(fileName, std::ios::in);
260 std::string line;
261 std::string nodes = "\"nodes\":[";
262 std::string sample = "\"samples\":[]";
263 uint32_t nodesSize = nodes.size();
264 uint32_t sampleSize = sample.size();
265 bool isNodesFind = false;
266 bool isSampleFind = false;
267 while (getline(inputStream, line)) {
268 if (line.substr(0U, nodesSize) == nodes) {
269 isNodesFind = true;
270 }
271
272 if (line.substr(0U, sampleSize) == sample) {
273 isSampleFind = true;
274 }
275 }
276 ASSERT_TRUE(isNodesFind);
277 ASSERT_TRUE(isSampleFind);
278
279 inputStream.close();
280 inputStream.clear();
281 std::remove(fileName.c_str());
282 }
283
HWTEST_F_L0(HeapTrackerTest, HeapSnapshotBuildUp)284 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotBuildUp)
285 {
286 bool isVmMode = true;
287 bool isPrivate = false;
288 bool traceAllocation = false;
289 bool captureNumericValue = false;
290 HeapProfiler heapProfiler(instance);
291 DumpSnapShotOption dumpOption;
292 dumpOption.isVmMode = isVmMode;
293 dumpOption.isPrivate = isPrivate;
294 dumpOption.captureNumericValue = captureNumericValue;
295 HeapSnapshot heapSnapshot(instance, heapProfiler.GetEcmaStringTable(), dumpOption, traceAllocation,
296 heapProfiler.GetEntryIdMap(), instance->GetChunk());
297 EXPECT_TRUE(heapSnapshot.BuildUp());
298 }
299
HWTEST_F_L0(HeapTrackerTest, HeapSnapshotUpdateNode)300 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotUpdateNode)
301 {
302 bool isVmMode = true;
303 bool isPrivate = false;
304 bool traceAllocation = false;
305 bool captureNumericValue = false;
306 HeapProfiler heapProfiler(instance);
307 DumpSnapShotOption dumpOption;
308 dumpOption.isVmMode = isVmMode;
309 dumpOption.isPrivate = isPrivate;
310 dumpOption.captureNumericValue = captureNumericValue;
311 HeapSnapshot heapSnapshot(instance, heapProfiler.GetEcmaStringTable(), dumpOption, traceAllocation,
312 heapProfiler.GetEntryIdMap(), instance->GetChunk());
313 size_t beginNode = heapSnapshot.GetNodeCount();
314 heapSnapshot.UpdateNodes();
315 size_t endNode = heapSnapshot.GetNodeCount();
316 EXPECT_TRUE(beginNode != endNode);
317 }
318 } // namespace panda::test
319