• 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 <gtest/gtest.h>
19 #include <link.h>
20 #include <random>
21 #include <sys/mman.h>
22 
23 #include <hilog/log.h>
24 
25 #include "symbols_file_test.h"
26 
27 using namespace testing::ext;
28 namespace OHOS {
29 namespace Developtools {
30 namespace HiPerf {
31 class VirtualThreadTest : public testing::Test {
32 public:
33     static void SetUpTestCase(void);
34     static void TearDownTestCase(void);
35     void SetUp();
36     void TearDown();
37     const std::string TEST_LOG_MESSAGE = "<HELLO_TEST_LOG_MESSAGE>";
38     void LogLevelTest(std::vector<std::string> args, DebugLevel level);
39     std::default_random_engine rnd_;
40     static std::string myFilePath_;
GetFullPath()41     static std::string GetFullPath()
42     {
43         char path[PATH_MAX];
44         int i = readlink("/proc/self/exe", path, sizeof(path));
45         path[i] = '\0';
46         return path;
47     }
48     static int PhdrCallBack(struct dl_phdr_info *info, size_t size, void *data);
49     static void MakeMapsFromDlpi(const std::string &dlpiName, const struct dl_phdr_info *info,
50                                  std::vector<DfxMap> &phdrMaps);
51 };
52 
53 std::string VirtualThreadTest::myFilePath_;
54 
SetUpTestCase()55 void VirtualThreadTest::SetUpTestCase()
56 {
57     DebugLogger::GetInstance()->OpenLog(DEFAULT_UT_LOG_DIR + "VirtualThreadTest.txt");
58 }
59 
TearDownTestCase()60 void VirtualThreadTest::TearDownTestCase()
61 {
62     DebugLogger::GetInstance()->RestoreLog();
63 }
64 
SetUp()65 void VirtualThreadTest::SetUp() {}
66 
TearDown()67 void VirtualThreadTest::TearDown() {}
68 
69 
MakeMapsFromDlpi(const std::string & dlpiName,const struct dl_phdr_info * info,std::vector<DfxMap> & phdrMaps)70 void VirtualThreadTest::MakeMapsFromDlpi(const std::string &dlpiName,
71                                          const struct dl_phdr_info *info,
72                                          std::vector<DfxMap> &phdrMaps)
73 {
74     if (info == nullptr) {
75         HLOGE("param is null");
76         return;
77     }
78     int phdrType;
79     HLOGV("Name: \"%s\" (%d segments)", dlpiName.c_str(), info->dlpi_phnum);
80     for (int i = 0; i < info->dlpi_phnum; i++) {
81         phdrType = info->dlpi_phdr[i].p_type;
82         if (phdrType != PT_LOAD) {
83             continue;
84         }
85         HLOGV("    %2d: [%14p; memsz:%7jx] align %jx flags: %#jx [(%#x)]", i,
86               (void *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr),
87               (uintmax_t)info->dlpi_phdr[i].p_memsz, (uintmax_t)info->dlpi_phdr[i].p_align,
88               (uintmax_t)info->dlpi_phdr[i].p_flags, phdrType);
89 
90         DfxMap &item = phdrMaps.emplace_back();
91         item.begin = (info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
92         item.end = RoundUp(item.begin + info->dlpi_phdr[i].p_memsz, info->dlpi_phdr[i].p_align);
93         item.offset = info->dlpi_phdr[i].p_offset;
94         item.name = dlpiName;
95     }
96     for (auto& item : phdrMaps) {
97         HLOGV("%s", item.ToString().c_str());
98     }
99     EXPECT_NE(phdrMaps.size(), 0u);
100 }
101 
PhdrCallBack(struct dl_phdr_info * info,size_t size,void * data)102 int VirtualThreadTest::PhdrCallBack(struct dl_phdr_info *info, size_t size, void *data)
103 {
104     if (info == nullptr || data == nullptr) {
105         HLOGE("param is null");
106         return -1;
107     }
108     VirtualThread *thread = static_cast<VirtualThread *>(data);
109     std::vector<DfxMap> phdrMaps {};
110     std::vector<std::shared_ptr<DfxMap>> memMaps {};
111     static std::string myFilePath = GetFullPath();
112     EXPECT_NE(thread->GetMaps().size(), 0u);
113     std::string dlpiName = info->dlpi_name;
114     if (StringStartsWith(dlpiName, "./") and !StringEndsWith(dlpiName, ".so")) {
115         dlpiName = myFilePath;
116     }
117     if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
118         // dont care empty pt
119         return 0;
120     } else {
121         MakeMapsFromDlpi(dlpiName, info, phdrMaps);
122     }
123 
124     for (auto item : thread->GetMaps()) {
125         if (item->name == dlpiName) {
126             HLOGV("%s", item->ToString().c_str());
127             memMaps.emplace_back(item);
128         }
129     }
130 
131     if (memMaps.size() == 0u) {
132         // show all the items if not any match mapitem found
133         for (auto item : thread->GetMaps()) {
134             HLOGV("%s", item->ToString().c_str());
135         }
136         return 0;
137     }
138 
139     if (memMaps.size() == phdrMaps.size()) {
140         EXPECT_EQ(memMaps.front()->begin, phdrMaps.front().begin);
141         EXPECT_EQ(memMaps.front()->offset, phdrMaps.front().offset);
142     }
143     return 0;
144 }
145 
146 /**
147  * @tc.name: ParseMap
148  * @tc.desc:
149  * @tc.type: FUNC
150  */
151 HWTEST_F(VirtualThreadTest, ParseMap, TestSize.Level2)
152 {
153     std::vector<std::unique_ptr<SymbolsFile>> files;
154     VirtualThread thread(getpid(), files);
155     thread.ParseMap();
156 
157     dl_iterate_phdr(PhdrCallBack, static_cast<void *>(&thread));
158 
159     for (auto item : thread.GetMaps()) {
160         EXPECT_EQ(item->name.empty(), false);
161         EXPECT_STRNE(item->name.c_str(), MMAP_NAME_HEAP.c_str());
162         EXPECT_STRNE(item->name.c_str(), MMAP_NAME_ANON.c_str());
163     }
164 }
165 
166 /**
167  * @tc.name: CreateMapItem
168  * @tc.desc:
169  * @tc.type: FUNC
170  */
171 HWTEST_F(VirtualThreadTest, CreateMapItem, TestSize.Level1)
172 {
173     std::vector<std::unique_ptr<SymbolsFile>> files;
174     VirtualThread thread(getpid(), files);
175     thread.CreateMapItem("0.so", 1000, 2000, 3000);
176     thread.CreateMapItem("1.so", 3000, 4000, 5000);
177     thread.CreateMapItem("2.so", 10000, 20000, 30000);
178 
179     auto& maps = thread.GetMaps();
180 
181     EXPECT_EQ(maps.size(), 3u);
182     EXPECT_EQ(maps.at(0)->begin, 1000u);
183     EXPECT_EQ(maps.at(1)->begin, 3000u);
184     EXPECT_EQ(maps.at(2)->begin, 10000u);
185 
186     EXPECT_EQ(maps.at(0)->end, 1000u + 2000u);
187     EXPECT_EQ(maps.at(1)->end, 3000u + 4000u);
188     EXPECT_EQ(maps.at(2)->end, 10000u + 20000u);
189 
190     EXPECT_EQ(maps.at(0)->offset, 3000u);
191     EXPECT_EQ(maps.at(1)->offset, 5000u);
192     EXPECT_EQ(maps.at(2)->offset, 30000u);
193 
194     EXPECT_STREQ(maps.at(0)->name.c_str(), "0.so");
195     EXPECT_STREQ(maps.at(1)->name.c_str(), "1.so");
196     EXPECT_STREQ(maps.at(2)->name.c_str(), "2.so");
197 }
198 
199 /**
200  * @tc.name: InheritMaps
201  * @tc.desc:
202  * @tc.type: FUNC
203  */
204 HWTEST_F(VirtualThreadTest, InheritMaps, TestSize.Level1)
205 {
206     std::vector<std::unique_ptr<SymbolsFile>> files;
207     VirtualThread thread(getpid(), files);
208     thread.ParseMap();
209 
210     VirtualThread thread2(getpid(), gettid() + 1u, thread, files);
211 
212     auto& maps = thread.GetMaps();
213     auto& maps2 = thread2.GetMaps();
214 
215     ASSERT_EQ(maps.size(), maps2.size());
216     for (size_t i = 0; i < maps.size(); i++) {
217         EXPECT_STREQ(maps[i]->ToString().c_str(), maps2[i]->ToString().c_str());
218     }
219 
220     size_t oldSize = thread.GetMaps().size();
221     thread.CreateMapItem("new", 0u, 1u, 2u); // update maps
222     size_t newSize = thread.GetMaps().size();
223     ASSERT_EQ(oldSize + 1, newSize);
224     ASSERT_EQ(maps.size(), maps2.size());
225     for (size_t i = 0; i < maps.size(); i++) {
226         EXPECT_STREQ(maps[i]->ToString().c_str(), maps2[i]->ToString().c_str());
227     }
228 }
229 
230 /**
231  * @tc.name: FindMapByAddr
232  * @tc.desc:
233  * @tc.type: FUNC
234  */
235 HWTEST_F(VirtualThreadTest, FindMapByAddr, TestSize.Level1)
236 {
237     std::vector<std::unique_ptr<SymbolsFile>> files;
238     VirtualThread thread(getpid(), files);
239 
240     thread.CreateMapItem("0.so", 1000u, 2000u, 3000u);
241     thread.CreateMapItem("1.so", 3000u, 4000u, 5000u);
242     thread.CreateMapItem("2.so", 10000u, 20000u, 30000u);
243 
244     std::shared_ptr<DfxMap> outMap = nullptr;
245     outMap = thread.FindMapByAddr(0000u);
246     EXPECT_EQ(outMap != nullptr, false);
247 
248     outMap = thread.FindMapByAddr(1000u);
249     ASSERT_EQ(outMap != nullptr, true);
250     EXPECT_EQ(outMap->begin, 1000u);
251 
252     outMap = thread.FindMapByAddr(2000u);
253     ASSERT_EQ(outMap != nullptr, true);
254     EXPECT_EQ(outMap->begin, 1000u);
255 
256     outMap = thread.FindMapByAddr(2999u);
257     ASSERT_EQ(outMap != nullptr, true);
258     EXPECT_EQ(outMap->begin, 1000u);
259 
260     outMap = thread.FindMapByAddr(3000u);
261     ASSERT_EQ(outMap != nullptr, true);
262     EXPECT_EQ(outMap->begin, 3000u);
263 
264     EXPECT_EQ(thread.FindMapByAddr(30000u - 1u) != nullptr, true);
265     EXPECT_EQ(thread.FindMapByAddr(30000u) != nullptr, false);
266     EXPECT_EQ(thread.FindMapByAddr(30000u + 1u) != nullptr, false);
267 }
268 
269 /**
270  * @tc.name: FindMapByFileInfo
271  * @tc.desc:
272  * @tc.type: FUNC
273  */
274 HWTEST_F(VirtualThreadTest, FindMapByFileInfo, TestSize.Level0)
275 {
276     std::vector<std::unique_ptr<SymbolsFile>> files;
277     VirtualThread thread(getpid(), files);
278 
279     thread.CreateMapItem("0.so", 1000u, 2000u, 3000u);
280     thread.CreateMapItem("1.so", 3000u, 4000u, 5000u);
281     thread.CreateMapItem("2.so", 10000u, 20000u, 30000u);
282 
283     std::shared_ptr<DfxMap> outMap = nullptr;
284     EXPECT_EQ(thread.FindMapByFileInfo("", 0000u), nullptr);
285     EXPECT_EQ(thread.FindMapByFileInfo("0.so", 0000u), nullptr);
286 
287     EXPECT_EQ(thread.FindMapByFileInfo("1.so", 3000u), nullptr);
288     ASSERT_NE(outMap = thread.FindMapByFileInfo("0.so", 3000u), nullptr);
289     EXPECT_EQ(outMap->begin, 1000u);
290 
291     EXPECT_EQ(thread.FindMapByFileInfo("1.so", 4000u), nullptr);
292     ASSERT_NE(outMap = thread.FindMapByFileInfo("0.so", 4000u), nullptr);
293     EXPECT_EQ(outMap->begin, 1000u);
294 
295     EXPECT_EQ(thread.FindMapByFileInfo("1.so", 4999u), nullptr);
296     ASSERT_NE(outMap = thread.FindMapByFileInfo("0.so", 4999u), nullptr);
297     EXPECT_EQ(outMap->begin, 1000u);
298 
299     EXPECT_EQ(thread.FindMapByFileInfo("0.so", 5000u), nullptr);
300     ASSERT_NE(outMap = thread.FindMapByFileInfo("1.so", 5000u), nullptr);
301     EXPECT_EQ(outMap->begin, 3000u);
302 
303     EXPECT_EQ(thread.FindMapByFileInfo("1.so", 50000u - 1), nullptr);
304     EXPECT_EQ(thread.FindMapByFileInfo("x.so", 50000u - 1), nullptr);
305     EXPECT_NE(thread.FindMapByFileInfo("2.so", 50000u - 1), nullptr);
306     EXPECT_EQ(thread.FindMapByFileInfo("2.so", 50000u), nullptr);
307     EXPECT_EQ(thread.FindMapByFileInfo("2.so", 50000u + 1), nullptr);
308 }
309 
310 /**
311  * @tc.name: FindSymbolsFileByMap
312  * @tc.desc:
313  * @tc.type: FUNC
314  */
315 HWTEST_F(VirtualThreadTest, FindSymbolsFileByMap, TestSize.Level0)
316 {
317     std::vector<std::unique_ptr<SymbolsFile>> files;
318     SymbolFileStruct symbolFileStruct;
319     symbolFileStruct.filePath_ = "1.elf";
320     files.emplace_back(SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct));
321     symbolFileStruct.filePath_ = "2.elf";
322     files.emplace_back(SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct));
323     symbolFileStruct.filePath_ = "3.elf";
324     files.emplace_back(SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct));
325     VirtualThread thread(getpid(), files);
326 
327     std::shared_ptr<DfxMap> inMap = std::make_shared<DfxMap>();
328 
329     inMap->name = "";
330     EXPECT_EQ(thread.FindSymbolsFileByMap(inMap), nullptr);
331 
332     inMap->name = "1";
333     EXPECT_EQ(thread.FindSymbolsFileByMap(inMap), nullptr);
334 
335     inMap->name = "1.elf";
336     ASSERT_NE(thread.FindSymbolsFileByMap(inMap), nullptr);
337     EXPECT_STREQ(thread.FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap->name.c_str());
338 
339     inMap->name = "2.elf";
340     inMap->symbolFileIndex = -1;
341     ASSERT_NE(thread.FindSymbolsFileByMap(inMap), nullptr);
342     EXPECT_STREQ(thread.FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap->name.c_str());
343 
344     inMap->name = "3.elf";
345     inMap->symbolFileIndex = -1;
346     ASSERT_NE(thread.FindSymbolsFileByMap(inMap), nullptr);
347     EXPECT_STREQ(thread.FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap->name.c_str());
348 }
349 
350 /**
351  * @tc.name: ReadRoMemory
352  * @tc.desc:
353  * @tc.type: FUNC
354  */
355 HWTEST_F(VirtualThreadTest, ReadRoMemory, TestSize.Level2)
356 {
357     std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles;
358     VirtualThread thread(getpid(), symbolsFiles);
359     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(TEST_FILE_ELF_FULL_PATH.c_str(), "rb"),
360                                                 fclose);
361     if (fp) {
362         struct stat sb = {};
363         if (fstat(fileno(fp.get()), &sb) == -1) {
364             HLOGE("unable to check the file size");
365         } else {
366             HLOGV("file stat size %" PRIu64 "", sb.st_size);
367         }
368 
369         thread.CreateMapItem(TEST_FILE_ELF_FULL_PATH, 0u, sb.st_size, 0u);
370         ASSERT_EQ(thread.GetMaps().size(), 1u);
371 
372         std::unique_ptr<SymbolsFile> symbolsFile =
373             SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_FILE_ELF_FULL_PATH);
374         ASSERT_NE(symbolsFile, nullptr);
375         ASSERT_EQ(symbolsFile->LoadSymbols(), true);
376 
377         // add to symbols list
378         symbolsFiles.emplace_back(std::move(symbolsFile));
379 
380         uint8_t freadByte = '\0';
381         uint8_t readRoByte = '\0';
382         uint64_t addr = 0x0;
383 
384         // first byte
385         ASSERT_EQ(fread(&freadByte, 1, 1, fp.get()), 1u);
386 
387         auto map = thread.FindMapByAddr(addr);
388         ASSERT_EQ(map != nullptr, true);
389         if (HasFailure()) {
390             printf("map: %s\n", thread.GetMaps().at(0)->ToString().c_str());
391         }
392 
393         EXPECT_NE(thread.FindSymbolsFileByMap(map), nullptr);
394         if (HasFailure()) {
395             printf("symbols: %s\n", thread.symbolsFiles_.front().get()->filePath_.c_str());
396         }
397 
398         ASSERT_EQ(thread.ReadRoMemory(addr++, &readRoByte, 1u), true);
399         ASSERT_EQ(freadByte, readRoByte);
400 
401         while (fread(&freadByte, 1, 1, fp.get()) == 1u) {
402             ASSERT_EQ(thread.ReadRoMemory(addr++, &readRoByte, 1u), true);
403             ASSERT_EQ(freadByte, readRoByte);
404         }
405 
406         // EOF , out of file size should return 0
407         ASSERT_EQ(thread.ReadRoMemory(addr++, &readRoByte, 1u), false);
408         thread.ParseServiceMap(TEST_FILE_ELF_FULL_PATH);
409         thread.ParseDevhostMap(getpid());
410     }
411 }
412 } // namespace HiPerf
413 } // namespace Developtools
414 } // namespace OHOS
415