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