• 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.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