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
19 #include "ecmascript/accessor_data.h"
20 #include "ecmascript/jspandafile/program_object-inl.h"
21 #include "ecmascript/ecma_module.h"
22 #include "ecmascript/ecma_vm.h"
23 #include "ecmascript/global_dictionary-inl.h"
24 #include "ecmascript/global_env.h"
25 #include "ecmascript/hprof/heap_profiler.h"
26 #include "ecmascript/hprof/heap_profiler_interface.h"
27 #include "ecmascript/hprof/heap_snapshot.h"
28 #include "ecmascript/hprof/heap_snapshot_json_serializer.h"
29 #include "ecmascript/hprof/string_hashmap.h"
30 #include "ecmascript/ic/ic_handler.h"
31 #include "ecmascript/ic/property_box.h"
32 #include "ecmascript/ic/proto_change_details.h"
33 #include "ecmascript/jobs/micro_job_queue.h"
34 #include "ecmascript/jobs/pending_job.h"
35 #include "ecmascript/js_arguments.h"
36 #include "ecmascript/js_array.h"
37 #include "ecmascript/js_array_iterator.h"
38 #include "ecmascript/js_arraybuffer.h"
39 #include "ecmascript/js_async_function.h"
40 #include "ecmascript/js_collator.h"
41 #include "ecmascript/js_dataview.h"
42 #include "ecmascript/js_date.h"
43 #include "ecmascript/js_date_time_format.h"
44 #include "ecmascript/js_for_in_iterator.h"
45 #include "ecmascript/js_function.h"
46 #include "ecmascript/js_generator_object.h"
47 #include "ecmascript/js_global_object.h"
48 #include "ecmascript/js_handle.h"
49 #include "ecmascript/js_intl.h"
50 #include "ecmascript/js_locale.h"
51 #include "ecmascript/js_map.h"
52 #include "ecmascript/js_map_iterator.h"
53 #include "ecmascript/js_number_format.h"
54 #include "ecmascript/js_object-inl.h"
55 #include "ecmascript/js_plural_rules.h"
56 #include "ecmascript/js_primitive_ref.h"
57 #include "ecmascript/js_promise.h"
58 #include "ecmascript/js_realm.h"
59 #include "ecmascript/js_regexp.h"
60 #include "ecmascript/js_relative_time_format.h"
61 #include "ecmascript/js_set.h"
62 #include "ecmascript/js_set_iterator.h"
63 #include "ecmascript/js_string_iterator.h"
64 #include "ecmascript/js_tagged_number.h"
65 #include "ecmascript/js_tagged_value-inl.h"
66 #include "ecmascript/js_thread.h"
67 #include "ecmascript/js_typed_array.h"
68 #include "ecmascript/js_weak_container.h"
69 #include "ecmascript/layout_info-inl.h"
70 #include "ecmascript/lexical_env.h"
71 #include "ecmascript/linked_hash_table-inl.h"
72 #include "ecmascript/mem/assert_scope-inl.h"
73 #include "ecmascript/mem/c_containers.h"
74 #include "ecmascript/mem/machine_code.h"
75 #include "ecmascript/object_factory.h"
76 #include "ecmascript/tagged_array.h"
77 #include "ecmascript/tagged_dictionary.h"
78 #include "ecmascript/template_map.h"
79 #include "ecmascript/tests/test_helper.h"
80 #include "ecmascript/transitions_dictionary.h"
81
82 using namespace panda::ecmascript;
83 using namespace panda::ecmascript::base;
84
85 namespace panda::test {
86 using MicroJobQueue = panda::ecmascript::job::MicroJobQueue;
87 using PendingJob = panda::ecmascript::job::PendingJob;
88 class HProfTest : public testing::Test {
89 public:
SetUpTestCase()90 static void SetUpTestCase()
91 {
92 GTEST_LOG_(INFO) << "SetUpTestCase";
93 }
94
TearDownTestCase()95 static void TearDownTestCase()
96 {
97 GTEST_LOG_(INFO) << "TearDownCase";
98 }
99
SetUp()100 void SetUp() override
101 {
102 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
103 }
104
TearDown()105 void TearDown() override
106 {
107 TestHelper::DestroyEcmaVMWithScope(instance, scope);
108 }
109 PandaVM *instance {nullptr};
110 EcmaHandleScope *scope {nullptr};
111 JSThread *thread {nullptr};
112 };
113
114 class HProfTestHelper {
115 public:
HProfTestHelper(const CString & aFilePath)116 explicit HProfTestHelper(const CString &aFilePath) : filePath(aFilePath) {}
117 ~HProfTestHelper() = default;
118
IsFileExist()119 bool IsFileExist()
120 {
121 bool exist = false;
122 exist = SafeOpenFile();
123 SafeCloseFile();
124 return exist;
125 }
126
RemoveExistingFile()127 bool RemoveExistingFile()
128 {
129 int code = 0;
130 if (IsFileExist()) {
131 code = std::remove(filePath.c_str());
132 }
133 return code == 0 ? true : false;
134 }
135
GenerateHeapDump(JSThread * thread)136 bool GenerateHeapDump(JSThread *thread)
137 {
138 int retry = 2;
139 RemoveExistingFile();
140 HeapProfilerInterface::DumpHeapSnapShot(thread, DumpFormat::JSON, filePath);
141 while (!IsFileExist() && retry > 0) {
142 retry--;
143 HeapProfilerInterface::DumpHeapSnapShot(thread, DumpFormat::JSON, filePath);
144 }
145 return IsFileExist();
146 }
147
ContrastJSONLineHeader(int lineId,CString lineHeader)148 bool ContrastJSONLineHeader(int lineId, CString lineHeader)
149 {
150 bool allSame = false;
151 if (!SafeOpenFile()) { // No JSON File
152 allSame = false;
153 SafeCloseFile();
154 } else {
155 CString line;
156 int i = 1;
157 while (getline(inputFile, line)) {
158 if (i == lineId && line.find(lineHeader) != line.npos) {
159 allSame = true;
160 break;
161 }
162 i++;
163 }
164 SafeCloseFile();
165 }
166
167 return allSame;
168 }
169
ContrastJSONSectionPayload(CString dataLable,int fieldNum)170 bool ContrastJSONSectionPayload(CString dataLable, int fieldNum)
171 {
172 if (!SafeOpenFile()) { // No JSON File
173 SafeCloseFile();
174 return false;
175 } else {
176 CString line;
177 int i = 1;
178 while (getline(inputFile, line)) {
179 if (i > 10 && line.find(dataLable) != line.npos) { // 10 : Hit the line
180 CString::size_type pos = 0;
181 int loop = 0;
182 while ((pos = line.find(",", pos)) != line.npos) {
183 pos++;
184 loop++; // "," count
185 }
186 SafeCloseFile();
187 return loop == fieldNum - 1;
188 break;
189 }
190 i++; // Search the Next Line
191 }
192 SafeCloseFile();
193 }
194 return false; // Lost the Line
195 }
196
ContrastJSONClousure()197 bool ContrastJSONClousure()
198 {
199 if (!SafeOpenFile()) { // No JSON File
200 SafeCloseFile();
201 return false;
202 }
203 CString lineBk; // The Last Line
204 CString line;
205 while (getline(inputFile, line)) {
206 lineBk = line;
207 }
208 SafeCloseFile();
209 return lineBk.compare("}") == 0;
210 }
211
ExtractCountFromMeta(CString typeLable)212 int ExtractCountFromMeta(CString typeLable)
213 {
214 if (!SafeOpenFile()) { // No JSON File
215 SafeCloseFile();
216 return -1;
217 } else {
218 CString line;
219 int i = 1;
220 while (getline(inputFile, line)) {
221 int length = line.length() - typeLable.length() - 1;
222 if (line.find(typeLable) != line.npos) { // Get
223 if (line.find(",") == line.npos) { // "trace_function_count" end without ","
224 length = line.length() - typeLable.length();
225 }
226 line = line.substr(typeLable.length(), length);
227 SafeCloseFile();
228 return std::stoi(line.c_str());
229 }
230 i++;
231 }
232 SafeCloseFile();
233 }
234 return -1;
235 }
236
ExtractCountFromPayload(CString dataLabel)237 int ExtractCountFromPayload(CString dataLabel)
238 {
239 if (!SafeOpenFile()) { // No JSON File
240 SafeCloseFile();
241 return -1;
242 } else {
243 CString line;
244 bool hit = false;
245 int loop = 0;
246 while (getline(inputFile, line)) {
247 if (!hit && line.find(dataLabel) != line.npos) { // Get
248 loop += 1; // First Line
249 hit = true;
250 if (line.find("[]") != line.npos) { // Empty
251 loop = 0;
252 SafeCloseFile();
253 return loop;
254 } else {
255 continue;
256 }
257 }
258 if (hit) {
259 if (line.find("],") != line.npos) { // Reach End
260 loop += 1; // End Line
261 SafeCloseFile();
262 return loop;
263 } else {
264 loop++;
265 continue;
266 }
267 }
268 }
269 SafeCloseFile();
270 }
271 return -1;
272 }
273
274 private:
SafeOpenFile()275 bool SafeOpenFile()
276 {
277 inputFile.clear();
278 inputFile.open(filePath.c_str(), std::ios::in);
279 int retry = 2;
280 while (!inputFile.good() && retry > 0) {
281 inputFile.open(filePath.c_str(), std::ios::in);
282 retry--;
283 }
284 return inputFile.good();
285 }
286
SafeCloseFile()287 void SafeCloseFile()
288 {
289 inputFile.close();
290 inputFile.clear(); // Reset File status
291 }
292 CString filePath;
293 std::fstream inputFile {};
294 };
295
HWTEST_F_L0(HProfTest,GenerateFileForManualCheck)296 HWTEST_F_L0(HProfTest, GenerateFileForManualCheck)
297 {
298 HProfTestHelper tester("hprof_json_test.heapsnapshot");
299 ASSERT_TRUE(tester.GenerateHeapDump(thread));
300 }
301
HWTEST_F_L0(HProfTest,GenerateFile)302 HWTEST_F_L0(HProfTest, GenerateFile)
303 {
304 HProfTestHelper tester("GenerateFile.heapsnapshot");
305 ASSERT_TRUE(tester.GenerateHeapDump(thread));
306 ASSERT_TRUE(tester.RemoveExistingFile());
307 }
308
HWTEST_F_L0(HProfTest,ParseJSONHeader)309 HWTEST_F_L0(HProfTest, ParseJSONHeader)
310 {
311 HProfTestHelper tester("ParseJSONHeader.heapsnapshot");
312 ASSERT_TRUE(tester.GenerateHeapDump(thread));
313 ASSERT_TRUE(tester.ContrastJSONLineHeader(1, "{\"snapshot\":"));
314 ASSERT_TRUE(tester.ContrastJSONLineHeader(2, "{\"meta\":"));
315 ASSERT_TRUE(tester.ContrastJSONLineHeader(3, "{\"node_fields\":"));
316 ASSERT_TRUE(tester.ContrastJSONLineHeader(4, "\"node_types\":"));
317 ASSERT_TRUE(tester.ContrastJSONLineHeader(5, "\"edge_fields\":"));
318 ASSERT_TRUE(tester.ContrastJSONLineHeader(6, "\"edge_types\":"));
319 ASSERT_TRUE(tester.ContrastJSONLineHeader(7, "\"trace_function_info_fields\":"));
320 ASSERT_TRUE(tester.ContrastJSONLineHeader(8, "\"trace_node_fields\":"));
321 ASSERT_TRUE(tester.ContrastJSONLineHeader(9, "\"sample_fields\":"));
322 ASSERT_TRUE(tester.ContrastJSONLineHeader(10, "\"location_fields\":"));
323 ASSERT_TRUE(tester.RemoveExistingFile());
324 }
325
HWTEST_F_L0(HProfTest,ContrastTraceFunctionInfo)326 HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfo)
327 {
328 HProfTestHelper tester("ContrastTraceFunctionInfo.heapsnapshot");
329 ASSERT_TRUE(tester.GenerateHeapDump(thread));
330 ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_function_infos\":", 2)); // Empty
331 ASSERT_TRUE(tester.RemoveExistingFile());
332 }
333
HWTEST_F_L0(HProfTest,ContrastTraceTree)334 HWTEST_F_L0(HProfTest, ContrastTraceTree)
335 {
336 HProfTestHelper tester("ContrastTraceTree.heapsnapshot");
337 ASSERT_TRUE(tester.GenerateHeapDump(thread));
338 ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_tree\":", 2)); // Empty
339 ASSERT_TRUE(tester.RemoveExistingFile());
340 }
341
HWTEST_F_L0(HProfTest,ContrastSamples)342 HWTEST_F_L0(HProfTest, ContrastSamples)
343 {
344 HProfTestHelper tester("ContrastSamples.heapsnapshot");
345 ASSERT_TRUE(tester.GenerateHeapDump(thread));
346 ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"samples\":", 2)); // Empty
347 ASSERT_TRUE(tester.RemoveExistingFile());
348 }
349
HWTEST_F_L0(HProfTest,ContrastLocations)350 HWTEST_F_L0(HProfTest, ContrastLocations)
351 {
352 HProfTestHelper tester("ContrastLocations.heapsnapshot");
353 ASSERT_TRUE(tester.GenerateHeapDump(thread));
354 ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"locations\":", 2)); // Empty
355 ASSERT_TRUE(tester.RemoveExistingFile());
356 }
357
HWTEST_F_L0(HProfTest,ContrastString)358 HWTEST_F_L0(HProfTest, ContrastString)
359 {
360 HProfTestHelper tester("ContrastString.heapsnapshot");
361 ASSERT_TRUE(tester.GenerateHeapDump(thread));
362 ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"strings\":[", 1 + 1));
363 ASSERT_TRUE(tester.RemoveExistingFile());
364 }
365
HWTEST_F_L0(HProfTest,ContrastClosure)366 HWTEST_F_L0(HProfTest, ContrastClosure)
367 {
368 HProfTestHelper tester("ContrastClosure.heapsnapshot");
369 ASSERT_TRUE(tester.GenerateHeapDump(thread));
370 ASSERT_TRUE(tester.ContrastJSONClousure());
371 ASSERT_TRUE(tester.RemoveExistingFile());
372 }
373
HWTEST_F_L0(HProfTest,ContrastEdgeCount)374 HWTEST_F_L0(HProfTest, ContrastEdgeCount)
375 {
376 HProfTestHelper tester("ContrastEdgeCount.heapsnapshot");
377 ASSERT_TRUE(tester.GenerateHeapDump(thread));
378 ASSERT_TRUE(tester.ExtractCountFromMeta("\"edge_count\":") == tester.ExtractCountFromPayload("\"edges\":["));
379 ASSERT_TRUE(tester.RemoveExistingFile());
380 }
381
HWTEST_F_L0(HProfTest,ContrastTraceFunctionInfoCount)382 HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfoCount)
383 {
384 HProfTestHelper tester("ContrastTraceFunctionInfoCount.heapsnapshot");
385 ASSERT_TRUE(tester.GenerateHeapDump(thread));
386 ASSERT_TRUE(tester.ExtractCountFromMeta("\"trace_function_count\":") ==
387 tester.ExtractCountFromPayload("\"trace_function_infos\":"));
388 ASSERT_TRUE(tester.RemoveExistingFile());
389 }
390 } // namespace panda::test
391