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