• 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 <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 heapProfilerChunkSise = 100_KB;
53         return heapProfilerChunkSise;
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<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
130         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
131     }
132 
133     // Create file test.heaptimeline
134     std::string fileName = "test.heaptimeline";
135     fstream outputString(fileName, std::ios::out);
136     outputString.close();
137     outputString.clear();
138 
139     FileStream stream(fileName.c_str());
140     heapProfile->StopHeapTracking(&stream, nullptr);
141     HeapProfilerInterface::Destroy(instance);
142 
143     // Check
144     fstream inputStream(fileName, std::ios::in);
145     std::string line;
146     std::string emptySample = "\"samples\":";
147     std::string firstSample = "\"samples\":[0, ";
148     uint32_t emptySize = emptySample.size();
149     bool isFind = false;
150     while (getline(inputStream, line)) {
151         if (line.substr(0U, emptySize) == emptySample) {
152             ASSERT_TRUE(line.substr(0, firstSample.size()) == firstSample);
153             isFind = true;
154         }
155     }
156     ASSERT_TRUE(isFind);
157 
158     inputStream.close();
159     inputStream.clear();
160     std::remove(fileName.c_str());
161 }
162 
HWTEST_F_L0(HeapTrackerTest,HeapTrackerTraceAllocation)163 HWTEST_F_L0(HeapTrackerTest, HeapTrackerTraceAllocation)
164 {
165     [[maybe_unused]] EcmaHandleScope handleScope(thread);
166     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
167     TestStream testStream;
168     heapProfile->StartHeapTracking(50, true, &testStream, true);
169     sleep(1);
170     int count = 100;
171     while (count-- > 0) {
172         instance->GetFactory()->NewJSAsyncFuncObject();
173     }
174     sleep(1);
175     count = 100;
176     while (count-- > 0) {
177         instance->GetFactory()->NewJSSymbol();
178     }
179     sleep(1);
180     count = 100;
181     while (count-- > 0) {
182         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
183         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
184     }
185 
186     // Create file test.heaptimeline
187     std::string fileName = "test.heaptimeline";
188     fstream outputString(fileName, std::ios::out);
189     outputString.close();
190     outputString.clear();
191 
192     FileStream stream(fileName.c_str());
193     TestProgress testProgress;
194     heapProfile->StopHeapTracking(&stream, &testProgress);
195     HeapProfilerInterface::Destroy(instance);
196 
197     // Check
198     fstream inputStream(fileName, std::ios::in);
199     std::string line;
200     std::string emptyTraceFunctionInfo = "\"trace_function_infos\":[";
201     std::string emptyTraceNode = "\"trace_tree\":[";
202     std::string firstTraceFunctionInfo = "\"trace_function_infos\":[0,";
203     std::string firstTraceNode = "\"trace_tree\":[1,0";
204     uint32_t emptyTraceFunctionInfoSize = emptyTraceFunctionInfo.size();
205     uint32_t emptyTraceNodeSize = emptyTraceNode.size();
206     bool traceFunctionInfoIsFind = false;
207     bool traceNodeIsFind = false;
208     while (getline(inputStream, line)) {
209         if (line.substr(0U, emptyTraceFunctionInfoSize) == emptyTraceFunctionInfo) {
210             ASSERT_TRUE(line.substr(0, firstTraceFunctionInfo.size()) == firstTraceFunctionInfo);
211             traceFunctionInfoIsFind = true;
212         }
213 
214         if (line.substr(0U, emptyTraceNodeSize) == emptyTraceNode) {
215             ASSERT_TRUE(line.substr(0, firstTraceNode.size()) == firstTraceNode);
216             traceNodeIsFind = true;
217         }
218     }
219     ASSERT_TRUE(traceFunctionInfoIsFind);
220     ASSERT_TRUE(traceNodeIsFind);
221 
222     inputStream.close();
223     inputStream.clear();
224     std::remove(fileName.c_str());
225 }
226 
HWTEST_F_L0(HeapTrackerTest,DumpHeapSnapshot)227 HWTEST_F_L0(HeapTrackerTest, DumpHeapSnapshot)
228 {
229     [[maybe_unused]] EcmaHandleScope handleScope(thread);
230     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
231 
232     sleep(1);
233     int count = 100;
234     while (count-- > 0) {
235         instance->GetFactory()->NewJSAsyncFuncObject();
236     }
237     sleep(1);
238     count = 100;
239     while (count-- > 0) {
240         instance->GetFactory()->NewJSSymbol();
241     }
242     sleep(1);
243     count = 100;
244     while (count-- > 0) {
245         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
246         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
247     }
248 
249     // Create file test.heapsnapshot
250     std::string fileName = "test.heapsnapshot";
251     fstream outputString(fileName, std::ios::out);
252     outputString.close();
253     outputString.clear();
254 
255     FileStream stream(fileName.c_str());
256     TestProgress testProgress;
257     heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true);
258     HeapProfilerInterface::Destroy(instance);
259 
260     // Check
261     fstream inputStream(fileName, std::ios::in);
262     std::string line;
263     std::string nodes = "\"nodes\":[";
264     std::string sample = "\"samples\":[]";
265     uint32_t nodesSize = nodes.size();
266     uint32_t sampleSize = sample.size();
267     bool isNodesFind = false;
268     bool isSampleFind = false;
269     while (getline(inputStream, line)) {
270         if (line.substr(0U, nodesSize) == nodes) {
271             isNodesFind = true;
272         }
273 
274         if (line.substr(0U, sampleSize) == sample) {
275             isSampleFind = true;
276         }
277     }
278     ASSERT_TRUE(isNodesFind);
279     ASSERT_TRUE(isSampleFind);
280 
281     inputStream.close();
282     inputStream.clear();
283     std::remove(fileName.c_str());
284 }
285 
HWTEST_F_L0(HeapTrackerTest,HeapSnapshotBuildUp)286 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotBuildUp)
287 {
288     bool isVmMode = true;
289     bool isPrivate = false;
290     bool traceAllocation = false;
291     HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, traceAllocation, instance->GetChunk());
292     EXPECT_TRUE(heapSnapshot.BuildUp());
293 }
294 
HWTEST_F_L0(HeapTrackerTest,HeapSnapshotUpdateNode)295 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotUpdateNode)
296 {
297     bool isVmMode = true;
298     bool isPrivate = false;
299     bool traceAllocation = false;
300     HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, traceAllocation, instance->GetChunk());
301     size_t beginNode = heapSnapshot.GetNodeCount();
302     heapSnapshot.UpdateNodes();
303     size_t endNode = heapSnapshot.GetNodeCount();
304     EXPECT_TRUE(beginNode != endNode);
305 }
306 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_001)307 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_001)
308 {
309     [[maybe_unused]] EcmaHandleScope handleScope(thread);
310     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
311 
312     sleep(1);
313     int count = 100;
314     while (count-- > 0) {
315         instance->GetFactory()->NewJSAsyncFuncObject();
316     }
317     sleep(1);
318     count = 100;
319     while (count-- > 0) {
320         instance->GetFactory()->NewJSSymbol();
321     }
322     sleep(1);
323     count = 100;
324     while (count-- > 0) {
325         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
326         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
327     }
328 
329     TestStream stream;
330     stream.Clear();
331     EXPECT_TRUE(!stream.Good());
332     TestProgress testProgress;
333     heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true);
334     HeapProfilerInterface::Destroy(instance);
335 }
336 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_002)337 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_002)
338 {
339     [[maybe_unused]] EcmaHandleScope handleScope(thread);
340     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
341 
342     sleep(1);
343     int count = 100;
344     while (count-- > 0) {
345         instance->GetFactory()->NewJSAsyncFuncObject();
346     }
347     sleep(1);
348     count = 100;
349     while (count-- > 0) {
350         instance->GetFactory()->NewJSSymbol();
351     }
352     sleep(1);
353     count = 100;
354     while (count-- > 0) {
355         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
356         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
357     }
358 
359     TestStream stream;
360     stream.Clear();
361     EXPECT_TRUE(!stream.Good());
362     TestProgress testProgress;
363     heapProfile->DumpHeapSnapshot(DumpFormat::BINARY, &stream, &testProgress, true, true);
364     HeapProfilerInterface::Destroy(instance);
365 }
366 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_003)367 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_003)
368 {
369     [[maybe_unused]] EcmaHandleScope handleScope(thread);
370     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
371 
372     sleep(1);
373     int count = 100;
374     while (count-- > 0) {
375         instance->GetFactory()->NewJSAsyncFuncObject();
376     }
377     sleep(1);
378     count = 100;
379     while (count-- > 0) {
380         instance->GetFactory()->NewJSSymbol();
381     }
382     sleep(1);
383     count = 100;
384     while (count-- > 0) {
385         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
386         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
387     }
388 
389     TestStream stream;
390     stream.Clear();
391     EXPECT_TRUE(!stream.Good());
392     TestProgress testProgress;
393     heapProfile->DumpHeapSnapshot(DumpFormat::OTHER, &stream, &testProgress, true, true);
394     HeapProfilerInterface::Destroy(instance);
395 }
396 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_004)397 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_004)
398 {
399     [[maybe_unused]] EcmaHandleScope handleScope(thread);
400     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
401 
402     sleep(1);
403     int count = 100;
404     while (count-- > 0) {
405         instance->GetFactory()->NewJSAsyncFuncObject();
406     }
407     sleep(1);
408     count = 100;
409     while (count-- > 0) {
410         instance->GetFactory()->NewJSSymbol();
411     }
412     sleep(1);
413     count = 100;
414     while (count-- > 0) {
415         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
416         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
417     }
418 
419     TestStream stream;
420     stream.Clear();
421     EXPECT_TRUE(!stream.Good());
422     TestProgress testProgress;
423     DumpFormat dumFormat = static_cast<DumpFormat>(5);
424     heapProfile->DumpHeapSnapshot(dumFormat, &stream, &testProgress, true, true);
425     HeapProfilerInterface::Destroy(instance);
426 }
427 
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamEndOfStream)428 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamEndOfStream)
429 {
430     int fd = 3;
431     FileDescriptorStream fileStream(fd);
432     EXPECT_TRUE(fileStream.Good());
433     fileStream.EndOfStream();
434 }
435 
HWTEST_F_L0(HeapTrackerTest,StreamWriterEnd)436 HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd)
437 {
438     [[maybe_unused]] EcmaHandleScope handleScope(thread);
439     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
440 
441     sleep(1);
442     int count = 100;
443     while (count-- > 0) {
444         instance->GetFactory()->NewJSAsyncFuncObject();
445     }
446     sleep(1);
447     count = 100;
448     while (count-- > 0) {
449         instance->GetFactory()->NewJSSymbol();
450     }
451     sleep(1);
452     count = 100;
453     while (count-- > 0) {
454         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
455         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string));
456     }
457 
458     // Create file test.heapsnapshot
459     std::string fileName = "test.heapsnapshot";
460     fstream outputString(fileName, std::ios::out);
461     outputString.close();
462     outputString.clear();
463 
464     FileStream stream(fileName.c_str());
465     CVector<HeapStat> statsBuffer;
466     statsBuffer.emplace_back(1, 2, 4);
467     stream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
468     stream.UpdateLastSeenObjectId(1, 1677567644913058);
469 
470     TestProgress testProgress;
471     heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true);
472     StreamWriter streamWriter(&stream);
473     streamWriter.End();
474     HeapProfilerInterface::Destroy(instance);
475 
476     // Check
477     fstream inputStream(fileName, std::ios::in);
478     std::string line;
479     std::string nodes = "\"nodes\":[";
480     std::string sample = "\"samples\":[]";
481     uint32_t nodesSize = nodes.size();
482     uint32_t sampleSize = sample.size();
483     bool isNodesFind = false;
484     bool isSampleFind = false;
485     while (getline(inputStream, line)) {
486         if (line.substr(0U, nodesSize) == nodes) {
487             isNodesFind = true;
488         }
489 
490         if (line.substr(0U, sampleSize) == sample) {
491             isSampleFind = true;
492         }
493     }
494     ASSERT_TRUE(isNodesFind);
495     ASSERT_TRUE(isSampleFind);
496 
497     inputStream.close();
498     inputStream.clear();
499     std::remove(fileName.c_str());
500 }
501 
HWTEST_F_L0(HeapTrackerTest,GetStringByKey)502 HWTEST_F_L0(HeapTrackerTest, GetStringByKey)
503 {
504     StringKey key = static_cast<StringKey>(2);
505     StringHashMap stringHashMap(instance);
506     CString *hashMap = stringHashMap.GetStringByKey(key);
507     EXPECT_TRUE(hashMap == nullptr);
508 }
509 
HWTEST_F_L0(HeapTrackerTest,FormatString)510 HWTEST_F_L0(HeapTrackerTest, FormatString)
511 {
512     bool isVmMode = true;
513     bool isPrivate = false;
514     bool traceAllocation = false;
515     HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, traceAllocation, instance->GetChunk());
516 
517     StringHashMap stringHashMap(instance);
518     CString ret = "H\"e\rl\nl\\o\t W\fo\31rld!";
519     stringHashMap.GetString(ret);
520     StringKey retKey = std::hash<std::string>{} (std::string(ret));
521 
522     CString *tmpResult = nullptr;
523     tmpResult = stringHashMap.GetStringByKey(retKey);
524     EXPECT_TRUE(tmpResult != nullptr);
525     EXPECT_TRUE(*tmpResult == "H`e`l`l`o` W`o`rld!");
526 }
527 
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamWriteChunk)528 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamWriteChunk)
529 {
530     int32_t fd = -1;
531     FileDescriptorStream testFileStream(fd);
532     CVector<HeapStat> statsBuffer;
533     statsBuffer.emplace_back(1, 2, 4);
534     testFileStream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
535     testFileStream.UpdateLastSeenObjectId(1, 1677567644913058);
536     testFileStream.GetSize();
537     std::string testString = "Hello!";
538     int strSize = testString.size();
539     bool isFileStream = testFileStream.WriteChunk(testString.data(), strSize);
540     EXPECT_TRUE(!isFileStream);
541 
542     fd = 5;
543     FileDescriptorStream tmpFileStream(fd);
544     tmpFileStream.Good();
545     testString = "Hello!";
546     strSize = testString.size();
547     isFileStream = tmpFileStream.WriteChunk(testString.data(), strSize);
548     close(fd);
549     EXPECT_TRUE(!isFileStream);
550 
551     std::string fileName = "test.StreamWriteChunk";
552     fd = open(fileName.c_str(), O_RDONLY);
553     if (fd < 0) {
554         fd = open(fileName.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IROTH);
555     }
556     FileDescriptorStream fileStream(fd);
557     testString = "Hello!";
558     strSize = testString.size();
559     isFileStream = fileStream.WriteChunk(testString.data(), strSize);
560     EXPECT_TRUE(isFileStream);
561     std::remove(fileName.c_str());
562     close(fd);
563 }
564 
HWTEST_F_L0(HeapTrackerTest,AddNodeToTree)565 HWTEST_F_L0(HeapTrackerTest, AddNodeToTree)
566 {
567     CVector<uint32_t> traceNodeIndex;
568     for (int i = 0; i < 3; i++) {
569         traceNodeIndex.push_back(i + 1);
570     }
571     TraceTree traceTree;
572     TraceNode *traceNode = traceTree.AddNodeToTree(traceNodeIndex);
573     EXPECT_TRUE(traceNode != nullptr);
574 }
575 
HWTEST_F_L0(HeapTrackerTest,FindOrAddChild)576 HWTEST_F_L0(HeapTrackerTest, FindOrAddChild)
577 {
578     TraceTree traceTree;
579     uint32_t index = 1;
580     TraceNode traceNode(&traceTree, index);
581     TraceNode *node = traceNode.FindOrAddChild(index);
582     EXPECT_TRUE(node->GetNodeIndex() == 1);
583 
584     TraceNode *tmpNode = traceNode.FindOrAddChild(2);
585     EXPECT_TRUE(tmpNode->GetNodeIndex() == 2);
586 }
587 }  // namespace panda::test
588