• 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     EXPECT_EQ(runtime_->kernelSpaceMemMaps_.size(), lines);
146     for (const std::unique_ptr<SymbolsFile> &symbolsFile : runtime_->GetSymbolsFiles()) {
147         EXPECT_EQ(symbolsFile->GetBuildId().empty(), false);
148     }
149 }
150 
151 /**
152  * @tc.name: SetSymbolsPaths
153  * @tc.desc:
154  * @tc.type: FUNC
155  */
156 HWTEST_F(VirtualRuntimeTest, SetSymbolsPaths, TestSize.Level1)
157 {
158     std::vector<std::string> symbolsSearchPaths;
159     runtime_->SetSymbolsPaths(symbolsSearchPaths);
160 
161     symbolsSearchPaths.clear();
162     symbolsSearchPaths.push_back(PATH_DATA_TEMP);
163     symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
164     EXPECT_EQ(runtime_->SetSymbolsPaths(symbolsSearchPaths), true);
165 
166     symbolsSearchPaths.clear();
167     symbolsSearchPaths.push_back(PATH_DATA_TEMP);
168     symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
169     symbolsSearchPaths.push_back(PATH_DATA_TEMP);
170     symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
171     EXPECT_EQ(runtime_->SetSymbolsPaths(symbolsSearchPaths), true);
172 
173     symbolsSearchPaths.clear();
174     symbolsSearchPaths.push_back(PATH_DATA_TEMP);
175     symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
176     symbolsSearchPaths.push_back(PATH_DATA_TEMP);
177     symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
178     symbolsSearchPaths.push_back(PATH_DATA_TEMP);
179     symbolsSearchPaths.push_back(PATH_NOT_EXISTS);
180     EXPECT_EQ(runtime_->SetSymbolsPaths(symbolsSearchPaths), true);
181 }
182 
183 /**
184  * @tc.name: GetSymbolsFiles
185  * @tc.desc:
186  * @tc.type: FUNC
187  */
188 HWTEST_F(VirtualRuntimeTest, GetSymbolsFiles, TestSize.Level1)
189 {
190     EXPECT_EQ(runtime_->GetSymbolsFiles().size(), 0u);
191     runtime_->UpdateKernelSymbols();
192     if (access("/sys/kernel/notes", F_OK) == 0) {
193         EXPECT_EQ(runtime_->GetSymbolsFiles().size(), 1u);
194     } else {
195         printf("cannot access /sys/kernel/notes\n");
196     }
197 }
198 
199 /**
200  * @tc.name: SetCallStackExpend
201  * @tc.desc:
202  * @tc.type: FUNC
203  */
204 HWTEST_F(VirtualRuntimeTest, SetCallStackExpend, TestSize.Level1)
205 {
206     runtime_->SetCallStackExpend(true);
207     EXPECT_EQ(runtime_->callstackMergeLevel_, true);
208     runtime_->SetCallStackExpend(false);
209     EXPECT_EQ(runtime_->callstackMergeLevel_, false);
210 }
211 
212 /**
213  * @tc.name: SetDisableUnwind
214  * @tc.desc:
215  * @tc.type: FUNC
216  */
217 HWTEST_F(VirtualRuntimeTest, SetDisableUnwind, TestSize.Level1)
218 {
219     runtime_->SetDisableUnwind(true);
220     EXPECT_EQ(runtime_->disableUnwind_, true);
221     runtime_->SetDisableUnwind(false);
222     EXPECT_EQ(runtime_->disableUnwind_, false);
223 }
224 
225 namespace {
226 constexpr const pid_t testTid = 1;
227 constexpr const uint64_t testUserVaddr = 0x1000;
228 constexpr const uint64_t testKernelVaddr = testUserVaddr / 4;
229 constexpr const uint64_t testKernelLen = testUserVaddr / 2;
230 constexpr const uint64_t testUserMapBegin = 0x2000;
231 constexpr const uint64_t testUserMapLen = 0x4000;
232 } // namespace
233 
PrepareKernelSymbol()234 void VirtualRuntimeTest::PrepareKernelSymbol()
235 {
236     std::string kernelSymbol = "kernel_symbol";
237     auto kernel = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE);
238     kernel->filePath_ = kernelSymbol;
239     kernel->symbols_.emplace_back(testKernelVaddr, 1u, "first_kernel_func", kernel->filePath_);
240     kernel->symbols_.emplace_back(testKernelVaddr + 1u, 1u, "second_kernel_func",
241                                   kernel->filePath_);
242     runtime_->symbolsFiles_.emplace_back(std::move(kernel));
243 
244     auto &kernelMap = runtime_->kernelSpaceMemMaps_.emplace_back();
245     kernelMap.name_ = kernelSymbol;
246     kernelMap.begin_ = 0;
247     kernelMap.end_ = 0 + testKernelLen;
248     kernelMap.pageoffset_ = 0;
249 }
250 
PrepareUserSymbol()251 void VirtualRuntimeTest::PrepareUserSymbol()
252 {
253     std::string userSymbol = "user_symbol";
254     auto user = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE);
255     user->filePath_ = userSymbol;
256     user->symbols_.emplace_back(testUserVaddr, 1u, "first_user_func", user->filePath_);
257     user->symbols_.emplace_back(testUserVaddr + 1u, 1u, "second_user_func", user->filePath_);
258     user->textExecVaddrFileOffset_ = testUserVaddr;
259     user->textExecVaddr_ = testUserVaddr;
260     user->debugInfoLoadResult_ = true;
261     runtime_->symbolsFiles_.emplace_back(std::move(user));
262 
263     VirtualThread &thread = runtime_->GetThread(testTid, testTid);
264     thread.CreateMapItem(userSymbol, testUserMapBegin, testUserMapLen, 0);
265 }
266 
267 /**
268  * @tc.name: GetSymbol
269  * @tc.desc:
270  * @tc.type: FUNC
271  */
272 HWTEST_F(VirtualRuntimeTest, GetSymbol, TestSize.Level1)
273 {
274     Symbol symbol;
275     PrepareKernelSymbol();
276     PrepareUserSymbol();
277 
278     ScopeDebugLevel tempLogLevel(LEVEL_MUCH);
279 
280     symbol = runtime_->GetSymbol(0u, testTid, testTid);
281     EXPECT_EQ(symbol.isValid(), false);
282 
283     symbol = runtime_->GetSymbol(testKernelVaddr, testTid, testTid);
284     // in kernel
285     EXPECT_EQ(symbol.isValid(), true);
286     EXPECT_EQ(symbol.funcVaddr_, testKernelVaddr);
287     EXPECT_STREQ(symbol.name_.data(), "first_kernel_func");
288 
289     symbol = runtime_->GetSymbol(testUserVaddr + testUserMapBegin, testTid, testTid);
290     // in user
291     EXPECT_EQ(symbol.isValid(), true);
292     EXPECT_EQ(symbol.funcVaddr_, testUserVaddr);
293     EXPECT_STREQ(symbol.name_.data(), "first_user_func");
294 }
295 
296 /**
297  * @tc.name: GetThread
298  * @tc.desc:
299  * @tc.type: FUNC
300  */
301 HWTEST_F(VirtualRuntimeTest, GetThread, TestSize.Level1)
302 {
303     runtime_->GetThread(1, 2);
304     runtime_->GetThread(3, 4);
305     runtime_->GetThread(5, 6);
306     // runtime have 0 thread, so here need +1u
307     EXPECT_EQ(runtime_->GetThreads().size(), 7u);
308     if (HasFailure()) {
309         for (auto &pair : runtime_->GetThreads()) {
310             printf("pid %d tid %d\n", pair.second.pid_, pair.second.tid_);
311         }
312     }
313 }
314 
315 /**
316  * @tc.name: UpdateFromPerfData
317  * @tc.desc:
318  * @tc.type: FUNC
319  */
320 HWTEST_F(VirtualRuntimeTest, UpdateFromPerfData, TestSize.Level1)
321 {
322     std::vector<SymbolFileStruct> symbolFileStructs;
323     SymbolFileStruct &symbolFileStruct = symbolFileStructs.emplace_back();
324     symbolFileStruct.filePath_ = "a";
325     symbolFileStruct.textExecVaddr_ = testUserVaddr;
326     symbolFileStruct.textExecVaddrFileOffset_ = testUserVaddr;
327     symbolFileStruct.buildId_ = "b";
328     symbolFileStruct.symbolStructs_.emplace_back(testUserVaddr, 1u, "first_user_func");
329     symbolFileStruct.symbolStructs_.emplace_back(testUserVaddr + 1u, 1u, "second_user_func");
330 
331     runtime_->UpdateFromPerfData(symbolFileStructs);
332     ASSERT_EQ(runtime_->GetSymbolsFiles().size(), 1u);
333     ASSERT_STREQ(runtime_->GetSymbolsFiles().front()->filePath_.c_str(), "a");
334     ASSERT_STREQ(runtime_->GetSymbolsFiles().front()->GetBuildId().c_str(), "b");
335     ASSERT_EQ(runtime_->GetSymbolsFiles().front()->GetSymbols().size(), 2u);
336 }
337 
338 /**
339  * @tc.name: UnwindFromRecord
340  * @tc.desc:
341  * @tc.type: FUNC
342  */
343 HWTEST_F(VirtualRuntimeTest, UnwindFromRecord, TestSize.Level1)
344 {
345     // symbol
346     auto &symbolsFile = runtime_->symbolsFiles_.emplace_back(
347         SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_DWARF_ELF));
348     ASSERT_EQ(symbolsFile->setSymbolsFilePath(PATH_RESOURCE_TEST_DWARF_DATA), true);
349     ASSERT_EQ(symbolsFile->LoadSymbols(TEST_DWARF_ELF), true);
350     symbolsFile->filePath_ = TEST_DWARF_ELF;
351 
352     // thread
353     VirtualThread &thread = runtime_->GetThread(TEST_DWARF_RECORD_PID, TEST_DWARF_RECORD_TID);
354     MakeMaps(thread);
355 
356     // record
357     std::vector<uint8_t> data;
358     LoadFromFile(PATH_RESOURCE_TEST_DWARF_DATA + TEST_DWARF_RECORD, data);
359     ASSERT_NE(data.size(), 0u);
360     perf_event_attr attr;
361     if (memset_s(&attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) {
362         printf("error: memset_s failed.");
363         return;
364     }
365     attr.sample_type = TEST_RECORD_SAMPLE_TYPE;
366     attr.sample_regs_user = TEST_DWARF_RECORD_REGS_USER;
367     PerfRecordSample sample(data.data(), attr);
368     sample.DumpLog("UnwindFromRecord");
369     ASSERT_EQ(sample.data_.stack_size, TEST_DWARF_RECORD_STACK_SIZE);
370 
371     // unwind
372     runtime_->UnwindFromRecord(sample);
373     ASSERT_LE(TEST_RECORD_CALLSTACK_IP_FUNC.size(), sample.callFrames_.size());
374     for (size_t i = 0; i < TEST_RECORD_CALLSTACK_IP_FUNC.size(); i++) {
375         EXPECT_EQ(TEST_RECORD_CALLSTACK_IP_FUNC[i].first, sample.callFrames_[i].vaddrInFile_);
376         EXPECT_STREQ(TEST_RECORD_CALLSTACK_IP_FUNC[i].second.data(),
377                      sample.callFrames_[i].symbolName_.data());
378     }
379 }
380 } // namespace HiPerf
381 } // namespace Developtools
382 } // namespace OHOS
383