[PGO] Handle and report overflow during profile merge for all types of data
authorNathan Slingerland <slingn@gmail.com>
Wed, 16 Dec 2015 21:45:43 +0000 (21:45 +0000)
committerNathan Slingerland <slingn@gmail.com>
Wed, 16 Dec 2015 21:45:43 +0000 (21:45 +0000)
Summary: Surface counter overflow when merging profile data. Merging still occurs on overflow but counts saturate to the maximum representable value. Overflow is reported to the user.

Reviewers: davidxl, dnovillo, silvas

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D15547

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

12 files changed:
include/llvm/ProfileData/InstrProf.h
include/llvm/ProfileData/SampleProf.h
lib/ProfileData/SampleProf.cpp
lib/ProfileData/SampleProfReader.cpp
test/tools/llvm-profdata/Inputs/overflow-instr.proftext [new file with mode: 0644]
test/tools/llvm-profdata/Inputs/overflow-sample.proftext [new file with mode: 0644]
test/tools/llvm-profdata/overflow-instr.test [new file with mode: 0644]
test/tools/llvm-profdata/overflow-sample.test [new file with mode: 0644]
test/tools/llvm-profdata/overflow.proftext [deleted file]
tools/llvm-profdata/llvm-profdata.cpp
unittests/ProfileData/InstrProfTest.cpp
unittests/ProfileData/SampleProfTest.cpp

index a1d5ed97e0b49b5fd07ca4a13f8905431f100f55..dca36a4b63b57bb1fecd9af623c5c59f461567fd 100644 (file)
@@ -184,6 +184,16 @@ inline std::error_code make_error_code(instrprof_error E) {
   return std::error_code(static_cast<int>(E), instrprof_category());
 }
 
+inline instrprof_error MergeResult(instrprof_error &Accumulator,
+                                   instrprof_error Result) {
+  // Prefer first error encountered as later errors may be secondary effects of
+  // the initial problem.
+  if (Accumulator == instrprof_error::success &&
+      Result != instrprof_error::success)
+    Accumulator = Result;
+  return Accumulator;
+}
+
 enum InstrProfValueKind : uint32_t {
 #define VALUE_PROF_KIND(Enumerator, Value) Enumerator = Value,
 #include "llvm/ProfileData/InstrProfData.inc"
@@ -224,30 +234,34 @@ struct InstrProfValueSiteRecord {
 
   /// Merge data from another InstrProfValueSiteRecord
   /// Optionally scale merged counts by \p Weight.
-  void mergeValueData(InstrProfValueSiteRecord &Input, uint64_t Weight = 1) {
+  instrprof_error mergeValueData(InstrProfValueSiteRecord &Input,
+                                 uint64_t Weight = 1) {
     this->sortByTargetValues();
     Input.sortByTargetValues();
     auto I = ValueData.begin();
     auto IE = ValueData.end();
+    instrprof_error Result = instrprof_error::success;
     for (auto J = Input.ValueData.begin(), JE = Input.ValueData.end(); J != JE;
          ++J) {
       while (I != IE && I->Value < J->Value)
         ++I;
       if (I != IE && I->Value == J->Value) {
-        // FIXME: Improve handling of counter overflow.
         uint64_t JCount = J->Count;
         bool Overflowed;
         if (Weight > 1) {
           JCount = SaturatingMultiply(JCount, Weight, &Overflowed);
-          assert(!Overflowed && "Value data counter overflowed!");
+          if (Overflowed)
+            Result = instrprof_error::counter_overflow;
         }
         I->Count = SaturatingAdd(I->Count, JCount, &Overflowed);
-        assert(!Overflowed && "Value data counter overflowed!");
+        if (Overflowed)
+          Result = instrprof_error::counter_overflow;
         ++I;
         continue;
       }
       ValueData.insert(I, *J);
     }
+    return Result;
   }
 };
 
@@ -352,9 +366,11 @@ private:
         getValueSitesForKind(ValueKind);
     std::vector<InstrProfValueSiteRecord> &OtherSiteRecords =
         Src.getValueSitesForKind(ValueKind);
+    instrprof_error Result = instrprof_error::success;
     for (uint32_t I = 0; I < ThisNumValueSites; I++)
-      ThisSiteRecords[I].mergeValueData(OtherSiteRecords[I], Weight);
-    return instrprof_error::success;
+      MergeResult(Result, ThisSiteRecords[I].mergeValueData(OtherSiteRecords[I],
+                                                            Weight));
+    return Result;
   }
 };
 
@@ -461,11 +477,8 @@ instrprof_error InstrProfRecord::merge(InstrProfRecord &Other,
       Result = instrprof_error::counter_overflow;
   }
 
-  for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) {
-    instrprof_error MergeValueResult = mergeValueProfData(Kind, Other, Weight);
-    if (MergeValueResult != instrprof_error::success)
-      Result = MergeValueResult;
-  }
+  for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind)
+    MergeResult(Result, mergeValueProfData(Kind, Other, Weight));
 
   return Result;
 }
@@ -480,7 +493,7 @@ inline support::endianness getHostEndianness() {
 #include "llvm/ProfileData/InstrProfData.inc"
 
  /*
- * Initialize the record for runtime value profile data. 
+ * Initialize the record for runtime value profile data.
  * Return 0 if the initialization is successful, otherwise
  * return 1.
  */
index 7607e24ec1c84714cc38c94b60af1c384ea52bf0..8df3fe80320930d38464e72cf4ed08e0fb6dc1d6 100644 (file)
@@ -38,13 +38,24 @@ enum class sampleprof_error {
   unrecognized_format,
   unsupported_writing_format,
   truncated_name_table,
-  not_implemented
+  not_implemented,
+  counter_overflow
 };
 
 inline std::error_code make_error_code(sampleprof_error E) {
   return std::error_code(static_cast<int>(E), sampleprof_category());
 }
 
+inline sampleprof_error MergeResult(sampleprof_error &Accumulator,
+                                    sampleprof_error Result) {
+  // Prefer first error encountered as later errors may be secondary effects of
+  // the initial problem.
+  if (Accumulator == sampleprof_error::success &&
+      Result != sampleprof_error::success)
+    Accumulator = Result;
+  return Accumulator;
+}
+
 } // end namespace llvm
 
 namespace std {
@@ -127,15 +138,18 @@ public:
   ///
   /// Sample counts accumulate using saturating arithmetic, to avoid wrapping
   /// around unsigned integers.
-  void addSamples(uint64_t S, uint64_t Weight = 1) {
-    // FIXME: Improve handling of counter overflow.
+  sampleprof_error addSamples(uint64_t S, uint64_t Weight = 1) {
     bool Overflowed;
     if (Weight > 1) {
       S = SaturatingMultiply(S, Weight, &Overflowed);
-      assert(!Overflowed && "Sample counter overflowed!");
+      if (Overflowed)
+        return sampleprof_error::counter_overflow;
     }
     NumSamples = SaturatingAdd(NumSamples, S, &Overflowed);
-    assert(!Overflowed && "Sample counter overflowed!");
+    if (Overflowed)
+      return sampleprof_error::counter_overflow;
+
+    return sampleprof_error::success;
   }
 
   /// Add called function \p F with samples \p S.
@@ -143,16 +157,20 @@ public:
   ///
   /// Sample counts accumulate using saturating arithmetic, to avoid wrapping
   /// around unsigned integers.
-  void addCalledTarget(StringRef F, uint64_t S, uint64_t Weight = 1) {
-    // FIXME: Improve handling of counter overflow.
+  sampleprof_error addCalledTarget(StringRef F, uint64_t S,
+                                   uint64_t Weight = 1) {
     uint64_t &TargetSamples = CallTargets[F];
     bool Overflowed;
     if (Weight > 1) {
       S = SaturatingMultiply(S, Weight, &Overflowed);
-      assert(!Overflowed && "Called target counter overflowed!");
+      if (Overflowed)
+        return sampleprof_error::counter_overflow;
     }
     TargetSamples = SaturatingAdd(TargetSamples, S, &Overflowed);
-    assert(!Overflowed && "Called target counter overflowed!");
+    if (Overflowed)
+      return sampleprof_error::counter_overflow;
+
+    return sampleprof_error::success;
   }
 
   /// Return true if this sample record contains function calls.
@@ -163,10 +181,12 @@ public:
 
   /// Merge the samples in \p Other into this record.
   /// Optionally scale sample counts by \p Weight.
-  void merge(const SampleRecord &Other, uint64_t Weight = 1) {
-    addSamples(Other.getSamples(), Weight);
-    for (const auto &I : Other.getCallTargets())
-      addCalledTarget(I.first(), I.second, Weight);
+  sampleprof_error merge(const SampleRecord &Other, uint64_t Weight = 1) {
+    sampleprof_error Result = addSamples(Other.getSamples(), Weight);
+    for (const auto &I : Other.getCallTargets()) {
+      MergeResult(Result, addCalledTarget(I.first(), I.second, Weight));
+    }
+    return Result;
   }
 
   void print(raw_ostream &OS, unsigned Indent) const;
@@ -193,35 +213,42 @@ public:
   FunctionSamples() : TotalSamples(0), TotalHeadSamples(0) {}
   void print(raw_ostream &OS = dbgs(), unsigned Indent = 0) const;
   void dump() const;
-  void addTotalSamples(uint64_t Num, uint64_t Weight = 1) {
-    // FIXME: Improve handling of counter overflow.
+  sampleprof_error addTotalSamples(uint64_t Num, uint64_t Weight = 1) {
     bool Overflowed;
     if (Weight > 1) {
       Num = SaturatingMultiply(Num, Weight, &Overflowed);
-      assert(!Overflowed && "Total samples counter overflowed!");
+      if (Overflowed)
+        return sampleprof_error::counter_overflow;
     }
     TotalSamples = SaturatingAdd(TotalSamples, Num, &Overflowed);
-    assert(!Overflowed && "Total samples counter overflowed!");
+    if (Overflowed)
+      return sampleprof_error::counter_overflow;
+
+    return sampleprof_error::success;
   }
-  void addHeadSamples(uint64_t Num, uint64_t Weight = 1) {
-    // FIXME: Improve handling of counter overflow.
+  sampleprof_error addHeadSamples(uint64_t Num, uint64_t Weight = 1) {
     bool Overflowed;
     if (Weight > 1) {
       Num = SaturatingMultiply(Num, Weight, &Overflowed);
-      assert(!Overflowed && "Total head samples counter overflowed!");
+      if (Overflowed)
+        return sampleprof_error::counter_overflow;
     }
     TotalHeadSamples = SaturatingAdd(TotalHeadSamples, Num, &Overflowed);
-    assert(!Overflowed && "Total head samples counter overflowed!");
+    if (Overflowed)
+      return sampleprof_error::counter_overflow;
+
+    return sampleprof_error::success;
   }
-  void addBodySamples(uint32_t LineOffset, uint32_t Discriminator, uint64_t Num,
-                      uint64_t Weight = 1) {
-    BodySamples[LineLocation(LineOffset, Discriminator)].addSamples(Num,
-                                                                    Weight);
+  sampleprof_error addBodySamples(uint32_t LineOffset, uint32_t Discriminator,
+                                  uint64_t Num, uint64_t Weight = 1) {
+    return BodySamples[LineLocation(LineOffset, Discriminator)].addSamples(
+        Num, Weight);
   }
-  void addCalledTargetSamples(uint32_t LineOffset, uint32_t Discriminator,
-                              std::string FName, uint64_t Num,
-                              uint64_t Weight = 1) {
-    BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget(
+  sampleprof_error addCalledTargetSamples(uint32_t LineOffset,
+                                          uint32_t Discriminator,
+                                          std::string FName, uint64_t Num,
+                                          uint64_t Weight = 1) {
+    return BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget(
         FName, Num, Weight);
   }
 
@@ -272,19 +299,21 @@ public:
 
   /// Merge the samples in \p Other into this one.
   /// Optionally scale samples by \p Weight.
-  void merge(const FunctionSamples &Other, uint64_t Weight = 1) {
-    addTotalSamples(Other.getTotalSamples(), Weight);
-    addHeadSamples(Other.getHeadSamples(), Weight);
+  sampleprof_error merge(const FunctionSamples &Other, uint64_t Weight = 1) {
+    sampleprof_error Result = sampleprof_error::success;
+    MergeResult(Result, addTotalSamples(Other.getTotalSamples(), Weight));
+    MergeResult(Result, addHeadSamples(Other.getHeadSamples(), Weight));
     for (const auto &I : Other.getBodySamples()) {
       const LineLocation &Loc = I.first;
       const SampleRecord &Rec = I.second;
-      BodySamples[Loc].merge(Rec, Weight);
+      MergeResult(Result, BodySamples[Loc].merge(Rec, Weight));
     }
     for (const auto &I : Other.getCallsiteSamples()) {
       const CallsiteLocation &Loc = I.first;
       const FunctionSamples &Rec = I.second;
-      functionSamplesAt(Loc).merge(Rec, Weight);
+      MergeResult(Result, functionSamplesAt(Loc).merge(Rec, Weight));
     }
+    return Result;
   }
 
 private:
index 1b76367f4de25a2da2de6fd0f47a3d102b9e2d9e..9ded757f2b28277cf97e13983a6963d2fb02edc1 100644 (file)
@@ -45,6 +45,8 @@ class SampleProfErrorCategoryType : public std::error_category {
       return "Truncated function name table";
     case sampleprof_error::not_implemented:
       return "Unimplemented feature";
+    case sampleprof_error::counter_overflow:
+      return "Counter overflow";
     }
     llvm_unreachable("A value of sampleprof_error has no message.");
   }
index cdd98e8e8d03108165be696dad379ca2532cec29..93cd87bb82f8b554842d435c11c69fcd640f80b2 100644 (file)
@@ -151,6 +151,7 @@ static bool ParseLine(const StringRef &Input, bool &IsCallsite, uint32_t &Depth,
 /// \returns true if the file was loaded successfully, false otherwise.
 std::error_code SampleProfileReaderText::read() {
   line_iterator LineIt(*Buffer, /*SkipBlanks=*/true, '#');
+  sampleprof_error Result = sampleprof_error::success;
 
   InlineCallStack InlineStack;
 
@@ -179,8 +180,8 @@ std::error_code SampleProfileReaderText::read() {
       }
       Profiles[FName] = FunctionSamples();
       FunctionSamples &FProfile = Profiles[FName];
-      FProfile.addTotalSamples(NumSamples);
-      FProfile.addHeadSamples(NumHeadSamples);
+      MergeResult(Result, FProfile.addTotalSamples(NumSamples));
+      MergeResult(Result, FProfile.addHeadSamples(NumHeadSamples));
       InlineStack.clear();
       InlineStack.push_back(&FProfile);
     } else {
@@ -202,7 +203,7 @@ std::error_code SampleProfileReaderText::read() {
         }
         FunctionSamples &FSamples = InlineStack.back()->functionSamplesAt(
             CallsiteLocation(LineOffset, Discriminator, FName));
-        FSamples.addTotalSamples(NumSamples);
+        MergeResult(Result, FSamples.addTotalSamples(NumSamples));
         InlineStack.push_back(&FSamples);
       } else {
         while (InlineStack.size() > Depth) {
@@ -210,15 +211,17 @@ std::error_code SampleProfileReaderText::read() {
         }
         FunctionSamples &FProfile = *InlineStack.back();
         for (const auto &name_count : TargetCountMap) {
-          FProfile.addCalledTargetSamples(LineOffset, Discriminator,
-                                          name_count.first, name_count.second);
+          MergeResult(Result, FProfile.addCalledTargetSamples(
+                                  LineOffset, Discriminator, name_count.first,
+                                  name_count.second));
         }
-        FProfile.addBodySamples(LineOffset, Discriminator, NumSamples);
+        MergeResult(Result, FProfile.addBodySamples(LineOffset, Discriminator,
+                                                    NumSamples));
       }
     }
   }
 
-  return sampleprof_error::success;
+  return Result;
 }
 
 bool SampleProfileReaderText::hasFormat(const MemoryBuffer &Buffer) {
diff --git a/test/tools/llvm-profdata/Inputs/overflow-instr.proftext b/test/tools/llvm-profdata/Inputs/overflow-instr.proftext
new file mode 100644 (file)
index 0000000..48d1db8
--- /dev/null
@@ -0,0 +1,6 @@
+overflow
+1
+3
+18446744073709551615
+9223372036854775808
+18446744073709551615
diff --git a/test/tools/llvm-profdata/Inputs/overflow-sample.proftext b/test/tools/llvm-profdata/Inputs/overflow-sample.proftext
new file mode 100644 (file)
index 0000000..a5486bb
--- /dev/null
@@ -0,0 +1,7 @@
+_Z3bari:18446744073709551615:1000
+ 1: 18446744073709551615
+_Z3fooi:18446744073709551615:1000
+ 1: 18446744073709551615
+main:1000:0
+ 1: 500 _Z3bari:18446744073709551615
+ 2: 500 _Z3fooi:18446744073709551615
diff --git a/test/tools/llvm-profdata/overflow-instr.test b/test/tools/llvm-profdata/overflow-instr.test
new file mode 100644 (file)
index 0000000..5b9a94a
--- /dev/null
@@ -0,0 +1,17 @@
+Tests for overflow when merging instrumented profiles.
+
+1- Merge profile having maximum counts with itself and verify overflow detected and saturation occurred
+RUN: llvm-profdata merge -instr %p/Inputs/overflow-instr.proftext %p/Inputs/overflow-instr.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=MERGE_OVERFLOW
+RUN: llvm-profdata show -instr %t.out | FileCheck %s --check-prefix=SHOW_OVERFLOW
+MERGE_OVERFLOW: {{.*}}: overflow: Counter overflow
+SHOW_OVERFLOW: Total functions: 1
+SHOW_OVERFLOW-NEXT: Maximum function count: 18446744073709551615
+SHOW_OVERFLOW-NEXT: Maximum internal block count: 18446744073709551615
+
+2- Merge profile having maximum counts by itself and verify no overflow
+RUN: llvm-profdata merge -instr %p/Inputs/overflow-instr.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=MERGE_NO_OVERFLOW -allow-empty
+RUN: llvm-profdata show -instr %t.out | FileCheck %s --check-prefix=SHOW_NO_OVERFLOW
+MERGE_NO_OVERFLOW-NOT: {{.*}}: overflow: Counter overflow
+SHOW_NO_OVERFLOW: Total functions: 1
+SHOW_NO_OVERFLOW-NEXT: Maximum function count: 18446744073709551615
+SHOW_NO_OVERFLOW-NEXT: Maximum internal block count: 18446744073709551615
diff --git a/test/tools/llvm-profdata/overflow-sample.test b/test/tools/llvm-profdata/overflow-sample.test
new file mode 100644 (file)
index 0000000..cd6268d
--- /dev/null
@@ -0,0 +1,43 @@
+Tests for overflow when merging sampled profiles.
+
+1- Merge profile having maximum counts with itself and verify overflow detected
+RUN: llvm-profdata merge -sample %p/Inputs/overflow-sample.proftext %p/Inputs/overflow-sample.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=MERGE_OVERFLOW
+RUN: llvm-profdata show -sample %t.out | FileCheck %s --check-prefix=SHOW_OVERFLOW
+MERGE_OVERFLOW: {{.*}}: main: Counter overflow
+SHOW_OVERFLOW: Function: main: 2000, 0, 2 sampled lines
+SHOW_OVERFLOW-NEXT: Samples collected in the function's body {
+SHOW_OVERFLOW-NEXT:   1: 1000, calls: _Z3bari:18446744073709551615
+SHOW_OVERFLOW-NEXT:   2: 1000, calls: _Z3fooi:18446744073709551615
+SHOW_OVERFLOW-NEXT: }
+SHOW_OVERFLOW-NEXT: No inlined callsites in this function
+SHOW_OVERFLOW-NEXT: Function: _Z3fooi: 18446744073709551615, 2000, 1 sampled lines
+SHOW_OVERFLOW-NEXT: Samples collected in the function's body {
+SHOW_OVERFLOW-NEXT:   1: 18446744073709551615
+SHOW_OVERFLOW-NEXT: }
+SHOW_OVERFLOW-NEXT: No inlined callsites in this function
+SHOW_OVERFLOW-NEXT: Function: _Z3bari: 18446744073709551615, 2000, 1 sampled lines
+SHOW_OVERFLOW-NEXT: Samples collected in the function's body {
+SHOW_OVERFLOW-NEXT:   1: 18446744073709551615
+SHOW_OVERFLOW-NEXT: }
+SHOW_OVERFLOW-NEXT: No inlined callsites in this function
+
+2- Merge profile having maximum counts by itself and verify no overflow
+RUN: llvm-profdata merge -sample %p/Inputs/overflow-sample.proftext -o %t.out 2>&1 | FileCheck %s -allow-empty -check-prefix=MERGE_NO_OVERFLOW
+RUN: llvm-profdata show -sample %t.out | FileCheck %s --check-prefix=SHOW_NO_OVERFLOW
+MERGE_NO_OVERFLOW-NOT: {{.*}}: main: Counter overflow
+SHOW_NO_OVERFLOW: Function: main: 1000, 0, 2 sampled lines
+SHOW_NO_OVERFLOW-NEXT: Samples collected in the function's body {
+SHOW_NO_OVERFLOW-NEXT:   1: 500, calls: _Z3bari:18446744073709551615
+SHOW_NO_OVERFLOW-NEXT:   2: 500, calls: _Z3fooi:18446744073709551615
+SHOW_NO_OVERFLOW-NEXT: }
+SHOW_NO_OVERFLOW-NEXT: No inlined callsites in this function
+SHOW_NO_OVERFLOW-NEXT: Function: _Z3fooi: 18446744073709551615, 1000, 1 sampled lines
+SHOW_NO_OVERFLOW-NEXT: Samples collected in the function's body {
+SHOW_NO_OVERFLOW-NEXT:   1: 18446744073709551615
+SHOW_NO_OVERFLOW-NEXT: }
+SHOW_NO_OVERFLOW-NEXT: No inlined callsites in this function
+SHOW_NO_OVERFLOW-NEXT: Function: _Z3bari: 18446744073709551615, 1000, 1 sampled lines
+SHOW_NO_OVERFLOW-NEXT: Samples collected in the function's body {
+SHOW_NO_OVERFLOW-NEXT:   1: 18446744073709551615
+SHOW_NO_OVERFLOW-NEXT: }
+SHOW_NO_OVERFLOW-NEXT: No inlined callsites in this function
diff --git a/test/tools/llvm-profdata/overflow.proftext b/test/tools/llvm-profdata/overflow.proftext
deleted file mode 100644 (file)
index b8401ff..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# RUN: llvm-profdata merge %s -o %t.out 2>&1 | FileCheck %s --check-prefix=MERGE
-# RUN: llvm-profdata show %t.out | FileCheck %s --check-prefix=SHOW
-# MERGE: overflow.proftext: overflow: Counter overflow
-# SHOW: Total functions: 1
-# SHOW: Maximum function count: 18446744073709551615
-# SHOW: Maximum internal block count: 18446744073709551615
-
-overflow
-1
-3
-18446744073709551615
-9223372036854775808
-18446744073709551615
-
-overflow
-1
-3
-9223372036854775808
-9223372036854775808
-0
index 4fa36c4b0b6a400bcd32c3bad1e2b9b824deb405..71768f92382a584c4049296e552ab9f65cbccb5a 100644 (file)
@@ -180,7 +180,11 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
          I != E; ++I) {
       StringRef FName = I->first();
       FunctionSamples &Samples = I->second;
-      ProfileMap[FName].merge(Samples, Input.Weight);
+      sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight);
+      if (Result != sampleprof_error::success) {
+        std::error_code EC = make_error_code(Result);
+        handleMergeWriterError(EC, Input.Filename, FName);
+      }
     }
   }
   Writer->write(ProfileMap);
index 0f68307b56f713d6b5d0de59a154ca2ef496cbe2..ee357053a17a9d0a8168e90f4d9611dde1c1c542 100644 (file)
@@ -353,41 +353,50 @@ TEST_F(InstrProfTest, get_icall_data_merge1) {
 TEST_F(InstrProfTest, get_icall_data_merge1_saturation) {
   const uint64_t Max = std::numeric_limits<uint64_t>::max();
 
-  InstrProfRecord Record1("caller", 0x1234, {1});
-  InstrProfRecord Record2("caller", 0x1234, {Max});
-  InstrProfRecord Record3("callee1", 0x1235, {3, 4});
-
-  Record1.reserveSites(IPVK_IndirectCallTarget, 1);
-  InstrProfValueData VD1[] = {{(uint64_t) "callee1", 1}};
-  Record1.addValueData(IPVK_IndirectCallTarget, 0, VD1, 1, nullptr);
-
-  Record2.reserveSites(IPVK_IndirectCallTarget, 1);
-  // FIXME: Improve handling of counter overflow. ValueData asserts on overflow.
-  //  InstrProfValueData VD2[] = {{(uint64_t) "callee1", Max}};
-  InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}};
-  Record2.addValueData(IPVK_IndirectCallTarget, 0, VD2, 1, nullptr);
-
-  Writer.addRecord(std::move(Record1));
-  Writer.addRecord(std::move(Record2));
-  Writer.addRecord(std::move(Record3));
+  InstrProfRecord Record1("foo", 0x1234, {1});
+  auto Result1 = Writer.addRecord(std::move(Record1));
+  ASSERT_EQ(Result1, instrprof_error::success);
+
+  // Verify counter overflow.
+  InstrProfRecord Record2("foo", 0x1234, {Max});
+  auto Result2 = Writer.addRecord(std::move(Record2));
+  ASSERT_EQ(Result2, instrprof_error::counter_overflow);
+
+  InstrProfRecord Record3("bar", 0x9012, {8});
+  auto Result3 = Writer.addRecord(std::move(Record3));
+  ASSERT_EQ(Result3, instrprof_error::success);
+
+  InstrProfRecord Record4("baz", 0x5678, {3, 4});
+  Record4.reserveSites(IPVK_IndirectCallTarget, 1);
+  InstrProfValueData VD4[] = {{(uint64_t) "bar", 1}};
+  Record4.addValueData(IPVK_IndirectCallTarget, 0, VD4, 1, nullptr);
+  auto Result4 = Writer.addRecord(std::move(Record4));
+  ASSERT_EQ(Result4, instrprof_error::success);
+
+  // Verify value data counter overflow.
+  InstrProfRecord Record5("baz", 0x5678, {5, 6});
+  Record5.reserveSites(IPVK_IndirectCallTarget, 1);
+  InstrProfValueData VD5[] = {{(uint64_t) "bar", Max}};
+  Record5.addValueData(IPVK_IndirectCallTarget, 0, VD5, 1, nullptr);
+  auto Result5 = Writer.addRecord(std::move(Record5));
+  ASSERT_EQ(Result5, instrprof_error::counter_overflow);
 
   auto Profile = Writer.writeBuffer();
   readProfile(std::move(Profile));
 
   // Verify saturation of counts.
-  ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
-  ASSERT_TRUE(NoError(R.getError()));
-
-  ASSERT_EQ(Max, R.get().Counts[0]);
-
-  ASSERT_EQ(1U, R.get().getNumValueSites(IPVK_IndirectCallTarget));
+  ErrorOr<InstrProfRecord> ReadRecord1 =
+      Reader->getInstrProfRecord("foo", 0x1234);
+  ASSERT_TRUE(NoError(ReadRecord1.getError()));
+  ASSERT_EQ(Max, ReadRecord1.get().Counts[0]);
+
+  ErrorOr<InstrProfRecord> ReadRecord2 =
+      Reader->getInstrProfRecord("baz", 0x5678);
+  ASSERT_EQ(1U, ReadRecord2.get().getNumValueSites(IPVK_IndirectCallTarget));
   std::unique_ptr<InstrProfValueData[]> VD =
-          R.get().getValueForSite(IPVK_IndirectCallTarget, 0);
-  ASSERT_EQ(StringRef("callee1"), StringRef((const char *)VD[0].Value, 7));
-
-  // FIXME: Improve handling of counter overflow. ValueData asserts on overflow.
-  //  ASSERT_EQ(Max, VD[0].Count);
-  ASSERT_EQ(2U, VD[0].Count);
+      ReadRecord2.get().getValueForSite(IPVK_IndirectCallTarget, 0);
+  ASSERT_EQ(StringRef("bar"), StringRef((const char *)VD[0].Value, 3));
+  ASSERT_EQ(Max, VD[0].Count);
 }
 
 // Synthesize runtime value profile data.
index aa1144d7913d5787b9d5d0c49d3e12a1ed4136ad..cc3c2f5306e48424a29f44b3dfc71789574ec027 100644 (file)
@@ -99,4 +99,34 @@ TEST_F(SampleProfTest, roundtrip_binary_profile) {
   testRoundTrip(SampleProfileFormat::SPF_Binary);
 }
 
+TEST_F(SampleProfTest, sample_overflow_saturation) {
+  const uint64_t Max = std::numeric_limits<uint64_t>::max();
+  sampleprof_error Result;
+
+  StringRef FooName("_Z3fooi");
+  FunctionSamples FooSamples;
+  Result = FooSamples.addTotalSamples(1);
+  ASSERT_EQ(Result, sampleprof_error::success);
+
+  Result = FooSamples.addHeadSamples(1);
+  ASSERT_EQ(Result, sampleprof_error::success);
+
+  Result = FooSamples.addBodySamples(10, 0, 1);
+  ASSERT_EQ(Result, sampleprof_error::success);
+
+  Result = FooSamples.addTotalSamples(Max);
+  ASSERT_EQ(Result, sampleprof_error::counter_overflow);
+  ASSERT_EQ(FooSamples.getTotalSamples(), Max);
+
+  Result = FooSamples.addHeadSamples(Max);
+  ASSERT_EQ(Result, sampleprof_error::counter_overflow);
+  ASSERT_EQ(FooSamples.getHeadSamples(), Max);
+
+  Result = FooSamples.addBodySamples(10, 0, Max);
+  ASSERT_EQ(Result, sampleprof_error::counter_overflow);
+  ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);
+  ASSERT_FALSE(BodySamples.getError());
+  ASSERT_EQ(BodySamples.get(), Max);
+}
+
 } // end anonymous namespace