[perf2bolt] Better mmap event matching

Summary:
When perf tool reports a mapping address of a binary, it is not always
the address of the first loadable segment we were checking against.
As a result, perf2botl was not working properly for binaries where the
first segment was not executable.

The fix is to check if the address reported by mmap event matches any
of the loadable segments. Note that the segment alignment has to be
applied to get real loadable address of the segment.

Fixes facebookincubator/BOLT#65

(cherry picked from FBD19146419)
This commit is contained in:
Maksim Panchenko 2019-12-17 11:17:31 -08:00
parent 16a497c627
commit d414acfbb6
5 changed files with 35 additions and 19 deletions

View File

@ -63,6 +63,7 @@ namespace bolt {
class BinaryFunction;
class BinaryBasicBlock;
class DataReader;
class ExecutableFileMemoryManager;
enum class MemoryContentsType : char {
UNKNOWN = 0, /// Unknown contents.
@ -196,6 +197,10 @@ public:
FilterIterator<binary_data_const_iterator>;
using FilteredBinaryDataIterator = FilterIterator<binary_data_iterator>;
/// Memory manager for sections and segments. Used to communicate with ORC
/// among other things.
std::shared_ptr<ExecutableFileMemoryManager> EFMM;
/// Return BinaryFunction containing a given \p Address or nullptr if
/// no registered function has it.
///

View File

@ -16,6 +16,7 @@
#include "BinaryFunction.h"
#include "BoltAddressTranslation.h"
#include "DataAggregator.h"
#include "ExecutableFileMemoryManager.h"
#include "Heatmap.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
@ -1857,11 +1858,22 @@ std::error_code DataAggregator::parseMMapEvents() {
auto Range = GlobalMMapInfo.equal_range(NameToUse);
for (auto I = Range.first; I != Range.second; ++I) {
if (BC->HasFixedLoadAddress && I->second.BaseAddress &&
I->second.BaseAddress != BC->FirstAllocAddress) {
errs() << "PERF2BOLT-WARNING: ignoring mapping of " << NameToUse
<< " at 0x" << Twine::utohexstr(I->second.BaseAddress) << '\n';
continue;
if (BC->HasFixedLoadAddress && I->second.BaseAddress) {
// Check that the binary mapping matches one of the segments.
bool MatchFound{false};
for (auto &KV : BC->EFMM->SegmentMapInfo) {
auto &SegInfo = KV.second;
const auto MapAddress = alignDown(SegInfo.Address, SegInfo.Alignment);
if (I->second.BaseAddress == MapAddress) {
MatchFound = true;
break;
}
}
if (!MatchFound) {
errs() << "PERF2BOLT-WARNING: ignoring mapping of " << NameToUse
<< " at 0x" << Twine::utohexstr(I->second.BaseAddress) << '\n';
continue;
}
}
BinaryMMapInfo.insert(std::make_pair(I->second.PID, I->second));

View File

@ -27,13 +27,15 @@ struct SegmentInfo {
uint64_t Size; /// Size of the segment in memory.
uint64_t FileOffset; /// Offset in the file.
uint64_t FileSize; /// Size in file.
uint64_t Alignment; /// Alignment of the segment.
void print(raw_ostream &OS) const {
OS << "SegmentInfo { Address: 0x"
<< Twine::utohexstr(Address) << ", Size: 0x"
<< Twine::utohexstr(Size) << ", FileOffset: 0x"
<< Twine::utohexstr(FileOffset) << ", FileSize: 0x"
<< Twine::utohexstr(FileSize) << "}";
<< Twine::utohexstr(FileSize) << ", Alignment: 0x"
<< Twine::utohexstr(Alignment) << "}";
};
};

View File

@ -780,7 +780,7 @@ void RewriteInstance::discoverStorage() {
// sections accounting for stubs when we need those sections to match the
// same size seen in the input binary, in case this section is a copy
// of the original one seen in the binary.
EFMM.reset(new ExecutableFileMemoryManager(*BC, /*AllowStubs*/ false));
BC->EFMM.reset(new ExecutableFileMemoryManager(*BC, /*AllowStubs*/ false));
auto ELF64LEFile = dyn_cast<ELF64LEObjectFile>(InputFile);
if (!ELF64LEFile) {
@ -808,10 +808,11 @@ void RewriteInstance::discoverStorage() {
NextAvailableOffset = std::max(NextAvailableOffset,
Phdr.p_offset + Phdr.p_filesz);
EFMM->SegmentMapInfo[Phdr.p_vaddr] = SegmentInfo{Phdr.p_vaddr,
Phdr.p_memsz,
Phdr.p_offset,
Phdr.p_filesz};
BC->EFMM->SegmentMapInfo[Phdr.p_vaddr] = SegmentInfo{Phdr.p_vaddr,
Phdr.p_memsz,
Phdr.p_offset,
Phdr.p_filesz,
Phdr.p_align};
}
}
@ -2969,7 +2970,7 @@ void RewriteInstance::emitAndLink() {
auto Resolver = orc::createLegacyLookupResolver(
[&](const std::string &Name) -> JITSymbol {
DEBUG(dbgs() << "BOLT: looking for " << Name << "\n");
if (EFMM->ObjectsLoaded) {
if (BC->EFMM->ObjectsLoaded) {
auto Result = OLT->findSymbol(Name, false);
if (cantFail(Result.getAddress()) == 0) {
// Resolve to a PLT entry if possible
@ -3008,7 +3009,7 @@ void RewriteInstance::emitAndLink() {
*ES,
[this, &Resolver](orc::VModuleKey Key) {
orc::RTDyldObjectLinkingLayer::Resources R;
R.MemMgr = EFMM;
R.MemMgr = BC->EFMM;
R.Resolver = Resolver;
// Get memory manager
return R;
@ -4934,8 +4935,8 @@ uint64_t RewriteInstance::getFileOffsetForAddress(uint64_t Address) const {
}
// Find an existing segment that matches the address.
const auto SegmentInfoI = EFMM->SegmentMapInfo.upper_bound(Address);
if (SegmentInfoI == EFMM->SegmentMapInfo.begin())
const auto SegmentInfoI = BC->EFMM->SegmentMapInfo.upper_bound(Address);
if (SegmentInfoI == BC->EFMM->SegmentMapInfo.begin())
return 0;
const auto &SegmentInfo = std::prev(SegmentInfoI)->second;

View File

@ -343,10 +343,6 @@ private:
std::unique_ptr<BinaryContext> BC;
std::unique_ptr<CFIReaderWriter> CFIRdWrt;
/// Memory manager for sections and segments. Used to communicate with ORC
/// among other things.
std::shared_ptr<ExecutableFileMemoryManager> EFMM;
std::unique_ptr<orc::SymbolStringPool> SSP;
std::unique_ptr<orc::ExecutionSession> ES;