Files
clang-p2996/bolt/DataAggregator.cpp
Maksim Panchenko d15b93bade [BOLT] Major overhaul of profiling in BOLT
Summary:
Profile reading was tightly coupled with building CFG. Since I plan
to move to a new profile format that will be associated with CFG
it is critical to decouple the two phases.

We now have read profile right after the cfg was constructed, but
before it is "canonicalized", i.e. CTCs will till be there.

After reading the profile, we do a post-processing pass that fixes
CFG and does some post-processing for debug info, such as
inference of fall-throughs, which is still required with the current
format.

Another good reason for decoupling is that we can use profile with
CFG to more accurately record fall-through branches during
aggregation.

At the moment we use "Offset" annotations to facilitate location
of instructions corresponding to the profile. This might not be
super efficient. However, once we switch to the new profile format
the offsets would be no longer needed. We might keep them for
the aggregator, but if we have to trust LBR data that might
not be strictly necessary.

I've tried to make changes while keeping backwards compatibly. This makes
it easier to verify correctness of the changes, but that also means
that we lose accuracy of the profile.

Some refactoring is included.

Flag "-prof-compat-mode" (on by default) is used for bug-level
backwards compatibility. Disable it for more accurate tracing.

(cherry picked from FBD6506156)
2017-11-28 09:57:21 -08:00

1023 lines
30 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";
}
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;
SmallVector<StringRef, 3> Redirects;
SmallVector<const StringRef*, 3> RedirectPtrs;
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);
}
Redirects.push_back(""); // Stdin
Redirects.push_back(StringRef(PerfBranchEventsOutputPath.data())); // Stdout
Redirects.push_back(StringRef(PerfBranchEventsErrPath.data())); // Stderr
RedirectPtrs.push_back(&Redirects[0]);
RedirectPtrs.push_back(&Redirects[1]);
RedirectPtrs.push_back(&Redirects[2]);
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< PerfBranchEventsOutputPath.data() << " 2> "
<< PerfBranchEventsErrPath.data() << "\n");
BranchEventsPI = sys::ExecuteNoWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, &RedirectPtrs[0]);
return true;
}
bool DataAggregator::launchPerfMemEventsNoWait() {
SmallVector<const char*, 4> Argv;
SmallVector<StringRef, 3> Redirects;
SmallVector<const StringRef*, 3> RedirectPtrs;
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);
}
Redirects.push_back(""); // Stdin
Redirects.push_back(StringRef(PerfMemEventsOutputPath.data())); // Stdout
Redirects.push_back(StringRef(PerfMemEventsErrPath.data())); // Stderr
RedirectPtrs.push_back(&Redirects[0]);
RedirectPtrs.push_back(&Redirects[1]);
RedirectPtrs.push_back(&Redirects[2]);
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< PerfMemEventsOutputPath.data() << " 2> "
<< PerfMemEventsErrPath.data() << "\n");
MemEventsPI = sys::ExecuteNoWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, &RedirectPtrs[0]);
return true;
}
bool DataAggregator::launchPerfTasksNoWait() {
SmallVector<const char*, 4> Argv;
SmallVector<StringRef, 3> Redirects;
SmallVector<const StringRef*, 3> RedirectPtrs;
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);
}
Redirects.push_back(""); // Stdin
Redirects.push_back(StringRef(PerfTasksOutputPath.data())); // Stdout
Redirects.push_back(StringRef(PerfTasksErrPath.data())); // Stderr
RedirectPtrs.push_back(&Redirects[0]);
RedirectPtrs.push_back(&Redirects[1]);
RedirectPtrs.push_back(&Redirects[2]);
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< PerfTasksOutputPath.data() << " 2> "
<< PerfTasksErrPath.data() << "\n");
TasksPI = sys::ExecuteNoWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, &RedirectPtrs[0]);
return true;
}
Optional<std::string> DataAggregator::getPerfBuildID() {
SmallVector<const char *, 4> Argv;
SmallVector<StringRef, 3> Redirects;
SmallVector<const StringRef*, 3> RedirectPtrs;
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);
}
Redirects.push_back(""); // Stdin
Redirects.push_back(StringRef(OutputPath.data())); // Stdout
Redirects.push_back(StringRef(ErrPath.data())); // Stderr
RedirectPtrs.push_back(&Redirects[0]);
RedirectPtrs.push_back(&Redirects[1]);
RedirectPtrs.push_back(&Redirects[2]);
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
<< OutputPath.data() << " 2> "
<< ErrPath.data() << "\n");
auto RetCode = sys::ExecuteAndWait(PerfPath.data(), Argv.data(),
/*envp*/ nullptr, &RedirectPtrs[0]);
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("Branch samples parsing", TimerGroupName,
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();
// Parser semantic actions
// LBRs are stored in reverse execution order. NextLBR refers to 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("Mem samples parsing", TimerGroupName, 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("Tasks parsing", TimerGroupName, 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";
}