• 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());
146 
147     dl_iterate_phdr(PhdrCallBack, static_cast<void *>(thread.get()));
148 }
149 
150 /**
151  * @tc.name: CreateMapItem
152  * @tc.desc:
153  * @tc.type: FUNC
154  */
155 HWTEST_F(VirtualThreadTest, CreateMapItem, TestSize.Level1)
156 {
157     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> files;
158     std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
159     std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), GetThreadId(),
160         files, runtime.get(), false);
161     const std::vector<std::shared_ptr<DfxMap>> &maps = thread->GetMaps();
162 
163     fprintf(stderr, "map size = %zd\n", maps.size());
164 
165     thread->CreateMapItem("0.so", 1000, 2000, 3000);
166     thread->CreateMapItem("1.so", 3000, 4000, 5000);
167     thread->CreateMapItem("2.so", 10000, 20000, 30000);
168 
169     EXPECT_EQ(maps.size(), 3u);
170     EXPECT_EQ(maps.at(0)->begin, 1000u);
171     EXPECT_EQ(maps.at(1)->begin, 3000u);
172     EXPECT_EQ(maps.at(2)->begin, 10000u);
173 
174     EXPECT_EQ(maps.at(0)->end, 1000u + 2000u);
175     EXPECT_EQ(maps.at(1)->end, 3000u + 4000u);
176     EXPECT_EQ(maps.at(2)->end, 10000u + 20000u);
177 
178     EXPECT_EQ(maps.at(0)->offset, 3000u);
179     EXPECT_EQ(maps.at(1)->offset, 5000u);
180     EXPECT_EQ(maps.at(2)->offset, 30000u);
181 
182     EXPECT_STREQ(maps.at(0)->name.c_str(), "0.so");
183     EXPECT_STREQ(maps.at(1)->name.c_str(), "1.so");
184     EXPECT_STREQ(maps.at(2)->name.c_str(), "2.so");
185 }
186 
187 /**
188  * @tc.name: InheritMaps
189  * @tc.desc:
190  * @tc.type: FUNC
191  */
192 HWTEST_F(VirtualThreadTest, InheritMaps, TestSize.Level1)
193 {
194     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> files;
195 
196     std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
197     std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(),
198         GetThreadId(), files, runtime.get());
199 
200     std::shared_ptr<VirtualThread> thread2 = std::make_shared<VirtualThread>(getpid(),
201         GetThreadId() + 1, files, runtime.get());
202 
203     const std::vector<std::shared_ptr<DfxMap>> &maps = thread->GetMaps();
204     const std::vector<std::shared_ptr<DfxMap>> &maps2 = thread2->GetMaps();
205     GTEST_LOG_(INFO) << maps.size();
206     GTEST_LOG_(INFO) << maps2.size();
207     ASSERT_EQ(maps.size(), maps2.size());
208     for (size_t i = 0; i < maps.size(); i++) {
209         EXPECT_STREQ(maps[i]->ToString().c_str(), maps2[i]->ToString().c_str());
210     }
211 
212     size_t oldSize = thread->GetMaps().size();
213     thread->CreateMapItem("new", 0u, 1u, 2u);
214     size_t newSize = thread->GetMaps().size();
215     ASSERT_GT(newSize, oldSize);
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 
222 /**
223  * @tc.name: FindMapByAddr
224  * @tc.desc:
225  * @tc.type: FUNC
226  */
227 HWTEST_F(VirtualThreadTest, FindMapByAddr, TestSize.Level1)
228 {
229     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> files;
230     std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
231     std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), GetThreadId(),
232         files, runtime.get(), false);
233 
234     thread->CreateMapItem("0.so", 1000u, 2000u, 3000u);
235     thread->CreateMapItem("1.so", 3000u, 4000u, 5000u);
236     thread->CreateMapItem("2.so", 10000u, 20000u, 30000u);
237 
238     std::shared_ptr<DfxMap> outMap = nullptr;
239     outMap = thread->FindMapByAddr(0000u);
240     EXPECT_EQ(outMap != nullptr, false);
241 
242     outMap = thread->FindMapByAddr(1000u);
243     ASSERT_EQ(outMap != nullptr, true);
244     EXPECT_EQ(outMap->begin, 1000u);
245 
246     outMap = thread->FindMapByAddr(2000u);
247     ASSERT_EQ(outMap != nullptr, true);
248     EXPECT_EQ(outMap->begin, 1000u);
249 
250     outMap = thread->FindMapByAddr(2999u);
251     ASSERT_EQ(outMap != nullptr, true);
252     EXPECT_EQ(outMap->begin, 1000u);
253 
254     outMap = thread->FindMapByAddr(3000u);
255     ASSERT_EQ(outMap != nullptr, true);
256     EXPECT_EQ(outMap->begin, 3000u);
257 
258     EXPECT_EQ(thread->FindMapByAddr(30000u - 1u) != nullptr, true);
259     EXPECT_EQ(thread->FindMapByAddr(30000u) != nullptr, false);
260     EXPECT_EQ(thread->FindMapByAddr(30000u + 1u) != nullptr, false);
261 }
262 
263 /**
264  * @tc.name: FindMapByFileInfo
265  * @tc.desc:
266  * @tc.type: FUNC
267  */
268 HWTEST_F(VirtualThreadTest, FindMapByFileInfo, TestSize.Level1)
269 {
270     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> files;
271     std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
272     std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), GetThreadId(),
273         files, runtime.get(), false);
274 
275     thread->CreateMapItem("0.so", 1000u, 2000u, 3000u);
276     thread->CreateMapItem("1.so", 3000u, 4000u, 5000u);
277     thread->CreateMapItem("2.so", 10000u, 20000u, 30000u);
278 
279     std::shared_ptr<DfxMap> outMap = nullptr;
280     EXPECT_EQ(thread->FindMapByFileInfo("", 0000u), nullptr);
281     EXPECT_EQ(thread->FindMapByFileInfo("0.so", 0000u), nullptr);
282 
283     EXPECT_EQ(thread->FindMapByFileInfo("1.so", 3000u), nullptr);
284     ASSERT_NE(outMap = thread->FindMapByFileInfo("0.so", 3000u), nullptr);
285     EXPECT_EQ(outMap->begin, 1000u);
286 
287     EXPECT_EQ(thread->FindMapByFileInfo("1.so", 4000u), nullptr);
288     ASSERT_NE(outMap = thread->FindMapByFileInfo("0.so", 4000u), nullptr);
289     EXPECT_EQ(outMap->begin, 1000u);
290 
291     EXPECT_EQ(thread->FindMapByFileInfo("1.so", 4999u), nullptr);
292     ASSERT_NE(outMap = thread->FindMapByFileInfo("0.so", 4999u), nullptr);
293     EXPECT_EQ(outMap->begin, 1000u);
294 
295     EXPECT_EQ(thread->FindMapByFileInfo("0.so", 5000u), nullptr);
296     ASSERT_NE(outMap = thread->FindMapByFileInfo("1.so", 5000u), nullptr);
297     EXPECT_EQ(outMap->begin, 3000u);
298 
299     EXPECT_EQ(thread->FindMapByFileInfo("1.so", 50000u - 1), nullptr);
300     EXPECT_EQ(thread->FindMapByFileInfo("x.so", 50000u - 1), nullptr);
301     EXPECT_NE(thread->FindMapByFileInfo("2.so", 50000u - 1), nullptr);
302     EXPECT_EQ(thread->FindMapByFileInfo("2.so", 50000u), nullptr);
303     EXPECT_EQ(thread->FindMapByFileInfo("2.so", 50000u + 1), nullptr);
304 }
305 
306 /**
307  * @tc.name: FindSymbolsFileByMap
308  * @tc.desc:
309  * @tc.type: FUNC
310  */
311 HWTEST_F(VirtualThreadTest, FindSymbolsFileByMap, TestSize.Level1)
312 {
313     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> files;
314     files["1.elf"] = std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, "1.elf");
315     files["2.elf"] = std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, "2.elf");
316     files["3.elf"] = std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, "3.elf");
317 
318     std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
319     std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), GetThreadId(),
320         files, runtime.get(), false);
321 
322     std::shared_ptr<DfxMap> inMap = std::make_shared<DfxMap>();
323 
324     inMap->name = "";
325     EXPECT_EQ(thread->FindSymbolsFileByMap(inMap), nullptr);
326 
327     inMap->name = "1";
328     EXPECT_EQ(thread->FindSymbolsFileByMap(inMap), nullptr);
329 
330     inMap->name = "1.elf";
331     ASSERT_NE(thread->FindSymbolsFileByMap(inMap), nullptr);
332     EXPECT_STREQ(thread->FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap->name.c_str());
333 
334     inMap->name = "2.elf";
335     ASSERT_NE(thread->FindSymbolsFileByMap(inMap), nullptr);
336     EXPECT_STREQ(thread->FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap->name.c_str());
337 
338     inMap->name = "3.elf";
339     ASSERT_NE(thread->FindSymbolsFileByMap(inMap), nullptr);
340     EXPECT_STREQ(thread->FindSymbolsFileByMap(inMap)->filePath_.c_str(), inMap->name.c_str());
341 }
342 
343 /**
344  * @tc.name: ReadRoMemory
345  * @tc.desc:
346  * @tc.type: FUNC
347  */
348 HWTEST_F(VirtualThreadTest, ReadRoMemory, TestSize.Level1)
349 {
350     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> symbolsFiles;
351     std::shared_ptr<VirtualRuntime> runtime = std::make_shared<VirtualRuntime>();
352     std::shared_ptr<VirtualThread> thread = std::make_shared<VirtualThread>(getpid(), GetThreadId(),
353         symbolsFiles, runtime.get(), false);
354     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(TEST_FILE_ELF_FULL_PATH.c_str(), "rb"),
355                                                 fclose);
356     if (fp) {
357         struct stat sb = {};
358         if (fstat(fileno(fp.get()), &sb) == -1) {
359             HLOGE("unable to check the file size");
360         } else {
361             HLOGV("file stat size %" PRIu64 "", sb.st_size);
362         }
363 
364         thread->CreateMapItem(TEST_FILE_ELF_FULL_PATH, 0u, sb.st_size, 0u);
365         ASSERT_EQ(thread->GetMaps().size(), 1u);
366 
367         std::unique_ptr<SymbolsFile> symbolsFile =
368             SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, TEST_FILE_ELF_FULL_PATH);
369         ASSERT_NE(symbolsFile, nullptr);
370         ASSERT_EQ(symbolsFile->LoadSymbols(), true);
371 
372         // add to symbols list
373         symbolsFiles[symbolsFile->filePath_] = std::move(symbolsFile);
374 
375         uint8_t freadByte = '\0';
376         uint8_t readRoByte = '\0';
377         uint64_t addr = 0x0;
378 
379         // first byte
380         ASSERT_EQ(fread(&freadByte, 1, 1, fp.get()), 1u);
381 
382         const std::shared_ptr<DfxMap> map = thread->FindMapByAddr(addr);
383         ASSERT_EQ(map != nullptr, true);
384         if (HasFailure()) {
385             printf("map: %s\n", thread->GetMaps().at(0)->ToString().c_str());
386         }
387 
388         EXPECT_NE(thread->FindSymbolsFileByMap(map), nullptr);
389         if (HasFailure()) {
390             printf("symbols: %s\n", thread->symbolsFiles_.begin()->second->filePath_.c_str());
391         }
392 
393         ASSERT_EQ(thread->ReadRoMemory(addr++, &readRoByte, 1u), true);
394         ASSERT_EQ(freadByte, readRoByte);
395 
396         while (fread(&freadByte, 1, 1, fp.get()) == 1u) {
397             ASSERT_EQ(thread->ReadRoMemory(addr++, &readRoByte, 1u), true);
398             ASSERT_EQ(freadByte, readRoByte);
399         }
400 
401         // EOF , out of file size should return 0
402         ASSERT_EQ(thread->ReadRoMemory(addr++, &readRoByte, 1u), false);
403     }
404 }
405 } // namespace NativeDaemon
406 } // namespace Developtools
407 } // namespace OHOS
408