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