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