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