Add inline stack streaming to binary sample profiles.
authorDiego Novillo <dnovillo@google.com>
Fri, 9 Oct 2015 17:54:24 +0000 (17:54 +0000)
committerDiego Novillo <dnovillo@google.com>
Fri, 9 Oct 2015 17:54:24 +0000 (17:54 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@249861 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ProfileData/SampleProfReader.h
lib/ProfileData/SampleProfReader.cpp
lib/ProfileData/SampleProfWriter.cpp
test/Transforms/SampleProfile/Inputs/fnptr.binprof
test/tools/llvm-profdata/Inputs/inline-samples.afdo [new file with mode: 0644]
test/tools/llvm-profdata/inline-samples.test [new file with mode: 0644]

index 9f4d53902e1c4e1687c1f418f944321423448352..e2976e2efab75a7db3bb354ce3ad9598bb68def1 100644 (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;
 
index 6d7d182e46c8325a3a5ac1ef6149b6c6dffbd9b4..c620d4ca84a5d3b7f02b333a939f8fa0de991ee9 100644 (file)
@@ -378,62 +378,94 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readString() {
   return Str;
 }
 
-std::error_code SampleProfileReaderBinary::read() {
-  while (!at_eof()) {
-    auto FName(readString());
-    if (std::error_code EC = FName.getError())
-      return EC;
+std::error_code
+SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) {
+  auto Val = readNumber<unsigned>();
+  if (std::error_code EC = Val.getError())
+    return EC;
+  FProfile.addTotalSamples(*Val);
 
-    Profiles[*FName] = FunctionSamples();
-    FunctionSamples &FProfile = Profiles[*FName];
+  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;
 
-    auto Val = readNumber<unsigned>();
-    if (std::error_code EC = Val.getError())
+  for (unsigned I = 0; I < *NumRecords; ++I) {
+    auto LineOffset = readNumber<uint64_t>();
+    if (std::error_code EC = LineOffset.getError())
       return EC;
-    FProfile.addTotalSamples(*Val);
 
-    Val = readNumber<unsigned>();
-    if (std::error_code EC = Val.getError())
+    auto Discriminator = readNumber<uint64_t>();
+    if (std::error_code EC = Discriminator.getError())
       return EC;
-    FProfile.addHeadSamples(*Val);
 
-    // Read the samples in the body.
-    auto NumRecords = readNumber<unsigned>();
-    if (std::error_code EC = NumRecords.getError())
+    auto NumSamples = readNumber<uint64_t>();
+    if (std::error_code EC = NumSamples.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 NumCalls = readNumber<unsigned>();
+    if (std::error_code EC = NumCalls.getError())
+      return EC;
 
-      auto NumSamples = readNumber<uint64_t>();
-      if (std::error_code EC = NumSamples.getError())
+    for (unsigned J = 0; J < *NumCalls; ++J) {
+      auto CalledFunction(readString());
+      if (std::error_code EC = CalledFunction.getError())
         return EC;
 
-      auto NumCalls = readNumber<unsigned>();
-      if (std::error_code EC = NumCalls.getError())
+      auto CalledFunctionSamples = readNumber<uint64_t>();
+      if (std::error_code EC = CalledFunctionSamples.getError())
         return EC;
 
-      for (unsigned J = 0; J < *NumCalls; ++J) {
-        auto CalledFunction(readString());
-        if (std::error_code EC = CalledFunction.getError())
-          return EC;
+      FProfile.addCalledTargetSamples(*LineOffset, *Discriminator,
+                                      *CalledFunction, *CalledFunctionSamples);
+    }
 
-        auto CalledFunctionSamples = readNumber<uint64_t>();
-        if (std::error_code EC = CalledFunctionSamples.getError())
-          return EC;
+    FProfile.addBodySamples(*LineOffset, *Discriminator, *NumSamples);
+  }
 
-        FProfile.addCalledTargetSamples(*LineOffset, *Discriminator,
-                                        *CalledFunction,
-                                        *CalledFunctionSamples);
-      }
+  // Read all the samples for inlined function calls.
+  auto NumCallsites = readNumber<unsigned>();
+  if (std::error_code EC = NumCallsites.getError())
+    return EC;
 
-      FProfile.addBodySamples(*LineOffset, *Discriminator, *NumSamples);
-    }
+  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());
+    if (std::error_code EC = FName.getError())
+      return EC;
+
+    Profiles[*FName] = FunctionSamples();
+    FunctionSamples &FProfile = Profiles[*FName];
+
+    if (std::error_code EC = readProfile(FProfile))
+      return EC;
   }
 
   return sampleprof_error::success;
index e6a4d7d132103aba5c527203e6ea399728851411..6be884b13e0e5511df261923ca680c7cacc998e0 100644 (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;
 }
 
index 14d7fd555dae85bec9e11b6a61bdb05466757aed..8cbe646c16ee598d8c4eaad145635cbfd7a8fabc 100644 (file)
Binary files a/test/Transforms/SampleProfile/Inputs/fnptr.binprof and b/test/Transforms/SampleProfile/Inputs/fnptr.binprof differ
diff --git a/test/tools/llvm-profdata/Inputs/inline-samples.afdo b/test/tools/llvm-profdata/Inputs/inline-samples.afdo
new file mode 100644 (file)
index 0000000..f8680d8
--- /dev/null
@@ -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
diff --git a/test/tools/llvm-profdata/inline-samples.test b/test/tools/llvm-profdata/inline-samples.test
new file mode 100644 (file)
index 0000000..b16ac48
--- /dev/null
@@ -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