• 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 
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     if (info == nullptr) {
152         HLOGE("param is null");
153         return;
154     }
155     int phdrType;
156     HLOGV("Name: \"%s\" (%d segments)", dlpiName.c_str(), info->dlpi_phnum);
157     for (int i = 0; i < info->dlpi_phnum; i++) {
158         phdrType = info->dlpi_phdr[i].p_type;
159         if (phdrType != PT_LOAD) {
160             continue;
161         }
162         HLOGV("    %2d: [%14p; memsz:%7jx] align %jx flags: %#jx [(%#x)]", i,
163               (void *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr),
164               (uintmax_t)info->dlpi_phdr[i].p_memsz, (uintmax_t)info->dlpi_phdr[i].p_align,
165               (uintmax_t)info->dlpi_phdr[i].p_flags, phdrType);
166 
167         MemMapItem &item = phdrMaps.emplace_back();
168         item.begin_ = (info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
169         item.end_ = RoundUp(item.begin_ + info->dlpi_phdr[i].p_memsz, info->dlpi_phdr[i].p_align);
170         item.pageoffset_ = info->dlpi_phdr[i].p_offset;
171         item.name_ = dlpiName;
172     }
173     for (const MemMapItem &item : phdrMaps) {
174         HLOGV("%s", item.ToString().c_str());
175     }
176     EXPECT_NE(phdrMaps.size(), 0u);
177 }
178 
PhdrCallBack(struct dl_phdr_info * info,size_t size,void * data)179 int VirtualThreadTest::PhdrCallBack(struct dl_phdr_info *info, size_t size, void *data)
180 {
181     if (info == nullptr || data == nullptr) {
182         HLOGE("param is null");
183         return -1;
184     }
185     VirtualThread *thread = static_cast<VirtualThread *>(data);
186     std::vector<MemMapItem> phdrMaps {};
187     std::vector<const MemMapItem *> memMaps {};
188     static std::string myFilePath = GetFullPath();
189     EXPECT_NE(thread->GetMaps().size(), 0u);
190     std::string dlpiName = info->dlpi_name;
191     if (StringStartsWith(dlpiName, "./") and !StringEndsWith(dlpiName, ".so")) {
192         dlpiName = myFilePath;
193     }
194     if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
195         // dont care empty pt
196         return 0;
197     } else {
198         MakeMapsFromDlpi(dlpiName, info, phdrMaps);
199     }
200 
201     for (const MemMapItem &item : thread->GetMaps()) {
202         if (item.name_ == dlpiName) {
203             HLOGV("%s", item.ToString().c_str());
204             memMaps.emplace_back(&item);
205         }
206     }
207 
208     if (memMaps.size() == 0u) {
209         // show all the items if not any match mapitem found
210         for (const MemMapItem &item : thread->GetMaps()) {
211             HLOGV("%s", item.ToString().c_str());
212         }
213         return 0;
214     }
215 
216     if (memMaps.size() == phdrMaps.size()) {
217         EXPECT_EQ(memMaps.front()->begin_, phdrMaps.front().begin_);
218         EXPECT_EQ(memMaps.front()->pageoffset_, phdrMaps.front().pageoffset_);
219     }
220     return 0;
221 }
222 
223 /**
224  * @tc.name: ParseMap
225  * @tc.desc:
226  * @tc.type: FUNC
227  */
228 HWTEST_F(VirtualThreadTest, ParseMap, TestSize.Level1)
229 {
230     std::vector<std::unique_ptr<SymbolsFile>> files;
231     VirtualThread thread(getpid(), files);
232     thread.ParseMap();
233 
234     dl_iterate_phdr(PhdrCallBack, static_cast<void *>(&thread));
235 
236     for (const MemMapItem &item : thread.GetMaps()) {
237         EXPECT_EQ(item.name_.empty(), false);
238         EXPECT_STRNE(item.name_.c_str(), MMAP_NAME_HEAP.c_str());
239         EXPECT_STRNE(item.name_.c_str(), MMAP_NAME_ANON.c_str());
240     }
241 }
242 
243 /**
244  * @tc.name: CreateMapItem
245  * @tc.desc:
246  * @tc.type: FUNC
247  */
248 HWTEST_F(VirtualThreadTest, CreateMapItem, TestSize.Level1)
249 {
250     std::vector<std::unique_ptr<SymbolsFile>> files;
251     VirtualThread thread(getpid(), files);
252     thread.CreateMapItem("0.so", 1000, 2000, 3000);
253     thread.CreateMapItem("1.so", 3000, 4000, 5000);
254     thread.CreateMapItem("2.so", 10000, 20000, 30000);
255 
256     const std::vector<MemMapItem> &maps = thread.GetMaps();
257 
258     EXPECT_EQ(maps.size(), 3u);
259     EXPECT_EQ(maps.at(0).begin_, 1000u);
260     EXPECT_EQ(maps.at(1).begin_, 3000u);
261     EXPECT_EQ(maps.at(2).begin_, 10000u);
262 
263     EXPECT_EQ(maps.at(0).end_, 1000u + 2000u);
264     EXPECT_EQ(maps.at(1).end_, 3000u + 4000u);
265     EXPECT_EQ(maps.at(2).end_, 10000u + 20000u);
266 
267     EXPECT_EQ(maps.at(0).pageoffset_, 3000u);
268     EXPECT_EQ(maps.at(1).pageoffset_, 5000u);
269     EXPECT_EQ(maps.at(2).pageoffset_, 30000u);
270 
271     EXPECT_STREQ(maps.at(0).name_.c_str(), "0.so");
272     EXPECT_STREQ(maps.at(1).name_.c_str(), "1.so");
273     EXPECT_STREQ(maps.at(2).name_.c_str(), "2.so");
274 }
275 
276 /**
277  * @tc.name: InheritMaps
278  * @tc.desc:
279  * @tc.type: FUNC
280  */
281 HWTEST_F(VirtualThreadTest, InheritMaps, TestSize.Level1)
282 {
283     std::vector<std::unique_ptr<SymbolsFile>> files;
284     VirtualThread thread(getpid(), files);
285     thread.ParseMap();
286 
287     VirtualThread thread2(getpid(), gettid() + 1u, thread, files);
288 
289     const std::vector<MemMapItem> &maps = thread.GetMaps();
290     const std::vector<MemMapItem> &maps2 = thread2.GetMaps();
291 
292     ASSERT_EQ(maps.size(), maps2.size());
293     for (size_t i = 0; i < maps.size(); i++) {
294         EXPECT_STREQ(maps[i].ToString().c_str(), maps2[i].ToString().c_str());
295     }
296 
297     size_t oldSize = thread.GetMaps().size();
298     thread.CreateMapItem("new", 0u, 1u, 2u);
299     size_t newSize = thread.GetMaps().size();
300     ASSERT_EQ(oldSize, newSize);
301     ASSERT_EQ(maps.size(), maps2.size());
302     for (size_t i = 0; i < maps.size(); i++) {
303         EXPECT_STREQ(maps[i].ToString().c_str(), maps2[i].ToString().c_str());
304     }
305 }
306 
307 /**
308  * @tc.name: FindMapByAddr
309  * @tc.desc:
310  * @tc.type: FUNC
311  */
312 HWTEST_F(VirtualThreadTest, FindMapByAddr, TestSize.Level1)
313 {
314     std::vector<std::unique_ptr<SymbolsFile>> files;
315     VirtualThread thread(getpid(), files);
316 
317     thread.CreateMapItem("0.so", 1000u, 2000u, 3000u);
318     thread.CreateMapItem("1.so", 3000u, 4000u, 5000u);
319     thread.CreateMapItem("2.so", 10000u, 20000u, 30000u);
320 
321     const MemMapItem *outMap;
322     outMap = thread.FindMapByAddr(0000u);
323     EXPECT_EQ(outMap != nullptr, false);
324 
325     outMap = thread.FindMapByAddr(1000u);
326     ASSERT_EQ(outMap != nullptr, true);
327     EXPECT_EQ(outMap->begin_, 1000u);
328 
329     outMap = thread.FindMapByAddr(2000u);
330     ASSERT_EQ(outMap != nullptr, true);
331     EXPECT_EQ(outMap->begin_, 1000u);
332 
333     outMap = thread.FindMapByAddr(2999u);
334     ASSERT_EQ(outMap != nullptr, true);
335     EXPECT_EQ(outMap->begin_, 1000u);
336 
337     outMap = thread.FindMapByAddr(3000u);
338     ASSERT_EQ(outMap != nullptr, true);
339     EXPECT_EQ(outMap->begin_, 3000u);
340 
341     EXPECT_EQ(thread.FindMapByAddr(30000u - 1u) != nullptr, true);
342     EXPECT_EQ(thread.FindMapByAddr(30000u) != nullptr, false);
343     EXPECT_EQ(thread.FindMapByAddr(30000u + 1u) != nullptr, false);
344 }
345 
346 /**
347  * @tc.name: FindMapByFileInfo
348  * @tc.desc:
349  * @tc.type: FUNC
350  */
351 HWTEST_F(VirtualThreadTest, FindMapByFileInfo, TestSize.Level1)
352 {
353     std::vector<std::unique_ptr<SymbolsFile>> files;
354     VirtualThread thread(getpid(), files);
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::vector<std::unique_ptr<SymbolsFile>> files;
395     SymbolFileStruct symbolFileStruct;
396     symbolFileStruct.filePath_ = "1.elf";
397     files.emplace_back(SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct));
398     symbolFileStruct.filePath_ = "2.elf";
399     files.emplace_back(SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct));
400     symbolFileStruct.filePath_ = "3.elf";
401     files.emplace_back(SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct));
402     VirtualThread thread(getpid(), files);
403 
404     MemMapItem inMap;
405 
406     inMap.name_ = "";
407     EXPECT_EQ(thread.FindSymbolsFileByMap(inMap), nullptr);
408 
409     inMap.name_ = "1";
410     EXPECT_EQ(thread.FindSymbolsFileByMap(inMap), nullptr);
411 
412     inMap.name_ = "1.elf";
413     ASSERT_NE(thread.FindSymbolsFileByMap(inMap), nullptr);
414     EXPECT_STREQ(thread.FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap.name_.c_str());
415 
416     inMap.name_ = "2.elf";
417     ASSERT_NE(thread.FindSymbolsFileByMap(inMap), nullptr);
418     EXPECT_STREQ(thread.FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap.name_.c_str());
419 
420     inMap.name_ = "3.elf";
421     ASSERT_NE(thread.FindSymbolsFileByMap(inMap), nullptr);
422     EXPECT_STREQ(thread.FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap.name_.c_str());
423 }
424 
425 /**
426  * @tc.name: ReadRoMemory
427  * @tc.desc:
428  * @tc.type: FUNC
429  */
430 HWTEST_F(VirtualThreadTest, ReadRoMemory, TestSize.Level1)
431 {
432     std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles;
433     VirtualThread thread(getpid(), symbolsFiles);
434     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(TEST_FILE_ELF_FULL_PATH.c_str(), "rb"),
435                                                 fclose);
436     if (fp) {
437         struct stat sb = {};
438         if (fstat(fileno(fp.get()), &sb) == -1) {
439             HLOGE("unable to check the file size");
440         } else {
441             HLOGV("file stat size %" PRIu64 "", sb.st_size);
442         }
443 
444         thread.CreateMapItem(TEST_FILE_ELF_FULL_PATH, 0u, sb.st_size, 0u);
445         ASSERT_EQ(thread.GetMaps().size(), 1u);
446 
447         std::unique_ptr<SymbolsFile> symbolsFile =
448             SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_FILE_ELF_FULL_PATH);
449         ASSERT_NE(symbolsFile, nullptr);
450         ASSERT_EQ(symbolsFile->LoadSymbols(), true);
451 
452         // add to symbols list
453         symbolsFiles.emplace_back(std::move(symbolsFile));
454 
455         uint8_t freadByte = '\0';
456         uint8_t readRoByte = '\0';
457         uint64_t addr = 0x0;
458 
459         // first byte
460         ASSERT_EQ(fread(&freadByte, 1, 1, fp.get()), 1u);
461 
462         const MemMapItem *map = thread.FindMapByAddr(addr);
463         ASSERT_EQ(map != nullptr, true);
464         if (HasFailure()) {
465             printf("map: %s\n", thread.GetMaps().at(0).ToString().c_str());
466         }
467 
468         EXPECT_NE(thread.FindSymbolsFileByMap(*map), nullptr);
469         if (HasFailure()) {
470             printf("symbols: %s\n", thread.symbolsFiles_.front().get()->filePath_.c_str());
471         }
472 
473         ASSERT_EQ(thread.ReadRoMemory(addr++, &readRoByte, 1u), true);
474         ASSERT_EQ(freadByte, readRoByte);
475 
476         while (fread(&freadByte, 1, 1, fp.get()) == 1u) {
477             ASSERT_EQ(thread.ReadRoMemory(addr++, &readRoByte, 1u), true);
478             ASSERT_EQ(freadByte, readRoByte);
479         }
480 
481         // EOF , out of file size should return 0
482         ASSERT_EQ(thread.ReadRoMemory(addr++, &readRoByte, 1u), false);
483     }
484 }
485 } // namespace HiPerf
486 } // namespace Developtools
487 } // namespace OHOS
488