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