[BOLT] Add parser for pre-aggregated perf data

Summary:
The regular perf2bolt aggregation job is to read perf output directly.
However, if the data is coming from a database instead of perf, one
could write a query to produce a pre-aggregated file. This function
deals with this case.

The pre-aggregated file contains aggregated LBR data, but without binary
knowledge. BOLT will parse it and, using information from the
disassembled binary, augment it with fall-through edge frequency
information. After this step is finished, this data can be either
written to disk to be consumed by BOLT later, or can be used by BOLT
immediately if kept in memory.

File format syntax:
{B|F|f} [<start_id>:]<start_offset> [<end_id>:]<end_offset> <count>
[<mispred_count>]

B - indicates an aggregated branch
F - an aggregated fall-through (trace)
f - an aggregated fall-through with external origin - used to disambiguate
between a return hitting a basic block head and a regular internal
jump to the block

<start_id> - build id of the object containing the start address. We can
skip it for the main binary and use "X" for an unknown object. This will
save some space and facilitate human parsing.

<start_offset> - hex offset from the object base load address (0 for the
main executable unless it's PIE) to the start address.

<end_id>, <end_offset> - same for the end address.

<count> - total aggregated count of the branch or a fall-through.

<mispred_count> - the number of times the branch was mispredicted.
Omitted for fall-throughs.

Example
F 41be50 41be50 3
F 41be90 41be90 4
f 41be90 41be90 7
B 4b1942 39b57f0 3 0
B 4b196f 4b19e0 2 0

(cherry picked from FBD8887182)
This commit is contained in:
Rafael Auler 2018-07-17 18:31:46 -07:00 committed by Maksim Panchenko
parent 27f3032447
commit ddfcf4f266
9 changed files with 519 additions and 59 deletions

View File

@ -2138,7 +2138,8 @@ public:
/// Return a vector of offsets corresponding to a trace in a function
/// (see recordTrace() above).
Optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>>
getFallthroughsInTrace(const LBREntry &First, const LBREntry &Second);
getFallthroughsInTrace(const LBREntry &First, const LBREntry &Second,
uint64_t Count = 1);
/// Returns an estimate of the function's hot part after splitting.
/// This is a very rough estimate, as with C++ exceptions there are

View File

@ -414,10 +414,11 @@ void BinaryFunction::postProcessProfile() {
Optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>>
BinaryFunction::getFallthroughsInTrace(const LBREntry &FirstLBR,
const LBREntry &SecondLBR) {
const LBREntry &SecondLBR,
uint64_t Count) {
SmallVector<std::pair<uint64_t, uint64_t>, 16> Res;
if (!recordTrace(FirstLBR, SecondLBR, 1, &Res))
if (!recordTrace(FirstLBR, SecondLBR, Count, &Res))
return NoneType();
return Res;

View File

@ -43,6 +43,13 @@ BasicAggregation("nl",
cl::ZeroOrMore,
cl::cat(AggregatorCategory));
static cl::opt<bool>
ReadPreAggregated("pa",
cl::desc("skip perf and read data from a pre-aggregated file format"),
cl::init(false),
cl::ZeroOrMore,
cl::cat(AggregatorCategory));
static cl::opt<bool>
IgnoreBuildID("ignore-build-id",
cl::desc("continue even if build-ids in input binary and perf.data mismatch"),
@ -79,6 +86,11 @@ void DataAggregator::start(StringRef PerfDataFilename) {
this->PerfDataFilename = PerfDataFilename;
outs() << "PERF2BOLT: Starting data aggregation job for " << PerfDataFilename
<< "\n";
// Don't launch perf for pre-aggregated files
if (opts::ReadPreAggregated)
return;
findPerfExecutable();
launchPerfBranchEventsNoWait();
launchPerfMemEventsNoWait();
@ -86,6 +98,9 @@ void DataAggregator::start(StringRef PerfDataFilename) {
}
void DataAggregator::abort() {
if (opts::ReadPreAggregated)
return;
std::string Error;
// Kill subprocesses in case they are not finished
@ -227,6 +242,9 @@ bool DataAggregator::launchPerfTasksNoWait() {
}
void DataAggregator::processFileBuildID(StringRef FileBuildID) {
if (opts::ReadPreAggregated)
return;
SmallVector<const char *, 4> Argv;
SmallVector<char, 256> OutputPath;
SmallVector<char, 256> ErrPath;
@ -322,6 +340,9 @@ void DataAggregator::processFileBuildID(StringRef FileBuildID) {
}
bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
if (opts::ReadPreAggregated)
return true;
int FD;
if (sys::fs::openFileForRead(FileName, FD)) {
return false;
@ -356,6 +377,38 @@ void DataAggregator::deleteTempFiles() {
deleteTempFile(PerfTasksOutputPath.data());
}
bool DataAggregator::processPreAggregated() {
std::string Error;
auto MB = MemoryBuffer::getFileOrSTDIN(PerfDataFilename);
if (std::error_code EC = MB.getError()) {
errs() << "PERF2BOLT-ERROR: cannot open " << PerfDataFilename << ": "
<< EC.message() << "\n";
exit(1);
}
FileBuf.reset(MB->release());
ParsingBuf = FileBuf->getBuffer();
Col = 0;
Line = 1;
if (parseAggregatedLBRSamples()) {
outs() << "PERF2BOLT: Failed to parse samples\n";
exit(1);
}
// Mark all functions with registered events as having a valid profile.
for (auto &BFI : *BFs) {
auto &BF = BFI.second;
if (BF.getBranchData()) {
const auto Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE
: BinaryFunction::PF_LBR;
BF.markProfiled(Flags);
}
}
return true;
}
bool DataAggregator::aggregate(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs) {
std::string Error;
@ -363,6 +416,9 @@ bool DataAggregator::aggregate(BinaryContext &BC,
this->BC = &BC;
this->BFs = &BFs;
if (opts::ReadPreAggregated)
return processPreAggregated();
outs() << "PERF2BOLT: Waiting for perf tasks collection to finish...\n";
auto PI1 = sys::Wait(TasksPI, 0, true, &Error);
@ -517,8 +573,9 @@ DataAggregator::doSample(BinaryFunction &Func, uint64_t Address) {
return true;
}
bool
DataAggregator::doIntraBranch(BinaryFunction &Func, const LBREntry &Branch) {
bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From,
uint64_t To, uint64_t Count,
uint64_t Mispreds) {
FuncBranchData *AggrData = Func.getBranchData();
if (!AggrData) {
AggrData = &FuncsToBranches[Func.getNames()[0]];
@ -526,21 +583,19 @@ DataAggregator::doIntraBranch(BinaryFunction &Func, const LBREntry &Branch) {
Func.setBranchData(AggrData);
}
AggrData->bumpBranchCount(Branch.From - Func.getAddress(),
Branch.To - Func.getAddress(),
Branch.Mispred);
AggrData->bumpBranchCount(From - Func.getAddress(), To - Func.getAddress(),
Count, Mispreds);
return true;
}
bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
BinaryFunction *ToFunc,
const LBREntry &Branch) {
BinaryFunction *ToFunc, uint64_t From,
uint64_t To, uint64_t Count,
uint64_t Mispreds) {
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();
@ -551,7 +606,7 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
}
From -= FromFunc->getAddress();
FromFunc->recordExit(From, Branch.Mispred);
FromFunc->recordExit(From, Mispreds, Count);
}
if (ToFunc) {
DstFunc = ToFunc->getNames()[0];
@ -563,44 +618,44 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
}
To -= ToFunc->getAddress();
ToFunc->recordEntry(To, Branch.Mispred);
ToFunc->recordEntry(To, Mispreds, Count);
}
if (FromAggrData)
FromAggrData->bumpCallCount(From, Location(!DstFunc.empty(), DstFunc, To),
Branch.Mispred);
Count, Mispreds);
if (ToAggrData)
ToAggrData->bumpEntryCount(Location(!SrcFunc.empty(), SrcFunc, From), To,
Branch.Mispred);
Count, Mispreds);
return true;
}
bool DataAggregator::doBranch(const LBREntry &Branch) {
auto *FromFunc = getBinaryFunctionContainingAddress(Branch.From);
auto *ToFunc = getBinaryFunctionContainingAddress(Branch.To);
bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
uint64_t Mispreds) {
auto *FromFunc = getBinaryFunctionContainingAddress(From);
auto *ToFunc = getBinaryFunctionContainingAddress(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);
FromFunc->recordBranch(From - FromFunc->getAddress(),
To - FromFunc->getAddress(), Count, Mispreds);
return doIntraBranch(*FromFunc, From, To, Count, Mispreds);
}
return doInterBranch(FromFunc, ToFunc, Branch);
return doInterBranch(FromFunc, ToFunc, From, To, Count, Mispreds);
}
bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second) {
bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
uint64_t Count) {
auto *FromFunc = getBinaryFunctionContainingAddress(First.To);
auto *ToFunc = getBinaryFunctionContainingAddress(Second.From);
if (!FromFunc || !ToFunc) {
++NumLongRangeTraces;
NumLongRangeTraces += Count;
return false;
}
if (FromFunc != ToFunc) {
++NumInvalidTraces;
NumInvalidTraces += Count;
DEBUG(dbgs() << "Trace starting in " << FromFunc->getPrintName() << " @ "
<< Twine::utohexstr(First.To - FromFunc->getAddress())
<< " and ending in " << ToFunc->getPrintName() << " @ "
@ -610,17 +665,15 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second) {
return false;
}
auto FTs = FromFunc->getFallthroughsInTrace(First, Second);
auto FTs = FromFunc->getFallthroughsInTrace(First, Second, Count);
if (!FTs) {
++NumInvalidTraces;
NumInvalidTraces += Count;
return false;
}
for (const auto &Pair : *FTs) {
doIntraBranch(*FromFunc,
LBREntry{Pair.first + FromFunc->getAddress(),
Pair.second + FromFunc->getAddress(),
false});
doIntraBranch(*FromFunc, Pair.first + FromFunc->getAddress(),
Pair.second + FromFunc->getAddress(), Count, false);
}
return true;
@ -802,6 +855,83 @@ ErrorOr<PerfMemSample> DataAggregator::parseMemSample() {
return PerfMemSample{PCRes.get(), AddrRes.get()};
}
ErrorOr<Location> DataAggregator::parseLocationOrOffset() {
auto parseOffset = [this]() -> ErrorOr<Location> {
auto Res = parseHexField(FieldSeparator);
if (std::error_code EC = Res.getError())
return EC;
return Location(Res.get());
};
auto Sep = ParsingBuf.find_first_of(" \n");
if (Sep == StringRef::npos)
return parseOffset();
auto LookAhead = ParsingBuf.substr(0, Sep);
if (LookAhead.find_first_of(":") == StringRef::npos)
return parseOffset();
auto BuildID = parseString(':');
if (std::error_code EC = BuildID.getError())
return EC;
auto Offset = parseHexField(FieldSeparator);
if (std::error_code EC = Offset.getError())
return EC;
return Location(true, BuildID.get(), Offset.get());
}
ErrorOr<AggregatedLBREntry> DataAggregator::parseAggregatedLBREntry() {
while (checkAndConsumeFS()) {}
auto TypeOrErr = parseString(FieldSeparator);
if (std::error_code EC = TypeOrErr.getError())
return EC;
auto Type{AggregatedLBREntry::BRANCH};
if (TypeOrErr.get() == "B") {
Type = AggregatedLBREntry::BRANCH;
} else if (TypeOrErr.get() == "F") {
Type = AggregatedLBREntry::FT;
} else if (TypeOrErr.get() == "f") {
Type = AggregatedLBREntry::FT_EXTERNAL_ORIGIN;
} else {
reportError("expected B, F or f");
return make_error_code(llvm::errc::io_error);
}
while (checkAndConsumeFS()) {}
auto From = parseLocationOrOffset();
if (std::error_code EC = From.getError())
return EC;
while (checkAndConsumeFS()) {}
auto To = parseLocationOrOffset();
if (std::error_code EC = To.getError())
return EC;
while (checkAndConsumeFS()) {}
auto Frequency = parseNumberField(FieldSeparator,
Type != AggregatedLBREntry::BRANCH);
if (std::error_code EC = Frequency.getError())
return EC;
uint64_t Mispreds{0};
if (Type == AggregatedLBREntry::BRANCH) {
while (checkAndConsumeFS()) {}
auto MispredsOrErr = parseNumberField(FieldSeparator, true);
if (std::error_code EC = MispredsOrErr.getError())
return EC;
Mispreds = static_cast<uint64_t>(MispredsOrErr.get());
}
if (!checkAndConsumeNewLine()) {
reportError("expected end of line");
return make_error_code(llvm::errc::io_error);
}
return AggregatedLBREntry{From.get(), To.get(),
static_cast<uint64_t>(Frequency.get()), Mispreds,
Type};
}
bool DataAggregator::hasData() {
if (ParsingBuf.size() == 0)
return false;
@ -836,7 +966,7 @@ std::error_code DataAggregator::parseBranchEvents() {
doTrace(LBR, *NextLBR);
++NumTraces;
}
doBranch(LBR);
doBranch(LBR.From, LBR.To, 1, LBR.Mispred);
NextLBR = &LBR;
}
}
@ -991,6 +1121,79 @@ std::error_code DataAggregator::parseMemEvents() {
return std::error_code();
}
std::error_code DataAggregator::parseAggregatedLBRSamples() {
outs() << "PERF2BOLT: Aggregating...\n";
NamedRegionTimer T("parseAggregated", "Aggregated LBR parsing", TimerGroupName,
TimerGroupDesc, opts::TimeAggregator);
uint64_t NumAggrEntries{0};
uint64_t NumTraces{0};
while (hasData()) {
auto AggrEntryRes = parseAggregatedLBREntry();
if (std::error_code EC = AggrEntryRes.getError())
return EC;
auto &AggrEntry = AggrEntryRes.get();
++NumAggrEntries;
switch (AggrEntry.EntryType) {
case AggregatedLBREntry::BRANCH:
doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count,
AggrEntry.Mispreds);
break;
case AggregatedLBREntry::FT:
case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: {
LBREntry First{AggrEntry.EntryType == AggregatedLBREntry::FT
? AggrEntry.From.Offset
: 0,
AggrEntry.From.Offset, false};
LBREntry Second{AggrEntry.To.Offset, AggrEntry.To.Offset, false};
doTrace(First, Second, AggrEntry.Count);
++NumTraces;
break;
}
}
}
outs() << "PERF2BOLT: Read " << NumAggrEntries << " aggregated 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";
dump();
return std::error_code();
}
ErrorOr<std::pair<StringRef, int64_t>> DataAggregator::parseTaskPID() {
while (checkAndConsumeFS()) {}

View File

@ -42,6 +42,16 @@ struct PerfMemSample {
uint64_t Addr;
};
/// Used for parsing specific pre-aggregated input files.
struct AggregatedLBREntry {
enum Type : char { BRANCH = 0, FT, FT_EXTERNAL_ORIGIN };
Location From;
Location To;
uint64_t Count;
uint64_t Mispreds;
Type EntryType;
};
/// DataAggregator inherits all parsing logic from DataReader as well as
/// its data structures used to represent aggregated profile data in memory.
///
@ -132,18 +142,21 @@ class DataAggregator : public DataReader {
bool doSample(BinaryFunction &Func, const uint64_t Address);
/// Register an intraprocedural branch \p Branch.
bool doIntraBranch(BinaryFunction &Func, const LBREntry &Branch);
bool doIntraBranch(BinaryFunction &Func, uint64_t From, uint64_t To,
uint64_t Count, uint64_t Mispreds);
/// Register an interprocedural branch from \p FromFunc to \p ToFunc with
/// offsets \p From and \p To, respectively.
bool doInterBranch(BinaryFunction *FromFunc, BinaryFunction *ToFunc,
const LBREntry &Branch);
uint64_t From, uint64_t To, uint64_t Count,
uint64_t Mispreds);
/// Register a \p Branch.
bool doBranch(const LBREntry &Branch);
bool doBranch(uint64_t From, uint64_t To, uint64_t Count, uint64_t Mispreds);
/// Register a trace between two LBR entries supplied in execution order.
bool doTrace(const LBREntry &First, const LBREntry &Second);
bool doTrace(const LBREntry &First, const LBREntry &Second,
uint64_t Count = 1);
/// Parser helpers
/// Return false if we exhausted our parser buffer and finished parsing
@ -162,6 +175,13 @@ class DataAggregator : public DataReader {
/// address.
ErrorOr<PerfMemSample> parseMemSample();
/// Parse pre-aggregated LBR samples created by an external tool
ErrorOr<AggregatedLBREntry> parseAggregatedLBREntry();
/// Parse either buildid:offset or just offset, representing a location in the
/// binary. Used exclusevely for pre-aggregated LBR samples.
ErrorOr<Location> parseLocationOrOffset();
/// Check if a field separator is the next char to parse and, if yes, consume
/// it and return true
bool checkAndConsumeFS();
@ -181,6 +201,10 @@ class DataAggregator : public DataReader {
/// Parse the full output generated by perf script to report memory events.
std::error_code parseMemEvents();
/// Parse the full output of pre-aggregated LBR samples generated by
/// an external tool.
std::error_code parseAggregatedLBRSamples();
/// Parse a single line of a PERF_RECORD_COMM event looking for an association
/// between the binary name and its PID. On success return a <FileName, PID>
/// pair.
@ -197,6 +221,50 @@ class DataAggregator : public DataReader {
/// and return a file name matching a given \p FileBuildID.
Optional<StringRef> getFileNameForBuildID(StringRef FileBuildID);
/// Coordinate reading and parsing of pre-aggregated file
///
/// The regular perf2bolt aggregation job is to read perf output directly.
/// However, if the data is coming from a database instead of perf, one could
/// write a query to produce a pre-aggregated file. This function deals with
/// this case.
///
/// The pre-aggregated file contains aggregated LBR data, but without binary
/// knowledge. BOLT will parse it and, using information from the disassembled
/// binary, augment it with fall-through edge frequency information. After this
/// step is finished, this data can be either written to disk to be consumed by
/// BOLT later, or can be used by BOLT immediately if kept in memory.
///
/// File format syntax:
/// {B|F|f} [<start_id>:]<start_offset> [<end_id>:]<end_offset> <count>
/// [<mispred_count>]
///
/// B - indicates an aggregated branch
/// F - an aggregated fall-through
/// f - an aggregated fall-through with external origin - used to disambiguate
/// between a return hitting a basic block head and a regular internal
/// jump to the block
///
/// <start_id> - build id of the object containing the start address. We can skip it
/// for the main binary and use "X" for an unknown object. This will save some space
/// and facilitate human parsing.
///
/// <start_offset> - hex offset from the object base load address (0 for the main
/// executable unless it's PIE) to the start address.
///
/// <end_id>, <end_offset> - same for the end address.
///
/// <count> - total aggregated count of the branch or a fall-through.
///
/// <mispred_count> - the number of times the branch was mispredicted. Omitted for
/// fall-throughs.
///
/// Example:
/// F 41be50 41be50 3
/// F 41be90 41be90 4
/// B 4b1942 39b57f0 3 0
/// B 4b196f 4b19e0 2 0
bool processPreAggregated();
public:
DataAggregator(raw_ostream &Diag, StringRef BinaryName)
: DataReader(Diag), BinaryName(llvm::sys::path::filename(BinaryName)) {}

View File

@ -130,46 +130,44 @@ void FuncSampleData::bumpCount(uint64_t Offset) {
}
void FuncBranchData::bumpBranchCount(uint64_t OffsetFrom, uint64_t OffsetTo,
bool Mispred) {
uint64_t Count, uint64_t Mispreds) {
auto Iter = IntraIndex[OffsetFrom].find(OffsetTo);
if (Iter == IntraIndex[OffsetFrom].end()) {
Data.emplace_back(Location(true, Name, OffsetFrom),
Location(true, Name, OffsetTo), Mispred, 1);
Location(true, Name, OffsetTo), Mispreds, Count);
IntraIndex[OffsetFrom][OffsetTo] = Data.size() - 1;
return;
}
auto &BI = Data[Iter->second];
++BI.Branches;
if (Mispred)
++BI.Mispreds;
BI.Branches += Count;
BI.Mispreds += Mispreds;
}
void FuncBranchData::bumpCallCount(uint64_t OffsetFrom, const Location &To,
bool Mispred) {
uint64_t Count, uint64_t Mispreds) {
auto Iter = InterIndex[OffsetFrom].find(To);
if (Iter == InterIndex[OffsetFrom].end()) {
Data.emplace_back(Location(true, Name, OffsetFrom), To, Mispred, 1);
Data.emplace_back(Location(true, Name, OffsetFrom), To, Mispreds, Count);
InterIndex[OffsetFrom][To] = Data.size() - 1;
return;
}
auto &BI = Data[Iter->second];
++BI.Branches;
if (Mispred)
++BI.Mispreds;
BI.Branches += Count;
BI.Mispreds += Mispreds;
}
void FuncBranchData::bumpEntryCount(const Location &From, uint64_t OffsetTo,
bool Mispred) {
uint64_t Count, uint64_t Mispreds) {
auto Iter = EntryIndex[OffsetTo].find(From);
if (Iter == EntryIndex[OffsetTo].end()) {
EntryData.emplace_back(From, Location(true, Name, OffsetTo), Mispred, 1);
EntryData.emplace_back(From, Location(true, Name, OffsetTo), Mispreds,
Count);
EntryIndex[OffsetTo][From] = EntryData.size() - 1;
return;
}
auto &BI = EntryData[Iter->second];
++BI.Branches;
if (Mispred)
++BI.Mispreds;
BI.Branches += Count;
BI.Mispreds += Mispreds;
}
void BranchInfo::mergeWith(const BranchInfo &BI) {
@ -306,9 +304,9 @@ ErrorOr<StringRef> DataReader::parseString(char EndChar, bool EndNl) {
// If EndNl was set and nl was found instead of EndChar, do not consume the
// new line.
bool EndNlInstreadOfEndChar =
bool EndNlInsteadOfEndChar =
ParsingBuf[StringEnd] == '\n' && EndChar != '\n';
unsigned End = EndNlInstreadOfEndChar ? StringEnd : StringEnd + 1;
unsigned End = EndNlInsteadOfEndChar ? StringEnd : StringEnd + 1;
ParsingBuf = ParsingBuf.drop_front(End);
if (EndChar == '\n') {

View File

@ -168,9 +168,12 @@ struct FuncBranchData {
DenseMap<uint64_t, DenseMap<Location, size_t>> InterIndex;
DenseMap<uint64_t, DenseMap<Location, size_t>> EntryIndex;
void bumpBranchCount(uint64_t OffsetFrom, uint64_t OffsetTo, bool Mispred);
void bumpCallCount(uint64_t OffsetFrom, const Location &To, bool Mispred);
void bumpEntryCount(const Location &From, uint64_t OffsetTo, bool Mispred);
void bumpBranchCount(uint64_t OffsetFrom, uint64_t OffsetTo, uint64_t Count,
uint64_t Mispreds);
void bumpCallCount(uint64_t OffsetFrom, const Location &To, uint64_t Count,
uint64_t Mispreds);
void bumpEntryCount(const Location &From, uint64_t OffsetTo, uint64_t Count,
uint64_t Mispreds);
};
/// MemInfo represents a single memory load from an address \p Addr at an \p

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
B X:7f36d18d60c0 400bbc 2 0
B 400ad1 400e00 2 0
B 400b10 4005f0 1 0
B 400bb7 400610 1 0
B 4005f0 X:7f36d18f2ce0 1 0
B 4011a0 4011a9 33 4
B 4011ad 401180 58 0
F 401170 4011b2 22

View File

@ -0,0 +1,42 @@
# This script checks that perf2bolt is reading pre-aggregated perf information
# correctly for a simple example. The perf.data of this example was generated
# with the following command:
#
# $ perf record -j any,u -e branch -o perf.data -- ./blarge
#
# blarge is the binary for "basicmath large inputs" taken from Mibench.
RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe
RUN: perf2bolt %t.exe -o %t -pa -p %p/Inputs/pre-aggregated.txt -w %t.new
RUN: cat %t | sort | FileCheck %s -check-prefix=PERF2BOLT
RUN: cat %t.new | FileCheck %s -check-prefix=NEWFORMAT
PERF2BOLT: 0 [unknown] 7f36d18d60c0 1 main 53c 0 2
PERF2BOLT: 1 main 451 1 SolveCubic 0 0 2
PERF2BOLT: 1 main 490 0 [unknown] 4005f0 0 1
PERF2BOLT: 1 main 537 0 [unknown] 400610 0 1
PERF2BOLT: 1 usqrt 30 1 usqrt 32 0 22
PERF2BOLT: 1 usqrt 30 1 usqrt 39 4 33
PERF2BOLT: 1 usqrt 35 1 usqrt 39 0 22
PERF2BOLT: 1 usqrt 3d 1 usqrt 10 0 58
PERF2BOLT: 1 usqrt 3d 1 usqrt 3f 0 22
PERF2BOLT: 1 usqrt 8 1 usqrt 10 0 22
NEWFORMAT: - name: usqrt
NEWFORMAT: fid: 7
NEWFORMAT: hash: 0x7EA5C50FA9564489
NEWFORMAT: exec: 0
NEWFORMAT: nblocks: 5
NEWFORMAT: blocks:
NEWFORMAT: - bid: 0
NEWFORMAT: insns: 3
NEWFORMAT: succ: [ { bid: 1, cnt: 22 } ]
NEWFORMAT: - bid: 1
NEWFORMAT: insns: 9
NEWFORMAT: succ: [ { bid: 3, cnt: 33, mis: 4 }, { bid: 2, cnt: 22 } ]
NEWFORMAT: - bid: 2
NEWFORMAT: insns: 2
NEWFORMAT: succ: [ { bid: 3, cnt: 22 } ]
NEWFORMAT: - bid: 3
NEWFORMAT: insns: 2
NEWFORMAT: succ: [ { bid: 1, cnt: 58 }, { bid: 4, cnt: 22 } ]