Files
llvm/bolt/DataAggregator.cpp
Rafael Auler 8a5a30156e [BOLT rebase] Rebase fixes on top of LLVM Feb2018
Summary:
This commit includes all code necessary to make BOLT working again
after the rebase. This includes a redesign of the EHFrame work,
cherry-pick of the 3dnow disassembly work, compilation error fixes,
and port of the debug_info work. The macroop fusion feature is not
ported yet.

The rebased version has minor changes to the "executed instructions"
dynostats counter because REP prefixes are considered a part of the
instruction it applies to. Also, some X86 instructions had the "mayLoad"
tablegen property removed, which BOLT  uses to identify and account
for loads, thus reducing the total number of loads reported by
dynostats. This was observed in X86::MOVDQUmr. TRAP instructions are
not terminators anymore, changing our CFG. This commit adds compensation
to preserve this old behavior and minimize tests changes. debug_info
sections are now slightly larger. The discriminator field in the line
table is slightly different due to a change upstream. New profiles
generated with the other bolt are incompatible with this version
because of different hash values calculated for functions, so they will
be considered 100% stale. This commit changes the corresponding test
to XFAIL so it can be updated. The hash function changes because it
relies on raw opcode values, which change according to the opcodes
described in the X86 tablegen files. When processing HHVM, bolt was
observed to be using about 800MB more memory in the rebased version
and being about 5% slower.

(cherry picked from FBD7078072)
2018-02-06 15:00:23 -08:00

1008 lines
29 KiB
C++

//===-- DataAggregator.cpp - Perf data aggregator ---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This family of functions reads profile data written by perf record,
// aggregate it and then write it back to an output file.
//
//===----------------------------------------------------------------------===//
#include "BinaryContext.h"
#include "BinaryFunction.h"
#include "DataAggregator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Options.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Timer.h"
#include <unistd.h>
#define DEBUG_TYPE "aggregator"
using namespace llvm;
using namespace bolt;
namespace opts {
extern cl::OptionCategory AggregatorCategory;
static llvm::cl::opt<bool>
TimeAggregator("time-aggr",
cl::desc("time BOLT aggregator"),
cl::init(false),
cl::ZeroOrMore,
cl::cat(AggregatorCategory));
}
namespace {
const char TimerGroupName[] = "aggregator";
const char TimerGroupDesc[] = "Aggregator";
}
void DataAggregator::findPerfExecutable() {
auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
if (!PerfExecutable) {
outs() << "PERF2BOLT: No perf executable found!\n";
exit(1);
}
PerfPath = *PerfExecutable;
}
void DataAggregator::start(StringRef PerfDataFilename) {
Enabled = true;
this->PerfDataFilename = PerfDataFilename;
outs() << "PERF2BOLT: Starting data aggregation job for " << PerfDataFilename
<< "\n";
findPerfExecutable();
launchPerfBranchEventsNoWait();
launchPerfMemEventsNoWait();
launchPerfTasksNoWait();
}
void DataAggregator::abort() {
std::string Error;
// Kill subprocesses in case they are not finished
sys::Wait(TasksPI, 1, false, &Error);
sys::Wait(BranchEventsPI, 1, false, &Error);
sys::Wait(MemEventsPI, 1, false, &Error);
deleteTempFiles();
}
bool DataAggregator::launchPerfBranchEventsNoWait() {
SmallVector<const char*, 4> Argv;
outs() << "PERF2BOLT: Spawning perf-script job to read branch events\n";
Argv.push_back(PerfPath.data());
Argv.push_back("script");
Argv.push_back("-F");
Argv.push_back("pid,brstack");
Argv.push_back("-i");
Argv.push_back(PerfDataFilename.data());
Argv.push_back(nullptr);
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "out",
PerfBranchEventsOutputPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< PerfBranchEventsOutputPath << " with error " << Errc.message() << "\n";
exit(1);
}
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "err",
PerfBranchEventsErrPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< PerfBranchEventsErrPath << " with error " << Errc.message() << "\n";
exit(1);
}
Optional<StringRef> Redirects[] = {
llvm::None, // Stdin
StringRef(PerfBranchEventsOutputPath.data()), // Stdout
StringRef(PerfBranchEventsErrPath.data())}; // Stderr
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< PerfBranchEventsOutputPath.data() << " 2> "
<< PerfBranchEventsErrPath.data() << "\n");
BranchEventsPI = sys::ExecuteNoWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, Redirects);
return true;
}
bool DataAggregator::launchPerfMemEventsNoWait() {
SmallVector<const char*, 4> Argv;
outs() << "PERF2BOLT: Spawning perf-script job to read mem events\n";
Argv.push_back(PerfPath.data());
Argv.push_back("script");
Argv.push_back("-F");
Argv.push_back("pid,event,addr,ip");
Argv.push_back("-i");
Argv.push_back(PerfDataFilename.data());
Argv.push_back(nullptr);
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "out",
PerfMemEventsOutputPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< PerfMemEventsOutputPath << " with error " << Errc.message() << "\n";
exit(1);
}
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "err",
PerfMemEventsErrPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< PerfMemEventsErrPath << " with error " << Errc.message() << "\n";
exit(1);
}
Optional<StringRef> Redirects[] = {
llvm::None, // Stdin
StringRef(PerfMemEventsOutputPath.data()), // Stdout
StringRef(PerfMemEventsErrPath.data())}; // Stderr
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< PerfMemEventsOutputPath.data() << " 2> "
<< PerfMemEventsErrPath.data() << "\n");
MemEventsPI = sys::ExecuteNoWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, Redirects);
return true;
}
bool DataAggregator::launchPerfTasksNoWait() {
SmallVector<const char*, 4> Argv;
outs() << "PERF2BOLT: Spawning perf-script job to read tasks\n";
Argv.push_back(PerfPath.data());
Argv.push_back("script");
Argv.push_back("--show-task-events");
Argv.push_back("-i");
Argv.push_back(PerfDataFilename.data());
Argv.push_back(nullptr);
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "out",
PerfTasksOutputPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< PerfTasksOutputPath << " with error " << Errc.message() << "\n";
exit(1);
}
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "err",
PerfTasksErrPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< PerfTasksErrPath << " with error " << Errc.message() << "\n";
exit(1);
}
Optional<StringRef> Redirects[] = {
llvm::None, // Stdin
StringRef(PerfTasksOutputPath.data()), // Stdout
StringRef(PerfTasksErrPath.data())}; // Stderr
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< PerfTasksOutputPath.data() << " 2> "
<< PerfTasksErrPath.data() << "\n");
TasksPI = sys::ExecuteNoWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, Redirects);
return true;
}
Optional<std::string> DataAggregator::getPerfBuildID() {
SmallVector<const char *, 4> Argv;
SmallVector<char, 256> OutputPath;
SmallVector<char, 256> ErrPath;
Argv.push_back(PerfPath.data());
Argv.push_back("buildid-list");
Argv.push_back("-i");
Argv.push_back(PerfDataFilename.data());
Argv.push_back(nullptr);
if (auto Errc = sys::fs::createTemporaryFile("perf.buildid", "out",
OutputPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< OutputPath << " with error " << Errc.message() << "\n";
exit(1);
}
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "err",
ErrPath)) {
outs() << "PERF2BOLT: Failed to create temporary file "
<< ErrPath << " with error " << Errc.message() << "\n";
exit(1);
}
Optional<StringRef> Redirects[] = {
llvm::None, // Stdin
StringRef(OutputPath.data()), // Stdout
StringRef(ErrPath.data())}; // Stderr
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< OutputPath.data() << " 2> "
<< ErrPath.data() << "\n");
auto RetCode = sys::ExecuteAndWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, Redirects);
if (RetCode != 0) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFileOrSTDIN(ErrPath.data());
StringRef ErrBuf = (*MB)->getBuffer();
errs() << "PERF-ERROR: Return code " << RetCode << "\n";
errs() << ErrBuf;
deleteTempFile(ErrPath.data());
deleteTempFile(OutputPath.data());
return NoneType();
}
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFileOrSTDIN(OutputPath.data());
if (std::error_code EC = MB.getError()) {
errs() << "Cannot open " << PerfTasksOutputPath.data() << ": "
<< EC.message() << "\n";
deleteTempFile(ErrPath.data());
deleteTempFile(OutputPath.data());
return NoneType();
}
FileBuf.reset(MB->release());
ParsingBuf = FileBuf->getBuffer();
Col = 0;
Line = 1;
auto ParseResult = parsePerfBuildID();
if (!ParseResult) {
outs() << "PERF2BOLT: Failed to parse build-id from perf output\n";
deleteTempFile(ErrPath.data());
deleteTempFile(OutputPath.data());
return NoneType();
}
outs() << "PERF2BOLT: Perf.data build-id is: " << *ParseResult << "\n";
deleteTempFile(ErrPath.data());
deleteTempFile(OutputPath.data());
return std::string(ParseResult->data(), ParseResult->size());
}
bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
int FD;
if (sys::fs::openFileForRead(FileName, FD)) {
return false;
}
char Buf[7] = {0, 0, 0, 0, 0, 0, 0};
if (::read(FD, Buf, 7) == -1) {
::close(FD);
return false;
}
::close(FD);
if (strncmp(Buf, "PERFILE", 7) == 0)
return true;
return false;
}
void DataAggregator::deleteTempFile(StringRef File) {
if (auto Errc = sys::fs::remove(File.data())) {
outs() << "PERF2BOLT: Failed to delete temporary file "
<< File << " with error " << Errc.message() << "\n";
}
}
void DataAggregator::deleteTempFiles() {
deleteTempFile(PerfBranchEventsErrPath.data());
deleteTempFile(PerfBranchEventsOutputPath.data());
deleteTempFile(PerfMemEventsErrPath.data());
deleteTempFile(PerfMemEventsOutputPath.data());
deleteTempFile(PerfTasksErrPath.data());
deleteTempFile(PerfTasksOutputPath.data());
}
bool DataAggregator::aggregate(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs) {
std::string Error;
this->BC = &BC;
this->BFs = &BFs;
outs() << "PERF2BOLT: Waiting for perf tasks collection to finish...\n";
auto PI1 = sys::Wait(TasksPI, 0, true, &Error);
if (!Error.empty()) {
errs() << "PERF-ERROR: " << Error << "\n";
deleteTempFiles();
exit(1);
}
if (PI1.ReturnCode != 0) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFileOrSTDIN(PerfTasksErrPath.data());
StringRef ErrBuf = (*MB)->getBuffer();
errs() << "PERF-ERROR: Return code " << PI1.ReturnCode << "\n";
errs() << ErrBuf;
deleteTempFiles();
exit(1);
}
ErrorOr<std::unique_ptr<MemoryBuffer>> MB1 =
MemoryBuffer::getFileOrSTDIN(PerfTasksOutputPath.data());
if (std::error_code EC = MB1.getError()) {
errs() << "Cannot open " << PerfTasksOutputPath.data() << ": "
<< EC.message() << "\n";
deleteTempFiles();
exit(1);
}
FileBuf.reset(MB1->release());
ParsingBuf = FileBuf->getBuffer();
Col = 0;
Line = 1;
if (parseTasks()) {
outs() << "PERF2BOLT: Failed to parse tasks\n";
}
outs()
<< "PERF2BOLT: Waiting for perf events collection to finish...\n";
auto PI2 = sys::Wait(BranchEventsPI, 0, true, &Error);
if (!Error.empty()) {
errs() << "PERF-ERROR: " << Error << "\n";
deleteTempFiles();
exit(1);
}
if (PI2.ReturnCode != 0) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFileOrSTDIN(PerfBranchEventsErrPath.data());
StringRef ErrBuf = (*MB)->getBuffer();
errs() << "PERF-ERROR: Return code " << PI2.ReturnCode << "\n";
errs() << ErrBuf;
deleteTempFiles();
exit(1);
}
ErrorOr<std::unique_ptr<MemoryBuffer>> MB2 =
MemoryBuffer::getFileOrSTDIN(PerfBranchEventsOutputPath.data());
if (std::error_code EC = MB2.getError()) {
errs() << "Cannot open " << PerfBranchEventsOutputPath.data() << ": "
<< EC.message() << "\n";
deleteTempFiles();
exit(1);
}
FileBuf.reset(MB2->release());
ParsingBuf = FileBuf->getBuffer();
Col = 0;
Line = 1;
if (parseBranchEvents()) {
outs() << "PERF2BOLT: Failed to parse branch events\n";
}
// Mark all functions with registered events as having a valid profile.
for (auto &BFI : BFs) {
auto &BF = BFI.second;
if (BF.getBranchData()) {
BF.markProfiled();
}
}
auto PI3 = sys::Wait(MemEventsPI, 0, true, &Error);
if (PI3.ReturnCode != 0) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFileOrSTDIN(PerfMemEventsErrPath.data());
StringRef ErrBuf = (*MB)->getBuffer();
deleteTempFiles();
Regex NoData("Samples for '.*' event do not have ADDR attribute set. "
"Cannot print 'addr' field.");
if (!NoData.match(ErrBuf)) {
errs() << "PERF-ERROR: Return code " << PI3.ReturnCode << "\n";
errs() << ErrBuf;
exit(1);
}
return true;
}
ErrorOr<std::unique_ptr<MemoryBuffer>> MB3 =
MemoryBuffer::getFileOrSTDIN(PerfMemEventsOutputPath.data());
if (std::error_code EC = MB3.getError()) {
errs() << "Cannot open " << PerfMemEventsOutputPath.data() << ": "
<< EC.message() << "\n";
deleteTempFiles();
exit(1);
}
FileBuf.reset(MB3->release());
ParsingBuf = FileBuf->getBuffer();
Col = 0;
Line = 1;
if (parseMemEvents()) {
outs() << "PERF2BOLT: Failed to parse memory events\n";
}
deleteTempFiles();
return true;
}
BinaryFunction *
DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) {
auto FI = BFs->upper_bound(Address);
if (FI == BFs->begin())
return nullptr;
--FI;
const auto UsedSize = FI->second.getMaxSize();
if (Address >= FI->first + UsedSize)
return nullptr;
return &FI->second;
}
bool
DataAggregator::doIntraBranch(BinaryFunction *Func, const LBREntry &Branch) {
FuncBranchData *AggrData = Func->getBranchData();
if (!AggrData) {
AggrData = &FuncsToBranches[Func->getNames()[0]];
AggrData->Name = Func->getNames()[0];
Func->setBranchData(AggrData);
}
AggrData->bumpBranchCount(Branch.From - Func->getAddress(),
Branch.To - Func->getAddress(),
Branch.Mispred);
return true;
}
bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
BinaryFunction *ToFunc,
const LBREntry &Branch) {
FuncBranchData *FromAggrData{nullptr};
FuncBranchData *ToAggrData{nullptr};
StringRef SrcFunc;
StringRef DstFunc;
auto From = Branch.From;
auto To = Branch.To;
if (FromFunc) {
SrcFunc = FromFunc->getNames()[0];
FromAggrData = FromFunc->getBranchData();
if (!FromAggrData) {
FromAggrData = &FuncsToBranches[SrcFunc];
FromAggrData->Name = SrcFunc;
FromFunc->setBranchData(FromAggrData);
}
From -= FromFunc->getAddress();
FromFunc->recordExit(From, Branch.Mispred);
}
if (ToFunc) {
DstFunc = ToFunc->getNames()[0];
ToAggrData = ToFunc->getBranchData();
if (!ToAggrData) {
ToAggrData = &FuncsToBranches[DstFunc];
ToAggrData->Name = DstFunc;
ToFunc->setBranchData(ToAggrData);
}
To -= ToFunc->getAddress();
ToFunc->recordEntry(To, Branch.Mispred);
}
if (FromAggrData)
FromAggrData->bumpCallCount(From, Location(!DstFunc.empty(), DstFunc, To),
Branch.Mispred);
if (ToAggrData)
ToAggrData->bumpEntryCount(Location(!SrcFunc.empty(), SrcFunc, From), To,
Branch.Mispred);
return true;
}
bool DataAggregator::doBranch(const LBREntry &Branch) {
auto *FromFunc = getBinaryFunctionContainingAddress(Branch.From);
auto *ToFunc = getBinaryFunctionContainingAddress(Branch.To);
if (!FromFunc && !ToFunc)
return false;
if (FromFunc == ToFunc) {
FromFunc->recordBranch(Branch.From - FromFunc->getAddress(),
Branch.To - FromFunc->getAddress(),
1,
Branch.Mispred);
return doIntraBranch(FromFunc, Branch);
}
return doInterBranch(FromFunc, ToFunc, Branch);
}
bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second) {
auto *FromFunc = getBinaryFunctionContainingAddress(First.To);
auto *ToFunc = getBinaryFunctionContainingAddress(Second.From);
if (!FromFunc || !ToFunc) {
++NumLongRangeTraces;
return false;
}
if (FromFunc != ToFunc) {
++NumInvalidTraces;
DEBUG(dbgs() << "Trace starting in " << FromFunc->getPrintName() << " @ "
<< Twine::utohexstr(First.To - FromFunc->getAddress())
<< " and ending in " << ToFunc->getPrintName() << " @ "
<< ToFunc->getPrintName() << " @ "
<< Twine::utohexstr(Second.From - ToFunc->getAddress())
<< '\n');
return false;
}
auto FTs = FromFunc->getFallthroughsInTrace(First, Second);
if (!FTs) {
++NumInvalidTraces;
return false;
}
for (const auto &Pair : *FTs) {
doIntraBranch(FromFunc,
LBREntry{Pair.first + FromFunc->getAddress(),
Pair.second + FromFunc->getAddress(),
false});
}
return true;
}
ErrorOr<LBREntry> DataAggregator::parseLBREntry() {
LBREntry Res;
auto FromStrRes = parseString('/');
if (std::error_code EC = FromStrRes.getError())
return EC;
StringRef OffsetStr = FromStrRes.get();
if (OffsetStr.getAsInteger(0, Res.From)) {
reportError("expected hexadecimal number with From address");
Diag << "Found: " << OffsetStr << "\n";
return make_error_code(llvm::errc::io_error);
}
auto ToStrRes = parseString('/');
if (std::error_code EC = ToStrRes.getError())
return EC;
OffsetStr = ToStrRes.get();
if (OffsetStr.getAsInteger(0, Res.To)) {
reportError("expected hexadecimal number with To address");
Diag << "Found: " << OffsetStr << "\n";
return make_error_code(llvm::errc::io_error);
}
auto MispredStrRes = parseString('/');
if (std::error_code EC = MispredStrRes.getError())
return EC;
StringRef MispredStr = MispredStrRes.get();
if (MispredStr.size() != 1 ||
(MispredStr[0] != 'P' && MispredStr[0] != 'M')) {
reportError("expected single char for mispred bit");
Diag << "Found: " << OffsetStr << "\n";
return make_error_code(llvm::errc::io_error);
}
Res.Mispred = MispredStr[0] == 'M';
auto Rest = parseString(FieldSeparator, true);
if (std::error_code EC = Rest.getError())
return EC;
if (Rest.get().size() < 5) {
reportError("expected rest of LBR entry");
Diag << "Found: " << OffsetStr << "\n";
return make_error_code(llvm::errc::io_error);
}
return Res;
}
bool DataAggregator::checkAndConsumeFS() {
if (ParsingBuf[0] != FieldSeparator) {
return false;
}
ParsingBuf = ParsingBuf.drop_front(1);
Col += 1;
return true;
}
void DataAggregator::consumeRestOfLine() {
auto LineEnd = ParsingBuf.find_first_of('\n');
if (LineEnd == StringRef::npos) {
ParsingBuf = StringRef();
Col = 0;
Line += 1;
return;
}
ParsingBuf = ParsingBuf.drop_front(LineEnd + 1);
Col = 0;
Line += 1;
}
ErrorOr<PerfBranchSample> DataAggregator::parseBranchSample() {
PerfBranchSample Res;
while (checkAndConsumeFS()) {}
auto PIDRes = parseNumberField(FieldSeparator, true);
if (std::error_code EC = PIDRes.getError())
return EC;
if (!PIDs.empty() && !PIDs.count(PIDRes.get())) {
consumeRestOfLine();
return Res;
}
while (!checkAndConsumeNewLine()) {
checkAndConsumeFS();
auto LBRRes = parseLBREntry();
if (std::error_code EC = LBRRes.getError())
return EC;
Res.LBR.push_back(LBRRes.get());
}
return Res;
}
ErrorOr<PerfMemSample> DataAggregator::parseMemSample() {
PerfMemSample Res{0,0};
while (checkAndConsumeFS()) {}
auto PIDRes = parseNumberField(FieldSeparator, true);
if (std::error_code EC = PIDRes.getError())
return EC;
if (!PIDs.empty() && !PIDs.count(PIDRes.get())) {
consumeRestOfLine();
return Res;
}
while (checkAndConsumeFS()) {}
auto Event = parseString(FieldSeparator);
if (std::error_code EC = Event.getError())
return EC;
if (Event.get().find("mem-loads") == StringRef::npos) {
consumeRestOfLine();
return Res;
}
while (checkAndConsumeFS()) {}
auto AddrRes = parseHexField(FieldSeparator);
if (std::error_code EC = AddrRes.getError()) {
return EC;
}
while (checkAndConsumeFS()) {}
auto PCRes = parseHexField(FieldSeparator, true);
if (std::error_code EC = PCRes.getError()) {
consumeRestOfLine();
return EC;
}
checkAndConsumeNewLine();
return PerfMemSample{PCRes.get(), AddrRes.get()};
}
bool DataAggregator::hasData() {
if (ParsingBuf.size() == 0)
return false;
return true;
}
std::error_code DataAggregator::parseBranchEvents() {
outs() << "PERF2BOLT: Aggregating branch events...\n";
NamedRegionTimer T("parseBranch", "Branch samples parsing", TimerGroupName,
TimerGroupDesc, opts::TimeAggregator);
uint64_t NumEntries{0};
uint64_t NumSamples{0};
uint64_t NumTraces{0};
while (hasData()) {
auto SampleRes = parseBranchSample();
if (std::error_code EC = SampleRes.getError())
return EC;
auto &Sample = SampleRes.get();
if (Sample.LBR.empty())
continue;
++NumSamples;
NumEntries += Sample.LBR.size();
// LBRs are stored in reverse execution order. NextLBR refers to the next
// executed branch record.
const LBREntry *NextLBR{nullptr};
for (const auto &LBR : Sample.LBR) {
if (NextLBR) {
doTrace(LBR, *NextLBR);
++NumTraces;
}
doBranch(LBR);
NextLBR = &LBR;
}
}
outs() << "PERF2BOLT: Read " << NumSamples << " samples and "
<< NumEntries << " LBR entries\n";
outs() << "PERF2BOLT: Traces mismatching disassembled function contents: "
<< NumInvalidTraces;
float Perc{0.0f};
if (NumTraces > 0) {
outs() << " (";
Perc = NumInvalidTraces * 100.0f / NumTraces;
if (outs().has_colors()) {
if (Perc > 10.0f) {
outs().changeColor(raw_ostream::RED);
} else if (Perc > 5.0f) {
outs().changeColor(raw_ostream::YELLOW);
} else {
outs().changeColor(raw_ostream::GREEN);
}
}
outs() << format("%.1f%%", Perc);
if (outs().has_colors())
outs().resetColor();
outs() << ")";
}
outs() << "\n";
if (Perc > 10.0f) {
outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
"binary is probably not the same binary used during profiling "
"collection. The generated data may be ineffective for improving "
"performance.\n\n";
}
outs() << "PERF2BOLT: Out of range traces involving unknown regions: "
<< NumLongRangeTraces;
if (NumTraces > 0) {
outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces);
}
outs() << "\n";
return std::error_code();
}
std::error_code DataAggregator::parseMemEvents() {
outs() << "PERF2BOLT: Aggregating memory events...\n";
NamedRegionTimer T("memevents", "Mem samples parsing", TimerGroupName,
TimerGroupDesc, opts::TimeAggregator);
while (hasData()) {
auto SampleRes = parseMemSample();
if (std::error_code EC = SampleRes.getError())
return EC;
auto PC = SampleRes.get().PC;
auto Addr = SampleRes.get().Addr;
StringRef FuncName;
StringRef MemName;
// Try to resolve symbol for PC
auto *Func = getBinaryFunctionContainingAddress(PC);
if (Func) {
FuncName = Func->getNames()[0];
PC -= Func->getAddress();
}
// Try to resolve symbol for memory load
auto *MemFunc = getBinaryFunctionContainingAddress(Addr);
if (MemFunc) {
MemName = MemFunc->getNames()[0];
Addr -= MemFunc->getAddress();
} else {
// TODO: global symbol size?
auto Sym = BC->getGlobalSymbolAtAddress(Addr);
if (Sym) {
MemName = Sym->getName();
Addr = 0;
}
}
const Location FuncLoc(!FuncName.empty(), FuncName, PC);
const Location AddrLoc(!MemName.empty(), MemName, Addr);
// TODO what does it mean when PC is 0 (or not a known function)?
DEBUG(if (!Func && PC != 0) {
dbgs() << "Skipped mem event: " << FuncLoc << " = " << AddrLoc << "\n";
});
if (Func) {
auto *MemData = &FuncsToMemEvents[FuncName];
Func->setMemData(MemData);
MemData->update(FuncLoc, AddrLoc);
DEBUG(dbgs() << "Mem event: " << FuncLoc << " = " << AddrLoc << "\n");
}
}
return std::error_code();
}
ErrorOr<int64_t> DataAggregator::parseTaskPID() {
while (checkAndConsumeFS()) {}
auto CommNameStr = parseString(FieldSeparator, true);
if (std::error_code EC = CommNameStr.getError())
return EC;
if (CommNameStr.get() != BinaryName) {
consumeRestOfLine();
return -1;
}
auto LineEnd = ParsingBuf.find_first_of("\n");
if (LineEnd == StringRef::npos) {
reportError("expected rest of line");
Diag << "Found: " << ParsingBuf << "\n";
return make_error_code(llvm::errc::io_error);
}
StringRef Line = ParsingBuf.substr(0, LineEnd);
if (Line.find("PERF_RECORD_COMM") != StringRef::npos) {
int64_t PID;
StringRef PIDStr = Line.rsplit(':').second.split('/').first;
if (PIDStr.getAsInteger(10, PID)) {
reportError("expected PID");
Diag << "Found: " << PIDStr << "\n";
return make_error_code(llvm::errc::io_error);
}
return PID;
}
consumeRestOfLine();
return -1;
}
std::error_code DataAggregator::parseTasks() {
outs() << "PERF2BOLT: Parsing perf-script tasks output\n";
NamedRegionTimer T("parseTasks", "Tasks parsing", TimerGroupName,
TimerGroupDesc, opts::TimeAggregator);
while (hasData()) {
auto PIDRes = parseTaskPID();
if (std::error_code EC = PIDRes.getError())
return EC;
auto PID = PIDRes.get();
if (PID == -1) {
continue;
}
PIDs.insert(PID);
}
if (!PIDs.empty())
outs() << "PERF2BOLT: Input binary is associated with " << PIDs.size()
<< " PID(s)\n";
else
outs() << "PERF2BOLT: Could not bind input binary to a PID - will parse "
"all samples in perf data.\n";
return std::error_code();
}
Optional<std::pair<StringRef, StringRef>>
DataAggregator::parseNameBuildIDPair() {
while (checkAndConsumeFS()) {}
auto BuildIDStr = parseString(FieldSeparator, true);
if (std::error_code EC = BuildIDStr.getError())
return NoneType();
auto NameStr = parseString(FieldSeparator, true);
if (std::error_code EC = NameStr.getError())
return NoneType();
consumeRestOfLine();
return std::make_pair(NameStr.get(), BuildIDStr.get());
}
Optional<StringRef> DataAggregator::parsePerfBuildID() {
while (hasData()) {
auto IDPair = parseNameBuildIDPair();
if (!IDPair)
return NoneType();
if (sys::path::filename(IDPair->first) != BinaryName)
continue;
return IDPair->second;
}
return NoneType();
}
std::error_code DataAggregator::writeAggregatedFile() const {
std::error_code EC;
raw_fd_ostream OutFile(OutputFDataName, EC, sys::fs::OpenFlags::F_None);
if (EC)
return EC;
bool WriteMemLocs = false;
auto writeLocation = [&OutFile,&WriteMemLocs](const Location &Loc) {
if (WriteMemLocs)
OutFile << (Loc.IsSymbol ? "4 " : "3 ");
else
OutFile << (Loc.IsSymbol ? "1 " : "0 ");
OutFile << (Loc.Name.empty() ? "[unknown]" : Loc.Name) << " "
<< Twine::utohexstr(Loc.Offset)
<< FieldSeparator;
};
uint64_t BranchValues{0};
uint64_t MemValues{0};
for (const auto &Func : FuncsToBranches) {
for (const auto &BI : Func.getValue().Data) {
writeLocation(BI.From);
writeLocation(BI.To);
OutFile << BI.Mispreds << " " << BI.Branches << "\n";
++BranchValues;
}
for (const auto &BI : Func.getValue().EntryData) {
// Do not output if source is a known symbol, since this was already
// accounted for in the source function
if (BI.From.IsSymbol)
continue;
writeLocation(BI.From);
writeLocation(BI.To);
OutFile << BI.Mispreds << " " << BI.Branches << "\n";
++BranchValues;
}
}
WriteMemLocs = true;
for (const auto &Func : FuncsToMemEvents) {
for (const auto &MemEvent : Func.getValue().Data) {
writeLocation(MemEvent.Offset);
writeLocation(MemEvent.Addr);
OutFile << MemEvent.Count << "\n";
++MemValues;
}
}
outs() << "PERF2BOLT: Wrote " << BranchValues << " branch objects and "
<< MemValues << " memory objects to " << OutputFDataName << "\n";
return std::error_code();
}
void DataAggregator::dump() const {
DataReader::dump();
}
void DataAggregator::dump(const LBREntry &LBR) const {
Diag << "From: " << Twine::utohexstr(LBR.From)
<< " To: " << Twine::utohexstr(LBR.To) << " Mispred? " << LBR.Mispred
<< "\n";
}
void DataAggregator::dump(const PerfBranchSample &Sample) const {
Diag << "Sample LBR entries: " << Sample.LBR.size() << "\n";
for (const auto &LBR : Sample.LBR) {
dump(LBR);
}
}
void DataAggregator::dump(const PerfMemSample &Sample) const {
Diag << "Sample mem entries: " << Sample.PC << ": " << Sample.Addr << "\n";
}