• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/ecma_vm.h"
21 #include "ecmascript/global_dictionary-inl.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/dfx/hprof/heap_profiler.h"
24 #include "ecmascript/dfx/hprof/heap_profiler_interface.h"
25 #include "ecmascript/dfx/hprof/heap_snapshot.h"
26 #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
27 #include "ecmascript/dfx/hprof/string_hashmap.h"
28 #include "ecmascript/ic/ic_handler.h"
29 #include "ecmascript/ic/property_box.h"
30 #include "ecmascript/ic/proto_change_details.h"
31 #include "ecmascript/jobs/micro_job_queue.h"
32 #include "ecmascript/jobs/pending_job.h"
33 #include "ecmascript/jspandafile/program_object.h"
34 #include "ecmascript/js_arguments.h"
35 #include "ecmascript/js_array.h"
36 #include "ecmascript/js_array_iterator.h"
37 #include "ecmascript/js_arraybuffer.h"
38 #include "ecmascript/js_async_function.h"
39 #include "ecmascript/js_collator.h"
40 #include "ecmascript/js_dataview.h"
41 #include "ecmascript/js_date.h"
42 #include "ecmascript/js_date_time_format.h"
43 #include "ecmascript/js_for_in_iterator.h"
44 #include "ecmascript/js_function.h"
45 #include "ecmascript/js_generator_object.h"
46 #include "ecmascript/js_global_object.h"
47 #include "ecmascript/js_handle.h"
48 #include "ecmascript/js_intl.h"
49 #include "ecmascript/js_locale.h"
50 #include "ecmascript/js_map.h"
51 #include "ecmascript/js_map_iterator.h"
52 #include "ecmascript/js_number_format.h"
53 #include "ecmascript/js_object-inl.h"
54 #include "ecmascript/js_plural_rules.h"
55 #include "ecmascript/js_primitive_ref.h"
56 #include "ecmascript/js_promise.h"
57 #include "ecmascript/js_realm.h"
58 #include "ecmascript/js_regexp.h"
59 #include "ecmascript/js_relative_time_format.h"
60 #include "ecmascript/js_set.h"
61 #include "ecmascript/js_set_iterator.h"
62 #include "ecmascript/js_string_iterator.h"
63 #include "ecmascript/js_tagged_number.h"
64 #include "ecmascript/js_tagged_value-inl.h"
65 #include "ecmascript/js_thread.h"
66 #include "ecmascript/js_typed_array.h"
67 #include "ecmascript/js_weak_container.h"
68 #include "ecmascript/layout_info-inl.h"
69 #include "ecmascript/lexical_env.h"
70 #include "ecmascript/linked_hash_table.h"
71 #include "ecmascript/mem/assert_scope.h"
72 #include "ecmascript/mem/c_containers.h"
73 #include "ecmascript/mem/machine_code.h"
74 #include "ecmascript/object_factory.h"
75 #include "ecmascript/tagged_array.h"
76 #include "ecmascript/tagged_dictionary.h"
77 #include "ecmascript/template_map.h"
78 #include "ecmascript/tests/test_helper.h"
79 #include "ecmascript/transitions_dictionary.h"
80 
81 using namespace panda::ecmascript;
82 using namespace panda::ecmascript::base;
83 
84 namespace panda::test {
85 using MicroJobQueue = panda::ecmascript::job::MicroJobQueue;
86 using PendingJob = panda::ecmascript::job::PendingJob;
87 class HProfTest : 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 
SetUp()99     void SetUp() override
100     {
101         TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
102     }
103 
TearDown()104     void TearDown() override
105     {
106         TestHelper::DestroyEcmaVMWithScope(instance, scope);
107     }
108     EcmaVM *instance {nullptr};
109     EcmaHandleScope *scope {nullptr};
110     JSThread *thread {nullptr};
111 };
112 
113 class HProfTestHelper {
114 public:
HProfTestHelper(EcmaVM * vm)115     explicit HProfTestHelper(EcmaVM *vm) : instance(vm) {}
116 
~HProfTestHelper()117     ~HProfTestHelper()
118     {
119         HeapProfilerInterface::Destroy(instance);
120     }
121 
GenerateSnapShot(const std::string & filePath)122     size_t GenerateSnapShot(const std::string &filePath)
123     {
124         // first generate this file of filePath if not exist,
125         // so the function `realpath` of FileStream can not failed on arm/arm64.
126         fstream outputString(filePath, std::ios::out);
127         outputString.close();
128         outputString.clear();
129         FileStream stream(filePath.c_str());
130         HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
131         heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream);
132         return heapProfile->GetIdCount();
133     }
134 
ContrastJSONLineHeader(const std::string & filePath,std::string lineHeader)135     bool ContrastJSONLineHeader(const std::string &filePath, std::string lineHeader)
136     {
137         std::string line;
138         std::ifstream inputStream(filePath);
139         while (getline(inputStream, line)) {
140             if (line.find(lineHeader) != line.npos) {
141                 return true;
142             }
143         }
144         return false;
145     }
146 
ContrastJSONSectionPayload(const std::string & filePath,std::string dataLable,int fieldNum)147     bool ContrastJSONSectionPayload(const std::string &filePath, std::string dataLable, int fieldNum)
148     {
149         std::string line;
150         int i = 1;
151         std::ifstream inputStream(filePath);
152         while (getline(inputStream, line)) {
153             if (i > 10 && line.find(dataLable) != line.npos) {  // 10 : Hit the line
154                 std::string::size_type pos = 0;
155                 int loop = 0;
156                 while ((pos = line.find(",", pos)) != line.npos) {
157                     pos++;
158                     loop++;  // "," count
159                 }
160                 return loop == fieldNum - 1;
161             }
162             i++;  // Search the Next Line
163         }
164         return false;  // Lost the Line
165     }
166 
ContrastJSONPayloadCntAndHitStrField(const std::string & filePath,std::string dataLable,int fieldCnt,std::string fieldStr)167     bool ContrastJSONPayloadCntAndHitStrField(const std::string &filePath, std::string dataLable, int fieldCnt,
168                                               std::string fieldStr)
169     {
170         std::string line;
171         int i = 1;
172         std::ifstream inputStream(filePath);
173         while (getline(inputStream, line)) {
174             if (line.find(dataLable) != line.npos) {  // 3 : Hit the line
175                 std::string::size_type pos = 0;
176                 int loop = 0;
177                 while ((pos = line.find(",", pos)) != line.npos) {
178                     pos++;
179                     loop++;  // "," count
180                 }
181                 if (loop != fieldCnt - 1) {
182                     return false;
183                 }
184                 return line.find(fieldStr) != line.npos; // check if hit the target field
185             }
186             i++;  // Search the Next Line
187         }
188         return false;  // Lost the Line
189     }
190 
ContrastJSONNativeSizeNum(const std::string & filePath,std::string nodesLable,int nNum)191     bool ContrastJSONNativeSizeNum(const std::string &filePath, std::string nodesLable, int nNum)
192     {
193         std::string line;
194         bool hit = false;
195         std::vector<std::string> nodeNativeSizes;
196         std::ifstream inputStream(filePath);
197         while (getline(inputStream, line)) {
198             if (!hit && line.find(nodesLable) == line.npos) {
199                 continue; // Not Get
200             }
201             if (line.find("[]") != line.npos) {  // Empty
202                 break;
203             }
204             if (!hit) {
205                 hit = true;
206             }
207             if (hit && (line.find("],") != line.npos)) {
208                 break; // Reach End
209             }
210             int rCommaInd = line.rfind(','); // find last one
211             if (rCommaInd == line.npos) {
212                 return false;
213             }
214             std::string nativeSizeStr = line.substr(rCommaInd + 1, line.size() - rCommaInd);
215             if (nativeSizeStr.compare("0") != 0) {
216                 nodeNativeSizes.push_back(nativeSizeStr);
217             }
218         }
219         if (instance->GetNativePointerList().size() != nodeNativeSizes.size() || nodeNativeSizes.size() != nNum) {
220             return false;
221         }
222         int ind = 0;
223         for (JSNativePointer *jsNp : instance->GetNativePointerList()) {
224             if (nodeNativeSizes[ind].compare(std::to_string(jsNp->GetBindingSize())) != 0) {
225                 return false;
226             }
227             ind++;
228         }
229         return true;
230     }
231 
ContrastJSONClousure(const std::string & filePath)232     bool ContrastJSONClousure(const std::string &filePath)
233     {
234         std::string lineBk;  // The Last Line
235         std::string line;
236         std::ifstream inputStream(filePath);
237         while (getline(inputStream, line)) {
238             lineBk = line;
239         }
240         return lineBk.compare("}") == 0;
241     }
242 
ExtractCountFromMeta(const std::string & filePath,std::string typeLable)243     int ExtractCountFromMeta(const std::string &filePath, std::string typeLable)
244     {
245         std::string line;
246         std::ifstream inputStream(filePath);
247         while (getline(inputStream, line)) {
248             int length = line.length() - typeLable.length() - 1;
249             if (line.find(typeLable) != line.npos) {  // Get
250                 if (line.find(",") == line.npos) {    // "trace_function_count" end without ","
251                     length = line.length() - typeLable.length();
252                 }
253                 line = line.substr(typeLable.length(), length);
254                 return std::stoi(line.c_str());
255             }
256         }
257         return -1;
258     }
259 
ExtractCountFromPayload(const std::string & filePath,std::string dataLabel)260     int ExtractCountFromPayload(const std::string &filePath, std::string dataLabel)
261     {
262         std::string line;
263         bool hit = false;
264         int loop = 0;
265         std::ifstream inputStream(filePath);
266         while (getline(inputStream, line)) {
267             if (!hit && line.find(dataLabel) != line.npos) {  // Get
268                 loop += 1;                                    // First Line
269                 hit = true;
270                 if (line.find("[]") != line.npos) {  // Empty
271                     loop = 0;
272                     return loop;
273                 } else {
274                     continue;
275                 }
276             }
277             if (hit) {
278                 if (line.find("],") != line.npos) {  // Reach End
279                     loop += 1;                       // End Line
280                     return loop;
281                 } else {
282                     loop++;
283                     continue;
284                 }
285             }
286         }
287         return -1;
288     }
289 
290 private:
291     EcmaVM *instance {nullptr};
292 };
293 
HWTEST_F_L0(HProfTest,ParseJSONHeader)294 HWTEST_F_L0(HProfTest, ParseJSONHeader)
295 {
296     HProfTestHelper tester(instance);
297     tester.GenerateSnapShot("test1.heapsnapshot");
298     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "{\"snapshot\":"));
299     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "{\"meta\":"));
300     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "{\"node_fields\":"));
301     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"node_types\":"));
302     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"edge_fields\":"));
303     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"edge_types\":"));
304     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"trace_function_info_fields\":"));
305     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"trace_node_fields\":"));
306     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"sample_fields\":"));
307     ASSERT_TRUE(tester.ContrastJSONLineHeader("test1.heapsnapshot", "\"location_fields\":"));
308 }
309 
HWTEST_F_L0(HProfTest,ContrastTraceFunctionInfo)310 HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfo)
311 {
312     HProfTestHelper tester(instance);
313     tester.GenerateSnapShot("test2.heapsnapshot");
314     ASSERT_TRUE(tester.ContrastJSONSectionPayload("test2.heapsnapshot", "\"trace_function_infos\":", 2));  // Empty
315 }
316 
HWTEST_F_L0(HProfTest,ContrastTraceTree)317 HWTEST_F_L0(HProfTest, ContrastTraceTree)
318 {
319     HProfTestHelper tester(instance);
320     tester.GenerateSnapShot("test3.heapsnapshot");
321     ASSERT_TRUE(tester.ContrastJSONSectionPayload("test3.heapsnapshot", "\"trace_tree\":", 2));  // Empty
322 }
323 
HWTEST_F_L0(HProfTest,ContrastSamples)324 HWTEST_F_L0(HProfTest, ContrastSamples)
325 {
326     HProfTestHelper tester(instance);
327     tester.GenerateSnapShot("test4.heapsnapshot");
328     ASSERT_TRUE(tester.ContrastJSONSectionPayload("test4.heapsnapshot", "\"samples\":", 2));  // Empty
329 }
330 
HWTEST_F_L0(HProfTest,ContrastLocations)331 HWTEST_F_L0(HProfTest, ContrastLocations)
332 {
333     HProfTestHelper tester(instance);
334     tester.GenerateSnapShot("test5.heapsnapshot");
335     ASSERT_TRUE(tester.ContrastJSONSectionPayload("test5.heapsnapshot", "\"locations\":", 2));  // Empty
336 }
337 
HWTEST_F_L0(HProfTest,ContrastString)338 HWTEST_F_L0(HProfTest, ContrastString)
339 {
340     HProfTestHelper tester(instance);
341     tester.GenerateSnapShot("test6.heapsnapshot");
342     ASSERT_TRUE(tester.ContrastJSONSectionPayload("test6.heapsnapshot", "\"strings\":[", 2));
343 }
344 
HWTEST_F_L0(HProfTest,ContrastClosure)345 HWTEST_F_L0(HProfTest, ContrastClosure)
346 {
347     HProfTestHelper tester(instance);
348     tester.GenerateSnapShot("test7.heapsnapshot");
349     ASSERT_TRUE(tester.ContrastJSONClousure("test7.heapsnapshot"));
350 }
351 
HWTEST_F_L0(HProfTest,ContrastEdgeCount)352 HWTEST_F_L0(HProfTest, ContrastEdgeCount)
353 {
354     HProfTestHelper tester(instance);
355     tester.GenerateSnapShot("test8.heapsnapshot");
356     ASSERT_TRUE(tester.ExtractCountFromMeta("test8.heapsnapshot", "\"edge_count\":") ==
357                 tester.ExtractCountFromPayload("test8.heapsnapshot", "\"edges\":["));
358 }
359 
HWTEST_F_L0(HProfTest,TraceFuncInfoCount)360 HWTEST_F_L0(HProfTest, TraceFuncInfoCount)
361 {
362     HProfTestHelper tester(instance);
363     tester.GenerateSnapShot("test9.heapsnapshot");
364     ASSERT_TRUE(tester.ExtractCountFromMeta("test9.heapsnapshot", "\"trace_function_count\":") ==
365                 tester.ExtractCountFromPayload("test9.heapsnapshot", "\"trace_function_infos\":"));
366 }
367 
HWTEST_F_L0(HProfTest,DumpNativeSize)368 HWTEST_F_L0(HProfTest, DumpNativeSize)
369 {
370     int nativeSizeNum = 6;
371     int count = nativeSizeNum / 2;
372     while (count-- > 0) {
373         instance->GetFactory()->NewJSArrayBuffer(10);
374         instance->GetFactory()->NewJSArrayBuffer(20);
375     }
376     HProfTestHelper tester(instance);
377     tester.GenerateSnapShot("test10.heapsnapshot");
378 
379     ASSERT_TRUE(tester.ContrastJSONPayloadCntAndHitStrField("test10.heapsnapshot", "{\"node_fields\":",
380                                                             9, "native_size"));
381     ASSERT_TRUE(tester.ContrastJSONSectionPayload("test10.heapsnapshot", "\"nodes\":[", 8));
382     ASSERT_TRUE(tester.ContrastJSONNativeSizeNum("test10.heapsnapshot", "\"nodes\":[", nativeSizeNum));
383 }
384 
HWTEST_F_L0(HProfTest,TestIdConsistency)385 HWTEST_F_L0(HProfTest, TestIdConsistency)
386 {
387     HProfTestHelper tester(instance);
388     int64_t count1 = tester.GenerateSnapShot("TestIdConsistency_1.heapsnapshot");
389     for (int i = 0; i < 100; ++i) {
390         instance->GetFactory()->NewJSAsyncFuncObject();
391         instance->GetFactory()->NewJSSymbol();
392     }
393     int64_t count2 = tester.GenerateSnapShot("TestIdConsistency_2.heapsnapshot");
394     ASSERT_TRUE(std::abs(count1 - count2) <= 500LL);
395     // load two heapsnapshots into chrome, and further use "Comparision View"
396 }
397 }  // namespace panda::test
398