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 
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 
49     void EndOfStream() override {}
50     int GetSize() override
51     {
52         static const int HEAP_PROFILER_CHUNK_SIZE = 100_KB;
53         return HEAP_PROFILER_CHUNK_SIZE;
54     }
55     bool WriteChunk([[maybe_unused]] char *data, [[maybe_unused]] int32_t size) override
56     {
57         return true;
58     }
59     bool WriteBinBlock(char *data, int32_t size) override
60     {
61         return WriteChunk(data, size);
62     }
63     bool Good() override
64     {
65         return testStream_.good();
66     }
67 
68     void UpdateHeapStats([[maybe_unused]] HeapStat* updateData, [[maybe_unused]] int32_t count) override
69     {
70     }
71 
72     void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId, [[maybe_unused]]int64_t timeStampUs) override
73     {
74     }
75 
Clear()76     void Clear()
77     {
78         testStream_.clear(std::ios::badbit);
79     }
80 
81 private:
82     std::fstream testStream_;
83 };
84 }
85 
86 namespace panda::test {
87 class HeapTrackerTest : 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 
99     void SetUp() override
100     {
101         TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
102         instance->SetEnableForceGC(false);
103     }
104 
105     void TearDown() override
106     {
107         TestHelper::DestroyEcmaVMWithScope(instance, scope);
108     }
109 
110     EcmaVM *instance {nullptr};
111     EcmaHandleScope *scope {nullptr};
112     JSThread *thread {nullptr};
113 };
114 
HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd)115 HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd)
116 {
117     [[maybe_unused]] EcmaHandleScope handleScope(thread);
118     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
119 
120     sleep(1);
121     int count = 100;
122     while (count-- > 0) {
123         instance->GetFactory()->NewJSAsyncFuncObject();
124     }
125     sleep(1);
126     count = 100;
127     while (count-- > 0) {
128         instance->GetFactory()->NewJSSymbol();
129     }
130     sleep(1);
131     count = 100;
132     while (count-- > 0) {
133         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
134         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
135         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
136     }
137 
138     // Create file test.heaptimeline
139     std::string fileName = "HeapTrackerTest3.heapsnapshot";
140     fstream outputString(fileName, std::ios::out);
141     outputString.close();
142     outputString.clear();
143 
144     FileStream stream(fileName.c_str());
145     CVector<HeapStat> statsBuffer;
146     statsBuffer.emplace_back(1, 2, 4);
147     stream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
148     stream.UpdateLastSeenObjectId(1, 1677567644913058);
149 
150     TestProgress testProgress;
151     DumpSnapShotOption dumpOption;
152     dumpOption.dumpFormat = DumpFormat::JSON;
153     dumpOption.isVmMode = true;
154     dumpOption.isPrivate = true;
155     dumpOption.captureNumericValue = false;
156     heapProfile->DumpHeapSnapshot(&stream, dumpOption, &testProgress);
157     StreamWriter streamWriter(&stream);
158     streamWriter.End();
159     HeapProfilerInterface::Destroy(instance);
160 
161     // Check
162     fstream inputStream(fileName, std::ios::in);
163     std::string line;
164     std::string nodes = "\"nodes\":[";
165     std::string sample = "\"samples\":[]";
166     uint32_t nodesSize = nodes.size();
167     uint32_t sampleSize = sample.size();
168     bool isNodesFind = false;
169     bool isSampleFind = false;
170     while (getline(inputStream, line)) {
171         if (line.substr(0U, nodesSize) == nodes) {
172             isNodesFind = true;
173         }
174 
175         if (line.substr(0U, sampleSize) == sample) {
176             isSampleFind = true;
177         }
178     }
179     ASSERT_TRUE(isNodesFind);
180     ASSERT_TRUE(isSampleFind);
181 
182     inputStream.close();
183     inputStream.clear();
184     std::remove(fileName.c_str());
185 }
186 
HWTEST_F_L0(HeapTrackerTest, GetStringByKey)187 HWTEST_F_L0(HeapTrackerTest, GetStringByKey)
188 {
189     StringKey key = static_cast<StringKey>(2);
190     StringHashMap stringHashMap(instance);
191     CString *hashMap = stringHashMap.GetStringByKey(key);
192     EXPECT_TRUE(hashMap == nullptr);
193 }
194 
HWTEST_F_L0(HeapTrackerTest, FormatString)195 HWTEST_F_L0(HeapTrackerTest, FormatString)
196 {
197     bool isVmMode = true;
198     bool isPrivate = false;
199     bool traceAllocation = false;
200     bool captureNumericValue = false;
201     HeapProfiler heapProfiler(instance);
202     DumpSnapShotOption dumpOption;
203     dumpOption.isVmMode = isVmMode;
204     dumpOption.isPrivate = isPrivate;
205     dumpOption.captureNumericValue = captureNumericValue;
206     HeapSnapshot heapSnapshot(instance, heapProfiler.GetEcmaStringTable(), dumpOption, traceAllocation,
207                               heapProfiler.GetEntryIdMap(), instance->GetChunk());
208 
209     StringHashMap stringHashMap(instance);
210     CString ret = "H\"e\rl\nl\\o\t W\fo\31rld!";
211     stringHashMap.GetString(ret);
212     StringKey retKey = std::hash<std::string>{} (std::string(ret));
213 
214     CString *tmpResult = nullptr;
215     tmpResult = stringHashMap.GetStringByKey(retKey);
216     EXPECT_TRUE(tmpResult != nullptr);
217     EXPECT_TRUE(*tmpResult == "H\"e\rl\nl\\o\t W\fo\31rld!");
218 }
219 
HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamWriteChunk)220 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamWriteChunk)
221 {
222     int32_t fd = -1;
223     FileDescriptorStream testFileStream(fd);
224     CVector<HeapStat> statsBuffer;
225     statsBuffer.emplace_back(1, 2, 4);
226     testFileStream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
227     testFileStream.UpdateLastSeenObjectId(1, 1677567644913058);
228     testFileStream.GetSize();
229     std::string testString = "Hello!";
230     int strSize = testString.size();
231     bool isFileStream = testFileStream.WriteChunk(testString.data(), strSize);
232     EXPECT_TRUE(!isFileStream);
233 
234     fd = 5;
235     FileDescriptorStream tmpFileStream(fd);
236     tmpFileStream.Good();
237     testString = "Hello!";
238     strSize = testString.size();
239     isFileStream = tmpFileStream.WriteChunk(testString.data(), strSize);
240     close(fd);
241     EXPECT_TRUE(!isFileStream);
242 
243     std::string fileName = "test.StreamWriteChunk";
244     fd = open(fileName.c_str(), O_RDONLY);
245     if (fd < 0) {
246         fd = open(fileName.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IROTH);
247     }
248     FileDescriptorStream fileStream(fd);
249     testString = "Hello!";
250     strSize = testString.size();
251     isFileStream = fileStream.WriteChunk(testString.data(), strSize);
252     EXPECT_TRUE(isFileStream);
253     std::remove(fileName.c_str());
254     close(fd);
255 }
256 
HWTEST_F_L0(HeapTrackerTest, AddNodeToTree)257 HWTEST_F_L0(HeapTrackerTest, AddNodeToTree)
258 {
259     CVector<uint32_t> traceNodeIndex;
260     for (int i = 0; i < 3; i++) {
261         traceNodeIndex.push_back(i + 1);
262     }
263     TraceTree traceTree;
264     TraceNode *traceNode = traceTree.AddNodeToTree(traceNodeIndex);
265     EXPECT_TRUE(traceNode != nullptr);
266 }
267 
HWTEST_F_L0(HeapTrackerTest, FindOrAddChild)268 HWTEST_F_L0(HeapTrackerTest, FindOrAddChild)
269 {
270     TraceTree traceTree;
271     uint32_t index = 1;
272     TraceNode traceNode(&traceTree, index);
273     TraceNode *node = traceNode.FindOrAddChild(index);
274     EXPECT_TRUE(node->GetNodeIndex() == 1);
275 
276     TraceNode *tmpNode = traceNode.FindOrAddChild(2);
277     EXPECT_TRUE(tmpNode->GetNodeIndex() == 2);
278 }
279 } // namespace panda::test
280