summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/ProfileData/SampleProf.h18
-rw-r--r--llvm/include/llvm/ProfileData/SampleProfReader.h7
-rw-r--r--llvm/include/llvm/ProfileData/SampleProfWriter.h8
-rw-r--r--llvm/lib/ProfileData/SampleProfReader.cpp112
-rw-r--r--llvm/lib/ProfileData/SampleProfWriter.cpp23
-rw-r--r--llvm/test/tools/llvm-profdata/Inputs/gcc-sample-profile.gcovbin0 -> 1960 bytes
-rw-r--r--llvm/test/tools/llvm-profdata/gcc-gcov-sample-profile.test29
-rw-r--r--llvm/tools/llvm-profdata/llvm-profdata.cpp8
8 files changed, 151 insertions, 54 deletions
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index ef5afc7eb3b..aa715df162f 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -208,7 +208,7 @@ typedef DenseMap<CallsiteLocation, FunctionSamples> CallsiteSampleMap;
class FunctionSamples {
public:
FunctionSamples() : TotalSamples(0), TotalHeadSamples(0) {}
- void print(raw_ostream &OS = dbgs());
+ void print(raw_ostream &OS = dbgs(), unsigned Indent = 0) const;
void addTotalSamples(unsigned Num) { TotalSamples += Num; }
void addHeadSamples(unsigned Num) { TotalHeadSamples += Num; }
void addBodySamples(int LineOffset, unsigned Discriminator, unsigned Num) {
@@ -302,6 +302,22 @@ private:
/// are an offset from the start of the function.
BodySampleMap BodySamples;
+ /// Map call sites to collected samples for the called function.
+ ///
+ /// Each entry in this map corresponds to all the samples
+ /// collected for the inlined function call at the given
+ /// location. For example, given:
+ ///
+ /// void foo() {
+ /// 1 bar();
+ /// ...
+ /// 8 baz();
+ /// }
+ ///
+ /// If the bar() and baz() calls were inlined inside foo(), this
+ /// map will contain two entries. One for all the samples collected
+ /// in the call to bar() at line offset 1, the other for all the samples
+ /// collected in the call to baz() at line offset 8.
CallsiteSampleMap CallsiteSamples;
};
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index 87c871f855b..9f4d53902e1 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -189,7 +189,7 @@ struct SourceInfo {
uint32_t Discriminator;
};
-typedef std::vector<SourceInfo> SourceStack;
+typedef SmallVector<FunctionSamples *, 10> InlineCallStack;
// Supported histogram types in GCC. Currently, we only need support for
// call target histograms.
@@ -220,9 +220,8 @@ public:
protected:
std::error_code readNameTable();
- std::error_code addSourceCount(StringRef Name, const SourceStack &Src,
- uint64_t Count);
- std::error_code readOneFunctionProfile(const SourceStack &Stack, bool Update);
+ std::error_code readOneFunctionProfile(const InlineCallStack &InlineStack,
+ bool Update, uint32_t Offset);
std::error_code readFunctionProfiles();
std::error_code skipNextWord();
template <typename T> ErrorOr<T> readNumber();
diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h
index 302a82d3286..d3fa7429a6d 100644
--- a/llvm/include/llvm/ProfileData/SampleProfWriter.h
+++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h
@@ -84,12 +84,18 @@ protected:
class SampleProfileWriterText : public SampleProfileWriter {
public:
SampleProfileWriterText(StringRef F, std::error_code &EC)
- : SampleProfileWriter(F, EC, sys::fs::F_Text) {}
+ : SampleProfileWriter(F, EC, sys::fs::F_Text), Indent(0) {}
bool write(StringRef FName, const FunctionSamples &S) override;
bool write(const Module &M, StringMap<FunctionSamples> &P) {
return SampleProfileWriter::write(M, P);
}
+
+private:
+ /// Indent level to use when writing.
+ ///
+ /// This is used when printing inlined callees.
+ unsigned Indent;
};
/// \brief Sample-based profile writer (binary format).
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index 94f87996bea..6d7d182e46c 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -132,13 +132,14 @@ using namespace llvm;
/// \brief Print the samples collected for a function on stream \p OS.
///
/// \param OS Stream to emit the output to.
-void FunctionSamples::print(raw_ostream &OS) {
+void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
OS << TotalSamples << ", " << TotalHeadSamples << ", " << BodySamples.size()
<< " sampled lines\n";
for (const auto &SI : BodySamples) {
LineLocation Loc = SI.first;
const SampleRecord &Sample = SI.second;
- OS << "\tline offset: " << Loc.LineOffset
+ OS.indent(Indent);
+ OS << "line offset: " << Loc.LineOffset
<< ", discriminator: " << Loc.Discriminator
<< ", number of samples: " << Sample.getSamples();
if (Sample.hasCalls()) {
@@ -148,7 +149,15 @@ void FunctionSamples::print(raw_ostream &OS) {
}
OS << "\n";
}
- OS << "\n";
+ for (const auto &CS : CallsiteSamples) {
+ CallsiteLocation Loc = CS.first;
+ const FunctionSamples &CalleeSamples = CS.second;
+ OS.indent(Indent);
+ OS << "line offset: " << Loc.LineOffset
+ << ", discriminator: " << Loc.Discriminator
+ << ", inlined callee: " << Loc.CalleeName << ": ";
+ CalleeSamples.print(OS, Indent + 2);
+ }
}
/// \brief Dump the function profile for \p FName.
@@ -266,7 +275,7 @@ static bool ParseLine(const StringRef &Input, bool &IsCallsite, unsigned &Depth,
std::error_code SampleProfileReaderText::read() {
line_iterator LineIt(*Buffer, /*SkipBlanks=*/true, '#');
- SmallVector<FunctionSamples *, 10> InlineStack;
+ InlineCallStack InlineStack;
for (; !LineIt.is_at_eof(); ++LineIt) {
if ((*LineIt)[(*LineIt).find_first_not_of(' ')] == '#')
@@ -559,31 +568,18 @@ std::error_code SampleProfileReaderGCC::readFunctionProfiles() {
if (!GcovBuffer.readInt(NumFunctions))
return sampleprof_error::truncated;
- SourceStack Stack;
+ InlineCallStack Stack;
for (uint32_t I = 0; I < NumFunctions; ++I)
- if (std::error_code EC = readOneFunctionProfile(Stack, true))
+ if (std::error_code EC = readOneFunctionProfile(Stack, true, 0))
return EC;
return sampleprof_error::success;
}
-std::error_code SampleProfileReaderGCC::addSourceCount(StringRef Name,
- const SourceStack &Src,
- uint64_t Count) {
- if (Src.size() == 0 || Src[0].Malformed())
- return sampleprof_error::malformed;
- FunctionSamples &FProfile = Profiles[Name];
- FProfile.addTotalSamples(Count);
- // FIXME(dnovillo) - Properly update inline stack for FnName.
- FProfile.addBodySamples(Src[0].Line, Src[0].Discriminator, Count);
- return sampleprof_error::success;
-}
-
-std::error_code
-SampleProfileReaderGCC::readOneFunctionProfile(const SourceStack &Stack,
- bool Update) {
+std::error_code SampleProfileReaderGCC::readOneFunctionProfile(
+ const InlineCallStack &InlineStack, bool Update, uint32_t Offset) {
uint64_t HeadCount = 0;
- if (Stack.size() == 0)
+ if (InlineStack.size() == 0)
if (!GcovBuffer.readInt64(HeadCount))
return sampleprof_error::truncated;
@@ -597,15 +593,31 @@ SampleProfileReaderGCC::readOneFunctionProfile(const SourceStack &Stack,
if (!GcovBuffer.readInt(NumPosCounts))
return sampleprof_error::truncated;
- uint32_t NumCallSites;
- if (!GcovBuffer.readInt(NumCallSites))
+ uint32_t NumCallsites;
+ if (!GcovBuffer.readInt(NumCallsites))
return sampleprof_error::truncated;
- if (Stack.size() == 0) {
- FunctionSamples &FProfile = Profiles[Name];
- FProfile.addHeadSamples(HeadCount);
- if (FProfile.getTotalSamples() > 0)
+ FunctionSamples *FProfile = nullptr;
+ if (InlineStack.size() == 0) {
+ // If this is a top function that we have already processed, do not
+ // update its profile again. This happens in the presence of
+ // function aliases. Since these aliases share the same function
+ // body, there will be identical replicated profiles for the
+ // original function. In this case, we simply not bother updating
+ // the profile of the original function.
+ FProfile = &Profiles[Name];
+ FProfile->addHeadSamples(HeadCount);
+ if (FProfile->getTotalSamples() > 0)
Update = false;
+ } else {
+ // Otherwise, we are reading an inlined instance. The top of the
+ // inline stack contains the profile of the caller. Insert this
+ // callee in the caller's CallsiteMap.
+ FunctionSamples *CallerProfile = InlineStack.front();
+ uint32_t LineOffset = Offset >> 16;
+ uint32_t Discriminator = Offset & 0xffff;
+ FProfile = &CallerProfile->functionSamplesAt(
+ CallsiteLocation(LineOffset, Discriminator, Name));
}
for (uint32_t I = 0; I < NumPosCounts; ++I) {
@@ -621,13 +633,28 @@ SampleProfileReaderGCC::readOneFunctionProfile(const SourceStack &Stack,
if (!GcovBuffer.readInt64(Count))
return sampleprof_error::truncated;
- SourceInfo Info(Name, "", "", 0, Offset >> 16, Offset & 0xffff);
- SourceStack NewStack;
- NewStack.push_back(Info);
- NewStack.insert(NewStack.end(), Stack.begin(), Stack.end());
- if (Update)
- addSourceCount(NewStack[NewStack.size() - 1].FuncName, NewStack, Count);
+ // The line location is encoded in the offset as:
+ // high 16 bits: line offset to the start of the function.
+ // low 16 bits: discriminator.
+ uint32_t LineOffset = Offset >> 16;
+ uint32_t Discriminator = Offset & 0xffff;
+
+ InlineCallStack NewStack;
+ NewStack.push_back(FProfile);
+ NewStack.insert(NewStack.end(), InlineStack.begin(), InlineStack.end());
+ if (Update) {
+ // Walk up the inline stack, adding the samples on this line to
+ // the total sample count of the callers in the chain.
+ for (auto CallerProfile : NewStack)
+ CallerProfile->addTotalSamples(Count);
+
+ // Update the body samples for the current profile.
+ FProfile->addBodySamples(LineOffset, Discriminator, Count);
+ }
+ // Process the list of functions called at an indirect call site.
+ // These are all the targets that a function pointer (or virtual
+ // function) resolved at runtime.
for (uint32_t J = 0; J < NumTargets; J++) {
uint32_t HistVal;
if (!GcovBuffer.readInt(HistVal))
@@ -647,24 +674,25 @@ SampleProfileReaderGCC::readOneFunctionProfile(const SourceStack &Stack,
if (Update) {
FunctionSamples &TargetProfile = Profiles[TargetName];
- TargetProfile.addBodySamples(NewStack[0].Line,
- NewStack[0].Discriminator, TargetCount);
+ TargetProfile.addCalledTargetSamples(LineOffset, Discriminator,
+ TargetName, TargetCount);
}
}
}
- for (uint32_t I = 0; I < NumCallSites; I++) {
+ // Process all the inlined callers into the current function. These
+ // are all the callsites that were inlined into this function.
+ for (uint32_t I = 0; I < NumCallsites; I++) {
// The offset is encoded as:
// high 16 bits: line offset to the start of the function.
// low 16 bits: discriminator.
uint32_t Offset;
if (!GcovBuffer.readInt(Offset))
return sampleprof_error::truncated;
- SourceInfo Info(Name, "", "", 0, Offset >> 16, Offset & 0xffff);
- SourceStack NewStack;
- NewStack.push_back(Info);
- NewStack.insert(NewStack.end(), Stack.begin(), Stack.end());
- if (std::error_code EC = readOneFunctionProfile(NewStack, Update))
+ InlineCallStack NewStack;
+ NewStack.push_back(FProfile);
+ NewStack.insert(NewStack.end(), InlineStack.begin(), InlineStack.end());
+ if (std::error_code EC = readOneFunctionProfile(NewStack, Update, Offset))
return EC;
}
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index c95267ad976..e6a4d7d1321 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -31,15 +31,15 @@ using namespace llvm;
/// \brief Write samples to a text file.
bool SampleProfileWriterText::write(StringRef FName, const FunctionSamples &S) {
- if (S.empty())
- return true;
-
- OS << FName << ":" << S.getTotalSamples() << ":" << S.getHeadSamples()
- << "\n";
+ OS << FName << ":" << S.getTotalSamples();
+ if (Indent == 0)
+ OS << ":" << S.getHeadSamples();
+ OS << "\n";
for (const auto &I : S.getBodySamples()) {
LineLocation Loc = I.first;
const SampleRecord &Sample = I.second;
+ OS.indent(Indent + 1);
if (Loc.Discriminator == 0)
OS << Loc.LineOffset << ": ";
else
@@ -52,6 +52,19 @@ bool SampleProfileWriterText::write(StringRef FName, const FunctionSamples &S) {
OS << "\n";
}
+ Indent += 1;
+ for (const auto &I : S.getCallsiteSamples()) {
+ CallsiteLocation Loc = I.first;
+ const FunctionSamples &CalleeSamples = I.second;
+ OS.indent(Indent);
+ if (Loc.Discriminator == 0)
+ OS << Loc.LineOffset << ": ";
+ else
+ OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
+ write(Loc.CalleeName, CalleeSamples);
+ }
+ Indent -= 1;
+
return true;
}
diff --git a/llvm/test/tools/llvm-profdata/Inputs/gcc-sample-profile.gcov b/llvm/test/tools/llvm-profdata/Inputs/gcc-sample-profile.gcov
new file mode 100644
index 00000000000..0099280af2f
--- /dev/null
+++ b/llvm/test/tools/llvm-profdata/Inputs/gcc-sample-profile.gcov
Binary files differ
diff --git a/llvm/test/tools/llvm-profdata/gcc-gcov-sample-profile.test b/llvm/test/tools/llvm-profdata/gcc-gcov-sample-profile.test
new file mode 100644
index 00000000000..723d547555b
--- /dev/null
+++ b/llvm/test/tools/llvm-profdata/gcc-gcov-sample-profile.test
@@ -0,0 +1,29 @@
+The input gcov file has been generated on a little endian machine. Expect
+failures on big endian systems.
+
+XFAIL: powerpc64-, s390x, mips-, mips64-, sparc
+
+Tests for sample profiles encoded in GCC's gcov format.
+
+1- Show all functions. This profile has a single main() function with several
+ inlined callees.
+RUN: llvm-profdata show --sample %p/Inputs/gcc-sample-profile.gcov | FileCheck %s --check-prefix=SHOW1
+SHOW1: Function: main: 364084, 0, 6 sampled lines
+SHOW1: line offset: 2, discriminator: 3, inlined callee: _Z3fool: 243786, 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: 98558, 0, 2 sampled lines
+
+2- Convert the profile to text encoding and check that they are both
+ identical.
+RUN: llvm-profdata merge --sample %p/Inputs/gcc-sample-profile.gcov --text -o - | llvm-profdata show --sample - -o %t-text
+RUN: llvm-profdata show --sample %p/Inputs/gcc-sample-profile.gcov -o %t-gcov
+RUN: diff %t-text %t-gcov
+
+4- Merge the gcov and text encodings of the profile and check that the
+ counters have doubled.
+RUN: llvm-profdata merge --sample --text %p/Inputs/gcc-sample-profile.gcov -o %t-gcov
+RUN: llvm-profdata merge --sample --text %p/Inputs/gcc-sample-profile.gcov %t-gcov -o - | FileCheck %s --check-prefix=MERGE1
+MERGE1: main:728168:0
+MERGE1: 2.3: 120298
+MERGE1: 2.3: _Z3fool:487572
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp
index fdf22c3642f..382ac908da4 100644
--- a/llvm/tools/llvm-profdata/llvm-profdata.cpp
+++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -77,13 +77,19 @@ static void mergeSampleProfile(const cl::list<std::string> &Inputs,
auto Writer = std::move(WriterOrErr.get());
StringMap<FunctionSamples> ProfileMap;
+ SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
for (const auto &Filename : Inputs) {
auto ReaderOrErr =
SampleProfileReader::create(Filename, getGlobalContext());
if (std::error_code EC = ReaderOrErr.getError())
exitWithError(EC.message(), Filename);
- auto Reader = std::move(ReaderOrErr.get());
+ // We need to keep the readers around until after all the files are
+ // read so that we do not lose the function names stored in each
+ // reader's memory. The function names are needed to write out the
+ // merged profile map.
+ Readers.push_back(std::move(ReaderOrErr.get()));
+ const auto Reader = Readers.back().get();
if (std::error_code EC = Reader->read())
exitWithError(EC.message(), Filename);
OpenPOWER on IntegriCloud