147 lines · cpp
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-exception6//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 18using namespace llvm;19using namespace llvm::object;20using namespace llvm::ELF;21using namespace bolt;22 23namespace opts {24extern cl::opt<std::string> ReadPerfEvents;25} // namespace opts26 27namespace {28 29/// Perform checks on memory map events normally captured in perf. Tests use30/// the 'opts::ReadPerfEvents' flag to emulate these events, passing a custom31/// 'perf script' output to DataAggregator.32struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {33 void SetUp() override {34 initalizeLLVM();35 prepareElf();36 initializeBOLT();37 }38 39protected:40 void initalizeLLVM() {41#define BOLT_TARGET(target) \42 LLVMInitialize##target##TargetInfo(); \43 LLVMInitialize##target##TargetMC(); \44 LLVMInitialize##target##AsmParser(); \45 LLVMInitialize##target##Disassembler(); \46 LLVMInitialize##target##Target(); \47 LLVMInitialize##target##AsmPrinter();48 49#include "bolt/Core/TargetConfig.def"50 }51 52 void prepareElf() {53 memcpy(ElfBuf, "\177ELF", 4);54 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);55 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;56 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;57 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;58 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");59 ObjFile = cantFail(ObjectFile::createObjectFile(Source));60 }61 62 void initializeBOLT() {63 Relocation::Arch = ObjFile->makeTriple().getArch();64 BC = cantFail(BinaryContext::createBinaryContext(65 ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),66 ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),67 {llvm::outs(), llvm::errs()}));68 ASSERT_FALSE(!BC);69 }70 71 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};72 std::unique_ptr<ObjectFile> ObjFile;73 std::unique_ptr<BinaryContext> BC;74};75} // namespace76 77#ifdef X86_AVAILABLE78 79INSTANTIATE_TEST_SUITE_P(X86, MemoryMapsTester,80 ::testing::Values(Triple::x86_64));81 82#endif83 84#ifdef AARCH64_AVAILABLE85 86INSTANTIATE_TEST_SUITE_P(AArch64, MemoryMapsTester,87 ::testing::Values(Triple::aarch64));88 89#endif90 91/// Check that the correct mmap size is computed when we have multiple text92/// segment mappings.93TEST_P(MemoryMapsTester, ParseMultipleSegments) {94 const int Pid = 1234;95 StringRef Filename = "BINARY";96 opts::ReadPerfEvents = formatv(97 "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "98 "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"99 "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "100 "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",101 Pid, Filename);102 103 BC->SegmentMapInfo[0x11da000] = SegmentInfo{104 0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true, false};105 BC->SegmentMapInfo[0x31d0000] = SegmentInfo{106 0x31d0000, 0x51ac82c, 0x31d0000, 0x3000000, 0x200000, true, false};107 108 DataAggregator DA("");109 BC->setFilename(Filename);110 Error Err = DA.preprocessProfile(*BC);111 112 // Ignore errors from perf2bolt when parsing memory events later on.113 ASSERT_THAT_ERROR(std::move(Err), Succeeded());114 115 auto &BinaryMMapInfo = DA.getBinaryMMapInfo();116 auto El = BinaryMMapInfo.find(Pid);117 // Check that memory mapping is present and has the expected size.118 ASSERT_NE(El, BinaryMMapInfo.end());119 ASSERT_EQ(El->second.Size, static_cast<uint64_t>(0xb1d0000));120}121 122/// Check that DataAggregator aborts when pre-processing an input binary123/// with multiple text segments that have different base addresses.124TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {125 const int Pid = 1234;126 StringRef Filename = "BINARY";127 opts::ReadPerfEvents = formatv(128 "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "129 "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"130 "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "131 "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",132 Pid, Filename);133 134 BC->SegmentMapInfo[0x11da000] = SegmentInfo{135 0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true, false};136 // Using '0x31d0fff' FileOffset which triggers a different base address137 // for this second text segment.138 BC->SegmentMapInfo[0x31d0000] = SegmentInfo{139 0x31d0000, 0x51ac82c, 0x31d0fff, 0x3000000, 0x200000, true, false};140 141 DataAggregator DA("");142 BC->setFilename(Filename);143 ASSERT_DEBUG_DEATH(144 { Error Err = DA.preprocessProfile(*BC); },145 "Base address on multiple segment mappings should match");146}147