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 "virtual_runtime_test.h"
17
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <link.h>
21 #include <random>
22 #include <sys/mman.h>
23
24 #include <hilog/log.h>
25
26 #include "symbols_file_test.h"
27
28 using namespace testing::ext;
29 using namespace std;
30 using namespace OHOS::HiviewDFX;
31 namespace OHOS {
32 namespace Developtools {
33 namespace HiPerf {
34 class VirtualRuntimeTest : public testing::Test {
35 public:
36 static void SetUpTestCase(void);
37 static void TearDownTestCase(void);
38 void SetUp();
39 void TearDown();
40 const std::string TEST_LOG_MESSAGE = "<HELLO_TEST_LOG_MESSAGE>";
41 void LogLevelTest(std::vector<std::string> args, DebugLevel level);
42 default_random_engine rnd_;
43 std::unique_ptr<VirtualRuntime> runtime_;
44 bool RecordCallBack(std::unique_ptr<PerfEventRecord> record);
45 size_t callbackCount_ = 0;
46
47 void PrepareKernelSymbol();
48 void PrepareUserSymbol();
49 };
50
SetUpTestCase()51 void VirtualRuntimeTest::SetUpTestCase()
52 {
53 DebugLogger::GetInstance()->OpenLog(DEFAULT_UT_LOG_DIR + "VirtualRuntimeTest.txt");
54 }
55
TearDownTestCase()56 void VirtualRuntimeTest::TearDownTestCase()
57 {
58 DebugLogger::GetInstance()->RestoreLog();
59 }
60
SetUp()61 void VirtualRuntimeTest::SetUp()
62 {
63 runtime_ = std::make_unique<VirtualRuntime>();
64 callbackCount_ = 0;
65 }
66
TearDown()67 void VirtualRuntimeTest::TearDown()
68 {
69 runtime_.release();
70 }
71
RecordCallBack(std::unique_ptr<PerfEventRecord> record)72 bool VirtualRuntimeTest::RecordCallBack(std::unique_ptr<PerfEventRecord> record)
73 {
74 callbackCount_++;
75 printf("callbackCount_ %zu: type %d\n", callbackCount_, record->GetType());
76 return true;
77 }
78
79 /**
80 * @tc.name: VirtualRuntimeTest
81 * @tc.desc:
82 * @tc.type: FUNC
83 */
84 HWTEST_F(VirtualRuntimeTest, VirtualRuntimeTest, TestSize.Level1) {}
85
86 /**
87 * @tc.name: SetRecordMode
88 * @tc.desc:
89 * @tc.type: FUNC
90 */
91 HWTEST_F(VirtualRuntimeTest, SetRecordMode, TestSize.Level1)
92 {
93 auto callBack = std::bind(&VirtualRuntimeTest::RecordCallBack, this, std::placeholders::_1);
94
95 EXPECT_EQ(runtime_->recordCallBack_, nullptr);
96 runtime_->SetRecordMode(callBack);
97 EXPECT_NE(runtime_->recordCallBack_, nullptr);
98 }
99
100 /**
101 * @tc.name: SetRecordMode
102 * @tc.desc:
103 * @tc.type: FUNC
104 */
105 HWTEST_F(VirtualRuntimeTest, UpdateFromRecord, TestSize.Level1)
106 {
107 HLOGD("Func2:%s", __FUNCTION__);
108 PerfRecordComm recordComm(false, -1, -2, "3");
109 PerfEventRecord &record = static_cast<PerfEventRecord &>(recordComm);
110 auto callBack = std::bind(&VirtualRuntimeTest::RecordCallBack, this, std::placeholders::_1);
111
112 runtime_->SetRecordMode(callBack);
113 runtime_->UpdateFromRecord(record);
114 // one is -1 the other is -2
115 EXPECT_EQ(callbackCount_, 2u);
116 }
117
118 /**
119 * @tc.name: UpdateKernelSymbols
120 * @tc.desc:
121 * @tc.type: FUNC
122 */
123 HWTEST_F(VirtualRuntimeTest, UpdateKernelSymbols, TestSize.Level1)
124 {
125 runtime_->UpdateKernelSymbols();
126 if (access("/sys/kernel/notes", F_OK) == 0) {
127 EXPECT_EQ(runtime_->symbolsFiles_.size(), 1u);
128 } else {
129 printf("cannot access /sys/kernel/notes\n");
130 }
131 }
132
133 /**
134 * @tc.name: UpdateKernelModulesSymbols
135 * @tc.desc:
136 * @tc.type: FUNC
137 */
138 HWTEST_F(VirtualRuntimeTest, UpdateKernelModulesSymbols, TestSize.Level1)
139 {
140 runtime_->UpdateKernelModulesSpaceMaps();
141 runtime_->UpdateKernelModulesSymbols();
142 std::string modulesMap = ReadFileToString("/proc/modules");
143 size_t lines = std::count(modulesMap.begin(), modulesMap.end(), '\n');
144 std::set<std::string> modulesCount;
145 if (runtime_->kernelSpaceMemMaps_.size() == 0) {
146 lines = 0;
147 }
148 EXPECT_EQ(runtime_->kernelSpaceMemMaps_.size(), lines);
149 int hasBuildId = 0;
150 int noBuildId = 0;
151 for (const std::unique_ptr<SymbolsFile> &symbolsFile : runtime_->GetSymbolsFiles()) {
152 if (symbolsFile->GetBuildId().empty()) {
153 noBuildId++;
154 } else {
155 hasBuildId++;
156 }
157 }
158 printf("no BuildId: %d hasBuildId: %d\n", noBuildId, hasBuildId);
159 }
160
161 /**
162 * @tc.name: SetSymbolsPaths
163 * @tc.desc:
164 * @tc.type: FUNC
165 */
166 HWTEST_F(VirtualRuntimeTest, SetSymbolsPaths, TestSize.Level1)
167 {
168 std::vector<std::string> symbolsSearchPaths;
169 runtime_->SetSymbolsPaths(symbolsSearchPaths);
170
171 symbolsSearchPaths.clear();
172 symbolsSearchPaths.push_back(PATH_DATA_TEMP);
173 symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
174 EXPECT_EQ(runtime_->SetSymbolsPaths(symbolsSearchPaths), true);
175
176 symbolsSearchPaths.clear();
177 symbolsSearchPaths.push_back(PATH_DATA_TEMP);
178 symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
179 symbolsSearchPaths.push_back(PATH_DATA_TEMP);
180 symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
181 EXPECT_EQ(runtime_->SetSymbolsPaths(symbolsSearchPaths), true);
182
183 symbolsSearchPaths.clear();
184 symbolsSearchPaths.push_back(PATH_DATA_TEMP);
185 symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
186 symbolsSearchPaths.push_back(PATH_DATA_TEMP);
187 symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
188 symbolsSearchPaths.push_back(PATH_DATA_TEMP);
189 symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
190 EXPECT_EQ(runtime_->SetSymbolsPaths(symbolsSearchPaths), true);
191 }
192
193 /**
194 * @tc.name: GetSymbolsFiles
195 * @tc.desc:
196 * @tc.type: FUNC
197 */
198 HWTEST_F(VirtualRuntimeTest, GetSymbolsFiles, TestSize.Level1)
199 {
200 EXPECT_EQ(runtime_->GetSymbolsFiles().size(), 0u);
201 runtime_->UpdateKernelSymbols();
202 if (access("/sys/kernel/notes", F_OK) == 0) {
203 EXPECT_EQ(runtime_->GetSymbolsFiles().size(), 1u);
204 } else {
205 printf("cannot access /sys/kernel/notes\n");
206 }
207 }
208
209 /**
210 * @tc.name: SetCallStackExpend
211 * @tc.desc:
212 * @tc.type: FUNC
213 */
214 HWTEST_F(VirtualRuntimeTest, SetCallStackExpend, TestSize.Level1)
215 {
216 runtime_->SetCallStackExpend(true);
217 EXPECT_EQ(runtime_->callstackMergeLevel_, true);
218 runtime_->SetCallStackExpend(false);
219 EXPECT_EQ(runtime_->callstackMergeLevel_, false);
220 }
221
222 /**
223 * @tc.name: SetDisableUnwind
224 * @tc.desc:
225 * @tc.type: FUNC
226 */
227 HWTEST_F(VirtualRuntimeTest, SetDisableUnwind, TestSize.Level1)
228 {
229 runtime_->SetDisableUnwind(true);
230 EXPECT_EQ(runtime_->disableUnwind_, true);
231 runtime_->SetDisableUnwind(false);
232 EXPECT_EQ(runtime_->disableUnwind_, false);
233 }
234
235 namespace {
236 constexpr const pid_t testTid = 1;
237 constexpr const uint64_t testUserVaddr = 0x1000;
238 constexpr const uint64_t testKernelVaddr = testUserVaddr / 4;
239 constexpr const uint64_t testKernelLen = testUserVaddr / 2;
240 constexpr const uint64_t testUserMapBegin = 0x2000;
241 constexpr const uint64_t testUserMapLen = 0x4000;
242 } // namespace
243
PrepareKernelSymbol()244 void VirtualRuntimeTest::PrepareKernelSymbol()
245 {
246 std::string kernelSymbol = "kernel_symbol";
247 auto kernel = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE);
248 kernel->filePath_ = kernelSymbol;
249 kernel->symbols_.emplace_back(testKernelVaddr, 1u, "first_kernel_func", kernel->filePath_);
250 kernel->symbols_.emplace_back(testKernelVaddr + 1u, 1u, "second_kernel_func",
251 kernel->filePath_);
252 runtime_->symbolsFiles_.emplace_back(std::move(kernel));
253
254 auto &kernelMap = runtime_->kernelSpaceMemMaps_.emplace_back();
255 kernelMap.name = kernelSymbol;
256 kernelMap.begin = 0;
257 kernelMap.end = 0 + testKernelLen;
258 kernelMap.offset = 0;
259 }
260
PrepareUserSymbol()261 void VirtualRuntimeTest::PrepareUserSymbol()
262 {
263 std::string userSymbol = "user_symbol";
264 auto user = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE);
265 user->filePath_ = userSymbol;
266 user->symbols_.emplace_back(testUserVaddr, 1u, "first_user_func", user->filePath_);
267 user->symbols_.emplace_back(testUserVaddr + 1u, 1u, "second_user_func", user->filePath_);
268 user->textExecVaddrFileOffset_ = testUserVaddr;
269 user->textExecVaddr_ = testUserVaddr;
270 user->debugInfoLoadResult_ = true;
271 runtime_->symbolsFiles_.emplace_back(std::move(user));
272
273 VirtualThread &thread = runtime_->GetThread(testTid, testTid);
274 thread.CreateMapItem(userSymbol, testUserMapBegin, testUserMapLen, 0);
275 }
276
277 /**
278 * @tc.name: GetSymbol
279 * @tc.desc:
280 * @tc.type: FUNC
281 */
282 HWTEST_F(VirtualRuntimeTest, GetSymbol, TestSize.Level1)
283 {
284 DfxSymbol symbol;
285 PrepareKernelSymbol();
286 PrepareUserSymbol();
287
288 ScopeDebugLevel tempLogLevel(LEVEL_MUCH);
289
290 symbol = runtime_->GetSymbol(0u, testTid, testTid);
291 EXPECT_EQ(symbol.IsValid(), false);
292
293 symbol = runtime_->GetSymbol(testKernelVaddr, testTid, testTid);
294 // in kernel
295 EXPECT_EQ(symbol.IsValid(), true);
296 EXPECT_EQ(symbol.funcVaddr_, testKernelVaddr);
297 EXPECT_STREQ(symbol.name_.data(), "first_kernel_func");
298
299 symbol = runtime_->GetSymbol(testUserVaddr + testUserMapBegin, testTid, testTid);
300 // in user
301 EXPECT_EQ(symbol.IsValid(), true);
302 EXPECT_EQ(symbol.funcVaddr_, testUserVaddr);
303 EXPECT_STREQ(symbol.name_.data(), "first_user_func");
304 }
305
306 /**
307 * @tc.name: GetThread
308 * @tc.desc:
309 * @tc.type: FUNC
310 */
311 HWTEST_F(VirtualRuntimeTest, GetThread, TestSize.Level1)
312 {
313 runtime_->GetThread(1, 2);
314 runtime_->GetThread(3, 4);
315 runtime_->GetThread(5, 6);
316 // runtime have 0 thread, so here need +1u
317 EXPECT_EQ(runtime_->GetThreads().size(), 7u);
318 if (HasFailure()) {
319 for (auto &pair : runtime_->GetThreads()) {
320 printf("pid %d tid %d\n", pair.second.pid_, pair.second.tid_);
321 }
322 }
323 }
324
325 /**
326 * @tc.name: UpdateFromPerfData
327 * @tc.desc:
328 * @tc.type: FUNC
329 */
330 HWTEST_F(VirtualRuntimeTest, UpdateFromPerfData, TestSize.Level1)
331 {
332 std::vector<SymbolFileStruct> symbolFileStructs;
333 SymbolFileStruct &symbolFileStruct = symbolFileStructs.emplace_back();
334 symbolFileStruct.filePath_ = "a";
335 symbolFileStruct.textExecVaddr_ = testUserVaddr;
336 symbolFileStruct.textExecVaddrFileOffset_ = testUserVaddr;
337 symbolFileStruct.buildId_ = "b";
338 symbolFileStruct.symbolStructs_.emplace_back(testUserVaddr, 1u, "first_user_func");
339 symbolFileStruct.symbolStructs_.emplace_back(testUserVaddr + 1u, 1u, "second_user_func");
340
341 runtime_->UpdateFromPerfData(symbolFileStructs);
342 ASSERT_EQ(runtime_->GetSymbolsFiles().size(), 1u);
343 ASSERT_STREQ(runtime_->GetSymbolsFiles().front()->filePath_.c_str(), "a");
344 ASSERT_STREQ(runtime_->GetSymbolsFiles().front()->GetBuildId().c_str(), "b");
345 ASSERT_EQ(runtime_->GetSymbolsFiles().front()->GetSymbols().size(), 2u);
346 }
347
348 /**
349 * @tc.name: UnwindFromRecord
350 * @tc.desc:
351 * @tc.type: FUNC
352 */
353 HWTEST_F(VirtualRuntimeTest, UnwindFromRecord, TestSize.Level1)
354 {
355 // symbol
356 auto &symbolsFile = runtime_->symbolsFiles_.emplace_back(
357 SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_DWARF_ELF));
358 ASSERT_EQ(symbolsFile->setSymbolsFilePath(PATH_RESOURCE_TEST_DWARF_DATA), true);
359 ASSERT_EQ(symbolsFile->LoadSymbols(nullptr, TEST_DWARF_ELF), true);
360 symbolsFile->filePath_ = TEST_DWARF_ELF;
361
362 // thread
363 VirtualThread &thread = runtime_->GetThread(TEST_DWARF_RECORD_PID, TEST_DWARF_RECORD_TID);
364 MakeMaps(thread);
365
366 // record
367 std::vector<uint8_t> data;
368 LoadFromFile(PATH_RESOURCE_TEST_DWARF_DATA + TEST_DWARF_RECORD, data);
369 ASSERT_NE(data.size(), 0u);
370 perf_event_attr attr;
371 if (memset_s(&attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) {
372 printf("error: memset_s failed.");
373 return;
374 }
375 attr.sample_type = TEST_RECORD_SAMPLE_TYPE;
376 attr.sample_regs_user = TEST_DWARF_RECORD_REGS_USER;
377 PerfRecordSample sample(data.data(), attr);
378 sample.DumpLog("UnwindFromRecord");
379 ASSERT_EQ(sample.data_.stack_size, TEST_DWARF_RECORD_STACK_SIZE);
380
381 // unwind
382 runtime_->UnwindFromRecord(sample);
383 ASSERT_LE(TEST_RECORD_CALLSTACK_IP_FUNC.size(), sample.callFrames_.size());
384 for (size_t i = 0; i < TEST_RECORD_CALLSTACK_IP_FUNC.size(); i++) {
385 EXPECT_EQ(TEST_RECORD_CALLSTACK_IP_FUNC[i].first, sample.callFrames_[i].vaddrInFile_);
386 EXPECT_STREQ(TEST_RECORD_CALLSTACK_IP_FUNC[i].second.data(),
387 sample.callFrames_[i].symbolName_.data());
388 }
389 }
390 } // namespace HiPerf
391 } // namespace Developtools
392 } // namespace OHOS
393