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