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 HEAP_PROFILER_CHUNK_SIZE = 100_KB;
53 return HEAP_PROFILER_CHUNK_SIZE;
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,StreamWriterEnd)111 HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd)
112 {
113 [[maybe_unused]] EcmaHandleScope handleScope(thread);
114 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
115
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<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
130 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
131 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
132 }
133
134 // Create file test.heaptimeline
135 std::string fileName = "HeapTrackerTest3.heapsnapshot";
136 fstream outputString(fileName, std::ios::out);
137 outputString.close();
138 outputString.clear();
139
140 FileStream stream(fileName.c_str());
141 CVector<HeapStat> statsBuffer;
142 statsBuffer.emplace_back(1, 2, 4);
143 stream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
144 stream.UpdateLastSeenObjectId(1, 1677567644913058);
145
146 TestProgress testProgress;
147 heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
148 StreamWriter streamWriter(&stream);
149 streamWriter.End();
150 HeapProfilerInterface::Destroy(instance);
151
152 // Check
153 fstream inputStream(fileName, std::ios::in);
154 std::string line;
155 std::string nodes = "\"nodes\":[";
156 std::string sample = "\"samples\":[]";
157 uint32_t nodesSize = nodes.size();
158 uint32_t sampleSize = sample.size();
159 bool isNodesFind = false;
160 bool isSampleFind = false;
161 while (getline(inputStream, line)) {
162 if (line.substr(0U, nodesSize) == nodes) {
163 isNodesFind = true;
164 }
165
166 if (line.substr(0U, sampleSize) == sample) {
167 isSampleFind = true;
168 }
169 }
170 ASSERT_TRUE(isNodesFind);
171 ASSERT_TRUE(isSampleFind);
172
173 inputStream.close();
174 inputStream.clear();
175 std::remove(fileName.c_str());
176 }
177
HWTEST_F_L0(HeapTrackerTest,GetStringByKey)178 HWTEST_F_L0(HeapTrackerTest, GetStringByKey)
179 {
180 StringKey key = static_cast<StringKey>(2);
181 StringHashMap stringHashMap(instance);
182 CString *hashMap = stringHashMap.GetStringByKey(key);
183 EXPECT_TRUE(hashMap == nullptr);
184 }
185
HWTEST_F_L0(HeapTrackerTest,FormatString)186 HWTEST_F_L0(HeapTrackerTest, FormatString)
187 {
188 bool isVmMode = true;
189 bool isPrivate = false;
190 bool traceAllocation = false;
191 bool captureNumericValue = false;
192 HeapProfiler heapProfiler(instance);
193 HeapSnapshot heapSnapshot(instance, heapProfiler.GetEcmaStringTable(), isVmMode,
194 isPrivate, captureNumericValue, traceAllocation,
195 heapProfiler.GetEntryIdMap(), instance->GetChunk());
196
197 StringHashMap stringHashMap(instance);
198 CString ret = "H\"e\rl\nl\\o\t W\fo\31rld!";
199 stringHashMap.GetString(ret);
200 StringKey retKey = std::hash<std::string>{} (std::string(ret));
201
202 CString *tmpResult = nullptr;
203 tmpResult = stringHashMap.GetStringByKey(retKey);
204 EXPECT_TRUE(tmpResult != nullptr);
205 EXPECT_TRUE(*tmpResult == "H\"e\rl\nl\\o\t W\fo\31rld!");
206 }
207
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamWriteChunk)208 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamWriteChunk)
209 {
210 int32_t fd = -1;
211 FileDescriptorStream testFileStream(fd);
212 CVector<HeapStat> statsBuffer;
213 statsBuffer.emplace_back(1, 2, 4);
214 testFileStream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
215 testFileStream.UpdateLastSeenObjectId(1, 1677567644913058);
216 testFileStream.GetSize();
217 std::string testString = "Hello!";
218 int strSize = testString.size();
219 bool isFileStream = testFileStream.WriteChunk(testString.data(), strSize);
220 EXPECT_TRUE(!isFileStream);
221
222 fd = 5;
223 FileDescriptorStream tmpFileStream(fd);
224 tmpFileStream.Good();
225 testString = "Hello!";
226 strSize = testString.size();
227 isFileStream = tmpFileStream.WriteChunk(testString.data(), strSize);
228 close(fd);
229 EXPECT_TRUE(!isFileStream);
230
231 std::string fileName = "test.StreamWriteChunk";
232 fd = open(fileName.c_str(), O_RDONLY);
233 if (fd < 0) {
234 fd = open(fileName.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IROTH);
235 }
236 FileDescriptorStream fileStream(fd);
237 testString = "Hello!";
238 strSize = testString.size();
239 isFileStream = fileStream.WriteChunk(testString.data(), strSize);
240 EXPECT_TRUE(isFileStream);
241 std::remove(fileName.c_str());
242 close(fd);
243 }
244
HWTEST_F_L0(HeapTrackerTest,AddNodeToTree)245 HWTEST_F_L0(HeapTrackerTest, AddNodeToTree)
246 {
247 CVector<uint32_t> traceNodeIndex;
248 for (int i = 0; i < 3; i++) {
249 traceNodeIndex.push_back(i + 1);
250 }
251 TraceTree traceTree;
252 TraceNode *traceNode = traceTree.AddNodeToTree(traceNodeIndex);
253 EXPECT_TRUE(traceNode != nullptr);
254 }
255
HWTEST_F_L0(HeapTrackerTest,FindOrAddChild)256 HWTEST_F_L0(HeapTrackerTest, FindOrAddChild)
257 {
258 TraceTree traceTree;
259 uint32_t index = 1;
260 TraceNode traceNode(&traceTree, index);
261 TraceNode *node = traceNode.FindOrAddChild(index);
262 EXPECT_TRUE(node->GetNodeIndex() == 1);
263
264 TraceNode *tmpNode = traceNode.FindOrAddChild(2);
265 EXPECT_TRUE(tmpNode->GetNodeIndex() == 2);
266 }
267 } // namespace panda::test
268