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