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
ContrastJSONClousure(const std::string & filePath)167 bool ContrastJSONClousure(const std::string &filePath)
168 {
169 std::string lineBk; // The Last Line
170 std::string line;
171 std::ifstream inputStream(filePath);
172 while (getline(inputStream, line)) {
173 lineBk = line;
174 }
175 return lineBk.compare("}") == 0;
176 }
177
ExtractCountFromMeta(const std::string & filePath,std::string typeLable)178 int ExtractCountFromMeta(const std::string &filePath, std::string typeLable)
179 {
180 std::string line;
181 std::ifstream inputStream(filePath);
182 while (getline(inputStream, line)) {
183 int length = line.length() - typeLable.length() - 1;
184 if (line.find(typeLable) != line.npos) { // Get
185 if (line.find(",") == line.npos) { // "trace_function_count" end without ","
186 length = line.length() - typeLable.length();
187 }
188 line = line.substr(typeLable.length(), length);
189 return std::stoi(line.c_str());
190 }
191 }
192 return -1;
193 }
194
ExtractCountFromPayload(const std::string & filePath,std::string dataLabel)195 int ExtractCountFromPayload(const std::string &filePath, std::string dataLabel)
196 {
197 std::string line;
198 bool hit = false;
199 int loop = 0;
200 std::ifstream inputStream(filePath);
201 while (getline(inputStream, line)) {
202 if (!hit && line.find(dataLabel) != line.npos) { // Get
203 loop += 1; // First Line
204 hit = true;
205 if (line.find("[]") != line.npos) { // Empty
206 loop = 0;
207 return loop;
208 } else {
209 continue;
210 }
211 }
212 if (hit) {
213 if (line.find("],") != line.npos) { // Reach End
214 loop += 1; // End Line
215 return loop;
216 } else {
217 loop++;
218 continue;
219 }
220 }
221 }
222 return -1;
223 }
224
225 private:
226 EcmaVM *instance {nullptr};
227 };
228
HWTEST_F_L0(HProfTest,ParseJSONHeader)229 HWTEST_F_L0(HProfTest, ParseJSONHeader)
230 {
231 HProfTestHelper tester(instance);
232 tester.GenerateSnapShot("test.heapsnapshot");
233 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "{\"snapshot\":"));
234 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "{\"meta\":"));
235 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "{\"node_fields\":"));
236 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"node_types\":"));
237 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"edge_fields\":"));
238 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"edge_types\":"));
239 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"trace_function_info_fields\":"));
240 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"trace_node_fields\":"));
241 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"sample_fields\":"));
242 ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"location_fields\":"));
243 }
244
HWTEST_F_L0(HProfTest,ContrastTraceFunctionInfo)245 HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfo)
246 {
247 HProfTestHelper tester(instance);
248 tester.GenerateSnapShot("test.heapsnapshot");
249 ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"trace_function_infos\":", 2)); // Empty
250 }
251
HWTEST_F_L0(HProfTest,ContrastTraceTree)252 HWTEST_F_L0(HProfTest, ContrastTraceTree)
253 {
254 HProfTestHelper tester(instance);
255 tester.GenerateSnapShot("test.heapsnapshot");
256 ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"trace_tree\":", 2)); // Empty
257 }
258
HWTEST_F_L0(HProfTest,ContrastSamples)259 HWTEST_F_L0(HProfTest, ContrastSamples)
260 {
261 HProfTestHelper tester(instance);
262 tester.GenerateSnapShot("test.heapsnapshot");
263 ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"samples\":", 2)); // Empty
264 }
265
HWTEST_F_L0(HProfTest,ContrastLocations)266 HWTEST_F_L0(HProfTest, ContrastLocations)
267 {
268 HProfTestHelper tester(instance);
269 tester.GenerateSnapShot("test.heapsnapshot");
270 ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"locations\":", 2)); // Empty
271 }
272
HWTEST_F_L0(HProfTest,ContrastString)273 HWTEST_F_L0(HProfTest, ContrastString)
274 {
275 HProfTestHelper tester(instance);
276 tester.GenerateSnapShot("test.heapsnapshot");
277 ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"strings\":[", 2));
278 }
279
HWTEST_F_L0(HProfTest,ContrastClosure)280 HWTEST_F_L0(HProfTest, ContrastClosure)
281 {
282 HProfTestHelper tester(instance);
283 tester.GenerateSnapShot("test.heapsnapshot");
284 ASSERT_TRUE(tester.ContrastJSONClousure("test.heapsnapshot"));
285 }
286
HWTEST_F_L0(HProfTest,ContrastEdgeCount)287 HWTEST_F_L0(HProfTest, ContrastEdgeCount)
288 {
289 HProfTestHelper tester(instance);
290 tester.GenerateSnapShot("test.heapsnapshot");
291 ASSERT_TRUE(tester.ExtractCountFromMeta("test.heapsnapshot", "\"edge_count\":") ==
292 tester.ExtractCountFromPayload("test.heapsnapshot", "\"edges\":["));
293 }
294
HWTEST_F_L0(HProfTest,TraceFuncInfoCount)295 HWTEST_F_L0(HProfTest, TraceFuncInfoCount)
296 {
297 HProfTestHelper tester(instance);
298 tester.GenerateSnapShot("test.heapsnapshot");
299 ASSERT_TRUE(tester.ExtractCountFromMeta("test.heapsnapshot", "\"trace_function_count\":") ==
300 tester.ExtractCountFromPayload("test.heapsnapshot", "\"trace_function_infos\":"));
301 }
302
HWTEST_F_L0(HProfTest,TestIdConsistency)303 HWTEST_F_L0(HProfTest, TestIdConsistency)
304 {
305 HProfTestHelper tester(instance);
306 int64_t count1 = tester.GenerateSnapShot("TestIdConsistency_1.heapsnapshot");
307 for (int i = 0; i < 100; ++i) {
308 instance->GetFactory()->NewJSAsyncFuncObject();
309 instance->GetFactory()->NewJSSymbol();
310 }
311 int64_t count2 = tester.GenerateSnapShot("TestIdConsistency_2.heapsnapshot");
312 ASSERT_TRUE(std::abs(count1 - count2) <= 500LL);
313 // load two heapsnapshots into chrome, and further use "Comparision View"
314 }
315 } // namespace panda::test
316