xref: /llvm-project/bolt/unittests/Core/MemoryMaps.cpp (revision 2ccf7ed277df28651b94bbee9fccefdf22fb074f)
1 //===- bolt/unittest/Core/MemoryMaps.cpp ----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "bolt/Core/BinaryContext.h"
10 #include "bolt/Profile/DataAggregator.h"
11 #include "llvm/BinaryFormat/ELF.h"
12 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
13 #include "llvm/Support/CommandLine.h"
14 #include "llvm/Support/TargetSelect.h"
15 #include "llvm/Testing/Support/Error.h"
16 #include "gtest/gtest.h"
17 
18 using namespace llvm;
19 using namespace llvm::object;
20 using namespace llvm::ELF;
21 using namespace bolt;
22 
23 namespace opts {
24 extern cl::opt<std::string> ReadPerfEvents;
25 } // namespace opts
26 
27 namespace {
28 
29 /// Perform checks on memory map events normally captured in perf. Tests use
30 /// the 'opts::ReadPerfEvents' flag to emulate these events, passing a custom
31 /// 'perf script' output to DataAggregator.
32 struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
33   void SetUp() override {
34     initalizeLLVM();
35     prepareElf();
36     initializeBOLT();
37   }
38 
39 protected:
40   void initalizeLLVM() {
41     llvm::InitializeAllTargetInfos();
42     llvm::InitializeAllTargetMCs();
43     llvm::InitializeAllAsmParsers();
44     llvm::InitializeAllDisassemblers();
45     llvm::InitializeAllTargets();
46     llvm::InitializeAllAsmPrinters();
47   }
48 
49   void prepareElf() {
50     memcpy(ElfBuf, "\177ELF", 4);
51     ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
52     EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
53     EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
54     EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
55     MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
56     ObjFile = cantFail(ObjectFile::createObjectFile(Source));
57   }
58 
59   void initializeBOLT() {
60     Relocation::Arch = ObjFile->makeTriple().getArch();
61     BC = cantFail(BinaryContext::createBinaryContext(
62         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
63         ObjFile->getFileName(), nullptr, true,
64         DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
65     ASSERT_FALSE(!BC);
66   }
67 
68   char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
69   std::unique_ptr<ObjectFile> ObjFile;
70   std::unique_ptr<BinaryContext> BC;
71 };
72 } // namespace
73 
74 #ifdef X86_AVAILABLE
75 
76 INSTANTIATE_TEST_SUITE_P(X86, MemoryMapsTester,
77                          ::testing::Values(Triple::x86_64));
78 
79 #endif
80 
81 #ifdef AARCH64_AVAILABLE
82 
83 INSTANTIATE_TEST_SUITE_P(AArch64, MemoryMapsTester,
84                          ::testing::Values(Triple::aarch64));
85 
86 #endif
87 
88 /// Check that the correct mmap size is computed when we have multiple text
89 /// segment mappings.
90 TEST_P(MemoryMapsTester, ParseMultipleSegments) {
91   const int Pid = 1234;
92   StringRef Filename = "BINARY";
93   opts::ReadPerfEvents = formatv(
94       "name       0 [000]     0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
95       "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
96       "name       0 [000]     0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
97       "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
98       Pid, Filename);
99 
100   BC->SegmentMapInfo[0x11da000] =
101       SegmentInfo{0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true};
102   BC->SegmentMapInfo[0x31d0000] =
103       SegmentInfo{0x31d0000, 0x51ac82c, 0x31d0000, 0x3000000, 0x200000, true};
104 
105   DataAggregator DA("");
106   BC->setFilename(Filename);
107   Error Err = DA.preprocessProfile(*BC);
108 
109   // Ignore errors from perf2bolt when parsing memory events later on.
110   ASSERT_THAT_ERROR(std::move(Err), Succeeded());
111 
112   auto &BinaryMMapInfo = DA.getBinaryMMapInfo();
113   auto El = BinaryMMapInfo.find(Pid);
114   // Check that memory mapping is present and has the expected size.
115   ASSERT_NE(El, BinaryMMapInfo.end());
116   ASSERT_EQ(El->second.Size, static_cast<uint64_t>(0xb1d0000));
117 }
118 
119 /// Check that DataAggregator aborts when pre-processing an input binary
120 /// with multiple text segments that have different base addresses.
121 TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
122   const int Pid = 1234;
123   StringRef Filename = "BINARY";
124   opts::ReadPerfEvents = formatv(
125       "name       0 [000]     0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
126       "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
127       "name       0 [000]     0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
128       "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
129       Pid, Filename);
130 
131   BC->SegmentMapInfo[0x11da000] =
132       SegmentInfo{0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true};
133   // Using '0x31d0fff' FileOffset which triggers a different base address
134   // for this second text segment.
135   BC->SegmentMapInfo[0x31d0000] =
136       SegmentInfo{0x31d0000, 0x51ac82c, 0x31d0fff, 0x3000000, 0x200000, true};
137 
138   DataAggregator DA("");
139   BC->setFilename(Filename);
140   ASSERT_DEBUG_DEATH(
141       { Error Err = DA.preprocessProfile(*BC); },
142       "Base address on multiple segment mappings should match");
143 }
144