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
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 opts
26
27namespace {
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.
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(dest: ElfBuf, src: "\177ELF", n: 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(ValOrErr: ObjectFile::createObjectFile(Object: Source));
60 }
61
62 void initializeBOLT() {
63 Relocation::Arch = ObjFile->makeTriple().getArch();
64 BC = cantFail(ValOrErr: BinaryContext::createBinaryContext(
65 TheTriple: ObjFile->makeTriple(), SSP: std::make_shared<orc::SymbolStringPool>(),
66 InputFileName: ObjFile->getFileName(), Features: nullptr, IsPIC: true, DwCtx: DWARFContext::create(Obj: *ObjFile),
67 Logger: {.Out: llvm::outs(), .Err: 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} // namespace
76
77#ifdef X86_AVAILABLE
78
79INSTANTIATE_TEST_SUITE_P(X86, MemoryMapsTester,
80 ::testing::Values(Triple::x86_64));
81
82#endif
83
84#ifdef AARCH64_AVAILABLE
85
86INSTANTIATE_TEST_SUITE_P(AArch64, MemoryMapsTester,
87 ::testing::Values(Triple::aarch64));
88
89#endif
90
91/// Check that the correct mmap size is computed when we have multiple text
92/// segment mappings.
93TEST_P(MemoryMapsTester, ParseMultipleSegments) {
94 const int Pid = 1234;
95 StringRef Filename = "BINARY";
96 opts::ReadPerfEvents = formatv(
97 Fmt: "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 Vals: Pid, Vals&: Filename);
102
103 BC->SegmentMapInfo[0x11da000] =
104 SegmentInfo{.Address: 0x11da000, .Size: 0x10da000, .FileOffset: 0x11ca000, .FileSize: 0x10da000, .Alignment: 0x10000, .IsExecutable: true};
105 BC->SegmentMapInfo[0x31d0000] =
106 SegmentInfo{.Address: 0x31d0000, .Size: 0x51ac82c, .FileOffset: 0x31d0000, .FileSize: 0x3000000, .Alignment: 0x200000, .IsExecutable: true};
107
108 DataAggregator DA("");
109 BC->setFilename(Filename);
110 Error Err = DA.preprocessProfile(BC&: *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(x: 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 binary
123/// 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 Fmt: "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 Vals: Pid, Vals&: Filename);
133
134 BC->SegmentMapInfo[0x11da000] =
135 SegmentInfo{.Address: 0x11da000, .Size: 0x10da000, .FileOffset: 0x11ca000, .FileSize: 0x10da000, .Alignment: 0x10000, .IsExecutable: true};
136 // Using '0x31d0fff' FileOffset which triggers a different base address
137 // for this second text segment.
138 BC->SegmentMapInfo[0x31d0000] =
139 SegmentInfo{.Address: 0x31d0000, .Size: 0x51ac82c, .FileOffset: 0x31d0fff, .FileSize: 0x3000000, .Alignment: 0x200000, .IsExecutable: true};
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

source code of bolt/unittests/Core/MemoryMaps.cpp