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