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