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_thread_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 #include "get_thread_id.h"
24 #include "symbols_file_test.h"
25 #include "virtual_runtime.h"
26 #include "virtual_thread_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 NativeDaemon {
34 class VirtualThreadTest : 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 static std::string myFilePath_;
GetFullPath()44 static std::string GetFullPath()
45 {
46 char path[PATH_MAX];
47 int i = readlink("/proc/self/exe", path, sizeof(path));
48 path[i] = '\0';
49 return path;
50 }
51 static int PhdrCallBack(struct dl_phdr_info *info, size_t size, void *data);
52 static void MakeMapsFromDlpi(const std::string &dlpiName, const struct dl_phdr_info *info,
53 std::vector<MemMapItem> &phdrMaps);
54 };
55
56 std::string VirtualThreadTest::myFilePath_;
57
SetUpTestCase()58 void VirtualThreadTest::SetUpTestCase() {}
59
TearDownTestCase()60 void VirtualThreadTest::TearDownTestCase() {}
61
SetUp()62 void VirtualThreadTest::SetUp() {}
63
TearDown()64 void VirtualThreadTest::TearDown() {}
65
66 /**
67 * @tc.name: MemMapItem Same
68 * @tc.desc:
69 * @tc.type: FUNC
70 */
71 HWTEST_F(VirtualThreadTest, MemMapItemSame, TestSize.Level1)
72 {
73 MemMapItem a;
74 EXPECT_EQ(a == "a", false);
75 EXPECT_EQ(a == "b", false);
76 EXPECT_EQ(a == "", true);
77
78 a.name_ = "a";
79 EXPECT_EQ(a == "a", true);
80 EXPECT_EQ(a == "b", false);
81
82 a.name_ = "a1";
83 EXPECT_EQ(a == "a", false);
84 EXPECT_EQ(a == "b", false);
85
86 a.name_ = "1";
87 EXPECT_EQ(a == "a", false);
88 EXPECT_EQ(a == "b", false);
89
90 a.name_ = "";
91 EXPECT_EQ(a == "a", false);
92 EXPECT_EQ(a == "b", false);
93 }
94
95 /**
96 * @tc.name: MemMapItem FileOffsetFromAddr
97 * @tc.desc:
98 * @tc.type: FUNC
99 */
100 HWTEST_F(VirtualThreadTest, MemMapItemFileOffsetFromAddr, TestSize.Level1)
101 {
102 MemMapItem a;
103
104 a.begin_ = 100;
105 a.pageoffset_ = 200;
106 EXPECT_EQ(a.FileOffsetFromAddr(0), 100u);
107 EXPECT_EQ(a.FileOffsetFromAddr(10), 110u);
108 EXPECT_EQ(a.FileOffsetFromAddr(100), 200u);
109 EXPECT_EQ(a.FileOffsetFromAddr(1000), 1100u);
110
111 a.begin_ = 300;
112 a.pageoffset_ = 200;
113 EXPECT_EQ(a.FileOffsetFromAddr(0), std::numeric_limits<uint64_t>::max() - 100u + 1u);
114 EXPECT_EQ(a.FileOffsetFromAddr(10), std::numeric_limits<uint64_t>::max() - 90u + 1u);
115 EXPECT_EQ(a.FileOffsetFromAddr(100), 0u);
116 EXPECT_EQ(a.FileOffsetFromAddr(1000), 900u);
117 }
118
119 /**
120 * @tc.name: MemMapItem ToString
121 * @tc.desc:
122 * @tc.type: FUNC
123 */
124 HWTEST_F(VirtualThreadTest, MemMapItemFileToString, TestSize.Level1)
125 {
126 MemMapItem a;
127
128 a.begin_ = 0x100;
129 a.pageoffset_ = 0x200;
130 EXPECT_EQ(a.ToString().find("100") != std::string::npos, true);
131 EXPECT_EQ(a.ToString().find("200") != std::string::npos, true);
132 EXPECT_EQ(a.ToString().find("300") != std::string::npos, false);
133
134 a.begin_ = 0x300;
135 a.pageoffset_ = 0x200;
136 EXPECT_EQ(a.ToString().find("100") != std::string::npos, false);
137 EXPECT_EQ(a.ToString().find("200") != std::string::npos, true);
138 EXPECT_EQ(a.ToString().find("300") != std::string::npos, true);
139 }
140
MakeMapsFromDlpi(const std::string & dlpiName,const struct dl_phdr_info * info,std::vector<MemMapItem> & phdrMaps)141 void VirtualThreadTest::MakeMapsFromDlpi(const std::string &dlpiName,
142 const struct dl_phdr_info *info,
143 std::vector<MemMapItem> &phdrMaps)
144 {
145 int phdrType;
146 HLOGV("Name: \"%s\" (%d segments)", dlpiName.c_str(), info->dlpi_phnum);
147 for (int i = 0; i < info->dlpi_phnum; i++) {
148 phdrType = info->dlpi_phdr[i].p_type;
149 if (phdrType != PT_LOAD) {
150 continue;
151 }
152 HLOGV(" %2d: [%14p; memsz:%7jx] align %jx flags: %#jx [(%#x)]", i,
153 reinterpret_cast<void *>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr),
154 static_cast<uintmax_t>(info->dlpi_phdr[i].p_memsz),
155 static_cast<uintmax_t>(info->dlpi_phdr[i].p_align),
156 static_cast<uintmax_t>(info->dlpi_phdr[i].p_flags), phdrType);
157
158 MemMapItem &item = phdrMaps.emplace_back();
159 item.begin_ = (info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
160 item.end_ = RoundUp(item.begin_ + info->dlpi_phdr[i].p_memsz, info->dlpi_phdr[i].p_align);
161 item.pageoffset_ = info->dlpi_phdr[i].p_offset;
162 item.name_ = dlpiName;
163 }
164 for (const MemMapItem &item : phdrMaps) {
165 HLOGV("%s", item.ToString().c_str());
166 }
167 EXPECT_NE(phdrMaps.size(), 0u);
168 }
169
PhdrCallBack(struct dl_phdr_info * info,size_t size,void * data)170 int VirtualThreadTest::PhdrCallBack(struct dl_phdr_info *info, size_t size, void *data)
171 {
172 VirtualThread *thread = static_cast<VirtualThread *>(data);
173 std::vector<MemMapItem> phdrMaps {};
174 std::vector<const MemMapItem *> memMaps {};
175 static std::string myFilePath = GetFullPath();
176 EXPECT_NE(thread->GetMaps().size(), 0u);
177 std::string dlpiName = info->dlpi_name;
178 if (StringStartsWith(dlpiName, "./") and !StringEndsWith(dlpiName, ".so")) {
179 dlpiName = myFilePath;
180 }
181 if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
182 // dont care empty pt
183 return 0;
184 } else {
185 MakeMapsFromDlpi(dlpiName, info, phdrMaps);
186 }
187
188 for (const MemMapItem &item : thread->GetMaps()) {
189 if (item.name_ == dlpiName) {
190 HLOGV("%s", item.ToString().c_str());
191 memMaps.emplace_back(&item);
192 }
193 }
194
195 if (memMaps.size() == 0u) {
196 // show all the items if not any match mapitem found
197 for (const MemMapItem &item : thread->GetMaps()) {
198 HLOGV("%s", item.ToString().c_str());
199 }
200 return 0;
201 }
202
203 if (memMaps.size() == phdrMaps.size()) {
204 EXPECT_EQ(memMaps.front()->begin_, phdrMaps.front().begin_);
205 EXPECT_EQ(memMaps.front()->pageoffset_, phdrMaps.front().pageoffset_);
206 }
207 return 0;
208 }
209
210 /**
211 * @tc.name: ParseMap
212 * @tc.desc:
213 * @tc.type: FUNC
214 */
215 HWTEST_F(VirtualThreadTest, ParseMap, TestSize.Level1)
216 {
217 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> files;
218
219 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
220 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(),
221 get_thread_id(), files, runtime.get());
222
223 dl_iterate_phdr(PhdrCallBack, static_cast<void *>(thread.get()));
224
225 for (const MemMapItem &item : thread->GetMaps()) {
226 EXPECT_EQ(item.name_.empty(), false);
227 EXPECT_STRNE(item.name_.c_str(), MMAP_NAME_HEAP.c_str());
228 EXPECT_STRNE(item.name_.c_str(), MMAP_NAME_ANON.c_str());
229 }
230 }
231
232 /**
233 * @tc.name: CreateMapItem
234 * @tc.desc:
235 * @tc.type: FUNC
236 */
237 HWTEST_F(VirtualThreadTest, CreateMapItem, TestSize.Level1)
238 {
239 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> files;
240 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
241 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), get_thread_id(),
242 files, runtime.get(), false);
243 const std::vector<MemMapItem> &maps = thread->GetMaps();
244
245 fprintf(stderr, "map size = %zd\n", maps.size());
246
247 thread->CreateMapItem("0.so", 1000, 2000, 3000);
248 thread->CreateMapItem("1.so", 3000, 4000, 5000);
249 thread->CreateMapItem("2.so", 10000, 20000, 30000);
250
251 EXPECT_EQ(maps.size(), 3u);
252 EXPECT_EQ(maps.at(0).begin_, 1000u);
253 EXPECT_EQ(maps.at(1).begin_, 3000u);
254 EXPECT_EQ(maps.at(2).begin_, 10000u);
255
256 EXPECT_EQ(maps.at(0).end_, 1000u + 2000u);
257 EXPECT_EQ(maps.at(1).end_, 3000u + 4000u);
258 EXPECT_EQ(maps.at(2).end_, 10000u + 20000u);
259
260 EXPECT_EQ(maps.at(0).pageoffset_, 3000u);
261 EXPECT_EQ(maps.at(1).pageoffset_, 5000u);
262 EXPECT_EQ(maps.at(2).pageoffset_, 30000u);
263
264 EXPECT_STREQ(maps.at(0).name_.c_str(), "0.so");
265 EXPECT_STREQ(maps.at(1).name_.c_str(), "1.so");
266 EXPECT_STREQ(maps.at(2).name_.c_str(), "2.so");
267 }
268
269 /**
270 * @tc.name: InheritMaps
271 * @tc.desc:
272 * @tc.type: FUNC
273 */
274 HWTEST_F(VirtualThreadTest, InheritMaps, TestSize.Level1)
275 {
276 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> files;
277
278 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
279 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(),
280 get_thread_id(), files, runtime.get());
281
282 std::shared_ptr<VirtualThread> thread2 = std::make_shared<VirtualThread>(getpid(),
283 get_thread_id() + 1, files, runtime.get());
284
285 const std::vector<MemMapItem> &maps = thread->GetMaps();
286 const std::vector<MemMapItem> &maps2 = thread2->GetMaps();
287
288 ASSERT_EQ(maps.size(), maps2.size());
289 for (size_t i = 0; i < maps.size(); i++) {
290 EXPECT_STREQ(maps[i].ToString().c_str(), maps2[i].ToString().c_str());
291 }
292
293 size_t oldSize = thread->GetMaps().size();
294 thread->CreateMapItem("new", 0u, 1u, 2u);
295 size_t newSize = thread->GetMaps().size();
296 ASSERT_EQ(oldSize, newSize);
297 ASSERT_EQ(maps.size(), maps2.size());
298 for (size_t i = 0; i < maps.size(); i++) {
299 EXPECT_STREQ(maps[i].ToString().c_str(), maps2[i].ToString().c_str());
300 }
301 }
302
303 /**
304 * @tc.name: FindMapByAddr
305 * @tc.desc:
306 * @tc.type: FUNC
307 */
308 HWTEST_F(VirtualThreadTest, FindMapByAddr, TestSize.Level1)
309 {
310 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> files;
311 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
312 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), get_thread_id(),
313 files, runtime.get(), false);
314
315 thread->CreateMapItem("0.so", 1000u, 2000u, 3000u);
316 thread->CreateMapItem("1.so", 3000u, 4000u, 5000u);
317 thread->CreateMapItem("2.so", 10000u, 20000u, 30000u);
318
319 const MemMapItem *outMap;
320 outMap = thread->FindMapByAddr(0000u);
321 EXPECT_EQ(outMap != nullptr, false);
322
323 outMap = thread->FindMapByAddr(1000u);
324 ASSERT_EQ(outMap != nullptr, true);
325 EXPECT_EQ(outMap->begin_, 1000u);
326
327 outMap = thread->FindMapByAddr(2000u);
328 ASSERT_EQ(outMap != nullptr, true);
329 EXPECT_EQ(outMap->begin_, 1000u);
330
331 outMap = thread->FindMapByAddr(2999u);
332 ASSERT_EQ(outMap != nullptr, true);
333 EXPECT_EQ(outMap->begin_, 1000u);
334
335 outMap = thread->FindMapByAddr(3000u);
336 ASSERT_EQ(outMap != nullptr, true);
337 EXPECT_EQ(outMap->begin_, 3000u);
338
339 EXPECT_EQ(thread->FindMapByAddr(30000u - 1u) != nullptr, true);
340 EXPECT_EQ(thread->FindMapByAddr(30000u) != nullptr, false);
341 EXPECT_EQ(thread->FindMapByAddr(30000u + 1u) != nullptr, false);
342 }
343
344 /**
345 * @tc.name: FindMapByFileInfo
346 * @tc.desc:
347 * @tc.type: FUNC
348 */
349 HWTEST_F(VirtualThreadTest, FindMapByFileInfo, TestSize.Level1)
350 {
351 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> files;
352 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
353 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), get_thread_id(),
354 files, runtime.get(), false);
355
356 thread->CreateMapItem("0.so", 1000u, 2000u, 3000u);
357 thread->CreateMapItem("1.so", 3000u, 4000u, 5000u);
358 thread->CreateMapItem("2.so", 10000u, 20000u, 30000u);
359
360 const MemMapItem *outMap;
361 EXPECT_EQ(thread->FindMapByFileInfo("", 0000u), nullptr);
362 EXPECT_EQ(thread->FindMapByFileInfo("0.so", 0000u), nullptr);
363
364 EXPECT_EQ(thread->FindMapByFileInfo("1.so", 3000u), nullptr);
365 ASSERT_NE(outMap = thread->FindMapByFileInfo("0.so", 3000u), nullptr);
366 EXPECT_EQ(outMap->begin_, 1000u);
367
368 EXPECT_EQ(thread->FindMapByFileInfo("1.so", 4000u), nullptr);
369 ASSERT_NE(outMap = thread->FindMapByFileInfo("0.so", 4000u), nullptr);
370 EXPECT_EQ(outMap->begin_, 1000u);
371
372 EXPECT_EQ(thread->FindMapByFileInfo("1.so", 4999u), nullptr);
373 ASSERT_NE(outMap = thread->FindMapByFileInfo("0.so", 4999u), nullptr);
374 EXPECT_EQ(outMap->begin_, 1000u);
375
376 EXPECT_EQ(thread->FindMapByFileInfo("0.so", 5000u), nullptr);
377 ASSERT_NE(outMap = thread->FindMapByFileInfo("1.so", 5000u), nullptr);
378 EXPECT_EQ(outMap->begin_, 3000u);
379
380 EXPECT_EQ(thread->FindMapByFileInfo("1.so", 50000u - 1), nullptr);
381 EXPECT_EQ(thread->FindMapByFileInfo("x.so", 50000u - 1), nullptr);
382 EXPECT_NE(thread->FindMapByFileInfo("2.so", 50000u - 1), nullptr);
383 EXPECT_EQ(thread->FindMapByFileInfo("2.so", 50000u), nullptr);
384 EXPECT_EQ(thread->FindMapByFileInfo("2.so", 50000u + 1), nullptr);
385 }
386
387 /**
388 * @tc.name: FindSymbolsFileByMap
389 * @tc.desc:
390 * @tc.type: FUNC
391 */
392 HWTEST_F(VirtualThreadTest, FindSymbolsFileByMap, TestSize.Level1)
393 {
394 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> files;
395 files.insert(std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, "1.elf"));
396 files.insert(std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, "2.elf"));
397 files.insert(std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, "3.elf"));
398
399 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
400 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), get_thread_id(),
401 files, runtime.get(), false);
402
403 MemMapItem inMap;
404
405 inMap.name_ = "";
406 EXPECT_EQ(thread->FindSymbolsFileByMap(inMap), nullptr);
407
408 inMap.name_ = "1";
409 EXPECT_EQ(thread->FindSymbolsFileByMap(inMap), nullptr);
410
411 inMap.name_ = "1.elf";
412 ASSERT_NE(thread->FindSymbolsFileByMap(inMap), nullptr);
413 EXPECT_STREQ(thread->FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap.name_.c_str());
414
415 inMap.name_ = "2.elf";
416 ASSERT_NE(thread->FindSymbolsFileByMap(inMap), nullptr);
417 EXPECT_STREQ(thread->FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap.name_.c_str());
418
419 inMap.name_ = "3.elf";
420 ASSERT_NE(thread->FindSymbolsFileByMap(inMap), nullptr);
421 EXPECT_STREQ(thread->FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap.name_.c_str());
422 }
423
424 /**
425 * @tc.name: ReadRoMemory
426 * @tc.desc:
427 * @tc.type: FUNC
428 */
429 HWTEST_F(VirtualThreadTest, ReadRoMemory, TestSize.Level1)
430 {
431 std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile> symbolsFiles;
432 std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
433 std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), get_thread_id(),
434 symbolsFiles, runtime.get(), false);
435 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(TEST_FILE_ELF_FULL_PATH.c_str(), "rb"),
436 fclose);
437 if (fp) {
438 struct stat sb = {};
439 if (fstat(fileno(fp.get()), &sb) == -1) {
440 HLOGE("unable to check the file size");
441 } else {
442 HLOGV("file stat size %" PRIu64 "", sb.st_size);
443 }
444
445 thread->CreateMapItem(TEST_FILE_ELF_FULL_PATH, 0u, sb.st_size, 0u);
446 ASSERT_EQ(thread->GetMaps().size(), 1u);
447
448 std::unique_ptr<SymbolsFile> symbolsFile =
449 SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_FILE_ELF_FULL_PATH);
450 ASSERT_NE(symbolsFile, nullptr);
451 ASSERT_EQ(symbolsFile->LoadSymbols(), true);
452
453 // add to symbols list
454 symbolsFiles.insert(std::move(symbolsFile));
455
456 uint8_t freadByte = '\0';
457 uint8_t readRoByte = '\0';
458 uint64_t addr = 0x0;
459
460 // first byte
461 ASSERT_EQ(fread(&freadByte, 1, 1, fp.get()), 1u);
462
463 const MemMapItem *map = thread->FindMapByAddr(addr);
464 ASSERT_EQ(map != nullptr, true);
465 if (HasFailure()) {
466 printf("map: %s\n", thread->GetMaps().at(0).ToString().c_str());
467 }
468
469 EXPECT_NE(thread->FindSymbolsFileByMap(*map), nullptr);
470 if (HasFailure()) {
471 printf("symbols: %s\n", thread->symbolsFiles_.begin()->get()->filePath_.c_str());
472 }
473
474 ASSERT_EQ(thread->ReadRoMemory(addr++, &readRoByte, 1u), true);
475 ASSERT_EQ(freadByte, readRoByte);
476
477 while (fread(&freadByte, 1, 1, fp.get()) == 1u) {
478 ASSERT_EQ(thread->ReadRoMemory(addr++, &readRoByte, 1u), true);
479 ASSERT_EQ(freadByte, readRoByte);
480 }
481
482 // EOF , out of file size should return 0
483 ASSERT_EQ(thread->ReadRoMemory(addr++, &readRoByte, 1u), false);
484 }
485 }
486 } // namespace NativeDaemon
487 } // namespace Developtools
488 } // namespace OHOS
489