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