Add inline stack streaming to binary sample profiles.

With this patch we can now read and write inline stacks in sample
profiles to the binary encoded profiles.

In a subsequent patch, I will add a string table to the binary encoding.
Right now function names are emitted as strings every time we find them.
This is too bloated and will produce large files in applications with
lots of inlining.

llvm-svn: 249861
This commit is contained in:
Diego Novillo 2015-10-09 17:54:24 +00:00
parent 5da6833905
commit 550aed332d
6 changed files with 143 additions and 49 deletions

View File

@ -157,6 +157,9 @@ protected:
/// \brief Return true if we've reached the end of file.
bool at_eof() const { return Data >= End; }
/// Read the contents of the given profile instance.
std::error_code readProfile(FunctionSamples &FProfile);
/// \brief Points to the current location in the buffer.
const uint8_t *Data;

View File

@ -378,6 +378,83 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readString() {
return Str;
}
std::error_code
SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) {
auto Val = readNumber<unsigned>();
if (std::error_code EC = Val.getError())
return EC;
FProfile.addTotalSamples(*Val);
Val = readNumber<unsigned>();
if (std::error_code EC = Val.getError())
return EC;
FProfile.addHeadSamples(*Val);
// Read the samples in the body.
auto NumRecords = readNumber<unsigned>();
if (std::error_code EC = NumRecords.getError())
return EC;
for (unsigned I = 0; I < *NumRecords; ++I) {
auto LineOffset = readNumber<uint64_t>();
if (std::error_code EC = LineOffset.getError())
return EC;
auto Discriminator = readNumber<uint64_t>();
if (std::error_code EC = Discriminator.getError())
return EC;
auto NumSamples = readNumber<uint64_t>();
if (std::error_code EC = NumSamples.getError())
return EC;
auto NumCalls = readNumber<unsigned>();
if (std::error_code EC = NumCalls.getError())
return EC;
for (unsigned J = 0; J < *NumCalls; ++J) {
auto CalledFunction(readString());
if (std::error_code EC = CalledFunction.getError())
return EC;
auto CalledFunctionSamples = readNumber<uint64_t>();
if (std::error_code EC = CalledFunctionSamples.getError())
return EC;
FProfile.addCalledTargetSamples(*LineOffset, *Discriminator,
*CalledFunction, *CalledFunctionSamples);
}
FProfile.addBodySamples(*LineOffset, *Discriminator, *NumSamples);
}
// Read all the samples for inlined function calls.
auto NumCallsites = readNumber<unsigned>();
if (std::error_code EC = NumCallsites.getError())
return EC;
for (unsigned J = 0; J < *NumCallsites; ++J) {
auto LineOffset = readNumber<uint64_t>();
if (std::error_code EC = LineOffset.getError())
return EC;
auto Discriminator = readNumber<uint64_t>();
if (std::error_code EC = Discriminator.getError())
return EC;
auto FName(readString());
if (std::error_code EC = FName.getError())
return EC;
FunctionSamples &CalleeProfile = FProfile.functionSamplesAt(
CallsiteLocation(*LineOffset, *Discriminator, *FName));
if (std::error_code EC = readProfile(CalleeProfile))
return EC;
}
return sampleprof_error::success;
}
std::error_code SampleProfileReaderBinary::read() {
while (!at_eof()) {
auto FName(readString());
@ -387,53 +464,8 @@ std::error_code SampleProfileReaderBinary::read() {
Profiles[*FName] = FunctionSamples();
FunctionSamples &FProfile = Profiles[*FName];
auto Val = readNumber<unsigned>();
if (std::error_code EC = Val.getError())
if (std::error_code EC = readProfile(FProfile))
return EC;
FProfile.addTotalSamples(*Val);
Val = readNumber<unsigned>();
if (std::error_code EC = Val.getError())
return EC;
FProfile.addHeadSamples(*Val);
// Read the samples in the body.
auto NumRecords = readNumber<unsigned>();
if (std::error_code EC = NumRecords.getError())
return EC;
for (unsigned I = 0; I < *NumRecords; ++I) {
auto LineOffset = readNumber<uint64_t>();
if (std::error_code EC = LineOffset.getError())
return EC;
auto Discriminator = readNumber<uint64_t>();
if (std::error_code EC = Discriminator.getError())
return EC;
auto NumSamples = readNumber<uint64_t>();
if (std::error_code EC = NumSamples.getError())
return EC;
auto NumCalls = readNumber<unsigned>();
if (std::error_code EC = NumCalls.getError())
return EC;
for (unsigned J = 0; J < *NumCalls; ++J) {
auto CalledFunction(readString());
if (std::error_code EC = CalledFunction.getError())
return EC;
auto CalledFunctionSamples = readNumber<uint64_t>();
if (std::error_code EC = CalledFunctionSamples.getError())
return EC;
FProfile.addCalledTargetSamples(*LineOffset, *Discriminator,
*CalledFunction,
*CalledFunctionSamples);
}
FProfile.addBodySamples(*LineOffset, *Discriminator, *NumSamples);
}
}
return sampleprof_error::success;

View File

@ -84,14 +84,13 @@ SampleProfileWriterBinary::SampleProfileWriterBinary(StringRef F,
/// \returns true if the samples were written successfully, false otherwise.
bool SampleProfileWriterBinary::write(StringRef FName,
const FunctionSamples &S) {
if (S.empty())
return true;
OS << FName;
encodeULEB128(0, OS);
encodeULEB128(S.getTotalSamples(), OS);
encodeULEB128(S.getHeadSamples(), OS);
encodeULEB128(S.getBodySamples().size(), OS);
// Emit all the body samples.
for (const auto &I : S.getBodySamples()) {
LineLocation Loc = I.first;
const SampleRecord &Sample = I.second;
@ -108,6 +107,16 @@ bool SampleProfileWriterBinary::write(StringRef FName,
}
}
// Recursively emit all the callsite samples.
encodeULEB128(S.getCallsiteSamples().size(), OS);
for (const auto &J : S.getCallsiteSamples()) {
CallsiteLocation Loc = J.first;
const FunctionSamples &CalleeSamples = J.second;
encodeULEB128(Loc.LineOffset, OS);
encodeULEB128(Loc.Discriminator, OS);
write(Loc.CalleeName, CalleeSamples);
}
return true;
}

View File

@ -0,0 +1,20 @@
main:366846:0
2.1: 60401
4: 0
3: 0
0: 0
2.3: 60401
1: 0
2.3: _Z3fool:246044
1.2: 39280
1.4: 46871
1: 60401
1.3: _Z3bari:0
1.2: 0
1.1: 0
1.8: _Z3bari:0
1.2: 0
1.1: 0
1.7: _Z3bari:99492
1.2: 46732
1.1: 52760

View File

@ -0,0 +1,30 @@
Tests for conversion between text and binary encoded sample profiles.
1- Encode the original profile into binary form. All the tests below will use
the binary profile.
RUN: llvm-profdata merge --sample %p/Inputs/inline-samples.afdo -o %t.profbin
2- Show all functions. This profile has a single main() function with several
inlined callees.
RUN: llvm-profdata show --sample %t.profbin | FileCheck %s --check-prefix=SHOW1
SHOW1: Function: main: 366846, 0, 6 sampled lines
SHOW1: line offset: 2, discriminator: 3, inlined callee: _Z3fool: 246044, 0, 3 sampled lines
SHOW1: line offset: 1, discriminator: 3, inlined callee: _Z3bari: 0, 0, 2 sampled lines
SHOW1: line offset: 1, discriminator: 8, inlined callee: _Z3bari: 0, 0, 2 sampled lines
SHOW1: line offset: 1, discriminator: 7, inlined callee: _Z3bari: 99492, 0, 2 sampled lines
SHOW1: line offset: 1, discriminator: 2, number of samples: 46732
3- Convert the binary profile to text encoding and check that they are both
identical.
RUN: llvm-profdata merge --sample %t.profbin --text -o - | llvm-profdata show --sample - -o %t-bintext
RUN: llvm-profdata show --sample %p/Inputs/inline-samples.afdo -o %t-text
RUN: diff %t-bintext %t-text
4- Merge the binary and text encodings of the profile and check that the
counters have doubled.
RUN: llvm-profdata merge --sample --text %t.profbin %p/Inputs/inline-samples.afdo -o - | FileCheck %s --check-prefix=MERGE1
MERGE1: main:733692:0
MERGE1: 2.3: 120802
MERGE1: 2.3: _Z3fool:492088
MERGE1: 1.7: _Z3bari:198984
MERGE1: 1.1: 105520