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