• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Level0)
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     ASSERT_EQ(runtime_->SetSymbolsPaths({"/data/local/tmp"}), true);
333     runtime_->UpdateFromPerfData(symbolFileStructs);
334     ASSERT_EQ(runtime_->GetSymbolsFiles().size(), 1u);
335     ASSERT_STREQ(runtime_->GetSymbolsFiles().front()->filePath_.c_str(), "a");
336     ASSERT_STREQ(runtime_->GetSymbolsFiles().front()->GetBuildId().c_str(), "b");
337     ASSERT_EQ(runtime_->GetSymbolsFiles().front()->GetSymbols().size(), 2u);
338 }
339 
340 /**
341  * @tc.name: UnwindFromRecord
342  * @tc.desc:
343  * @tc.type: FUNC
344  */
345 HWTEST_F(VirtualRuntimeTest, UnwindFromRecord, TestSize.Level0)
346 {
347     // symbol
348     auto &symbolsFile = runtime_->symbolsFiles_.emplace_back(
349         SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_DWARF_ELF));
350     ASSERT_EQ(symbolsFile->setSymbolsFilePath(PATH_RESOURCE_TEST_DWARF_DATA), true);
351     ASSERT_EQ(symbolsFile->LoadSymbols(nullptr, TEST_DWARF_ELF), true);
352     symbolsFile->filePath_ = TEST_DWARF_ELF;
353 
354     // thread
355     VirtualThread &thread = runtime_->GetThread(TEST_DWARF_RECORD_PID, TEST_DWARF_RECORD_TID);
356     MakeMaps(thread);
357 
358     // record
359     std::vector<uint8_t> data;
360     LoadFromFile(PATH_RESOURCE_TEST_DWARF_DATA + TEST_DWARF_RECORD, data);
361     ASSERT_NE(data.size(), 0u);
362     perf_event_attr attr;
363     if (memset_s(&attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != 0) {
364         printf("memset_s perf_event_attr failed");
365     }
366     attr.sample_type = TEST_RECORD_SAMPLE_TYPE;
367     attr.sample_regs_user = TEST_DWARF_RECORD_REGS_USER;
368     PerfRecordSample sample;
369     sample.Init(data.data(), attr);
370     sample.DumpLog("UnwindFromRecord");
371     ASSERT_EQ(sample.data_.stack_size, TEST_DWARF_RECORD_STACK_SIZE);
372 
373     // unwind
374     runtime_->UnwindFromRecord(sample);
375     ASSERT_LE(TEST_RECORD_CALLSTACK_IP_FUNC.size(), sample.callFrames_.size());
376     for (size_t i = 0; i < TEST_RECORD_CALLSTACK_IP_FUNC.size(); i++) {
377         EXPECT_EQ(TEST_RECORD_CALLSTACK_IP_FUNC[i].first, sample.callFrames_[i].funcOffset);
378         EXPECT_STREQ(TEST_RECORD_CALLSTACK_IP_FUNC[i].second.data(),
379                      sample.callFrames_[i].funcName.data());
380     }
381 }
382 
383 /**
384  * @tc.name: Update
385  * @tc.desc: Test update maps and symbols
386  * @tc.type: FUNC
387  */
388 HWTEST_F(VirtualRuntimeTest, Update, TestSize.Level2)
389 {
390     EXPECT_EQ(runtime_->IsKernelThread(0), false);
391     runtime_->UpdateServiceSpaceMaps();
392     runtime_->UpdateDevhostSpaceMaps();
393     runtime_->UpdateServiceSymbols();
394     runtime_->UpdateDevhostSymbols();
395 }
396 
397 /**
398  * @tc.name: ClearSymbolCache
399  * @tc.desc: Test Clear Symbol Cache
400  * @tc.type: FUNC
401  */
402 HWTEST_F(VirtualRuntimeTest, ClearSymbolCache, TestSize.Level2)
403 {
404     VirtualRuntime runtime;
405     std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles = {};
406     VirtualThread virtualThread(1, symbolsFiles);
407     runtime.userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(1),
408                                     std::forward_as_tuple(1, symbolsFiles));
409     runtime.kernelSpaceMemMaps_ = {{}, {}};
410     runtime.processStackMap_ = {{1, nullptr}, {2, nullptr}};
411     runtime.symbolsPaths_ = {"abc", "def"};
412     DfxSymbol symbol;
413     runtime.userSymbolCache_.reserve(1);
414     runtime.userSymbolCache_[0] = symbol;
415     runtime.kernelSymbolCache_[0] = symbol;
416     runtime.kThreadSymbolCache_[0] = symbol;
417     runtime.ClearSymbolCache();
418     EXPECT_EQ(runtime.userSpaceThreadMap_.size(), 0);
419     EXPECT_EQ(runtime.kernelSpaceMemMaps_.size(), 0);
420     EXPECT_EQ(runtime.processStackMap_.size(), 0);
421     EXPECT_EQ(runtime.symbolsFiles_.size(), 0);
422     EXPECT_EQ(runtime.userSymbolCache_.size(), 0);
423     EXPECT_EQ(runtime.kernelSymbolCache_.size(), 0);
424     EXPECT_EQ(runtime.kThreadSymbolCache_.size(), 0);
425     EXPECT_EQ(runtime.symbolsPaths_.size(), 0);
426 }
427 
428 /**
429  * @tc.name: UpdateHapSymbolsWithNull
430  * @tc.desc: Test UpdateHapSymbols with null
431  * @tc.type: FUNC
432  */
433 HWTEST_F(VirtualRuntimeTest, UpdateHapSymbolsWithNull, TestSize.Level2)
434 {
435     EXPECT_FALSE(runtime_->UpdateHapSymbols(nullptr));
436 }
437 } // namespace HiPerf
438 } // namespace Developtools
439 } // namespace OHOS
440