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