Index: llvm/trunk/include/llvm/ProfileData/InstrProf.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/InstrProf.h +++ llvm/trunk/include/llvm/ProfileData/InstrProf.h @@ -184,6 +184,16 @@ return std::error_code(static_cast(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 @@ /// 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 @@ getValueSitesForKind(ValueKind); std::vector &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 @@ 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 @@ #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: llvm/trunk/include/llvm/ProfileData/SampleProf.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/SampleProf.h +++ llvm/trunk/include/llvm/ProfileData/SampleProf.h @@ -38,13 +38,24 @@ 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(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 @@ /// /// 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 @@ /// /// 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 @@ /// 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 @@ 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); - } - 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 addBodySamples(uint32_t LineOffset, uint32_t Discriminator, + uint64_t Num, uint64_t Weight = 1) { + return BodySamples[LineLocation(LineOffset, Discriminator)].addSamples( + Num, Weight); + } + 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 @@ /// 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: llvm/trunk/lib/ProfileData/SampleProf.cpp =================================================================== --- llvm/trunk/lib/ProfileData/SampleProf.cpp +++ llvm/trunk/lib/ProfileData/SampleProf.cpp @@ -45,6 +45,8 @@ 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: llvm/trunk/lib/ProfileData/SampleProfReader.cpp =================================================================== --- llvm/trunk/lib/ProfileData/SampleProfReader.cpp +++ llvm/trunk/lib/ProfileData/SampleProfReader.cpp @@ -151,6 +151,7 @@ /// \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 @@ } 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 @@ } 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 @@ } 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) { Index: llvm/trunk/test/tools/llvm-profdata/Inputs/overflow-instr.proftext =================================================================== --- llvm/trunk/test/tools/llvm-profdata/Inputs/overflow-instr.proftext +++ llvm/trunk/test/tools/llvm-profdata/Inputs/overflow-instr.proftext @@ -0,0 +1,6 @@ +overflow +1 +3 +18446744073709551615 +9223372036854775808 +18446744073709551615 Index: llvm/trunk/test/tools/llvm-profdata/Inputs/overflow-sample.proftext =================================================================== --- llvm/trunk/test/tools/llvm-profdata/Inputs/overflow-sample.proftext +++ llvm/trunk/test/tools/llvm-profdata/Inputs/overflow-sample.proftext @@ -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 Index: llvm/trunk/test/tools/llvm-profdata/overflow-instr.test =================================================================== --- llvm/trunk/test/tools/llvm-profdata/overflow-instr.test +++ llvm/trunk/test/tools/llvm-profdata/overflow-instr.test @@ -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 Index: llvm/trunk/test/tools/llvm-profdata/overflow-sample.test =================================================================== --- llvm/trunk/test/tools/llvm-profdata/overflow-sample.test +++ llvm/trunk/test/tools/llvm-profdata/overflow-sample.test @@ -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 Index: llvm/trunk/test/tools/llvm-profdata/overflow.proftext =================================================================== --- llvm/trunk/test/tools/llvm-profdata/overflow.proftext +++ llvm/trunk/test/tools/llvm-profdata/overflow.proftext @@ -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: llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp +++ llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp @@ -180,7 +180,11 @@ 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: llvm/trunk/unittests/ProfileData/InstrProfTest.cpp =================================================================== --- llvm/trunk/unittests/ProfileData/InstrProfTest.cpp +++ llvm/trunk/unittests/ProfileData/InstrProfTest.cpp @@ -353,41 +353,50 @@ TEST_F(InstrProfTest, get_icall_data_merge1_saturation) { const uint64_t Max = std::numeric_limits::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 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 ReadRecord1 = + Reader->getInstrProfRecord("foo", 0x1234); + ASSERT_TRUE(NoError(ReadRecord1.getError())); + ASSERT_EQ(Max, ReadRecord1.get().Counts[0]); + + ErrorOr ReadRecord2 = + Reader->getInstrProfRecord("baz", 0x5678); + ASSERT_EQ(1U, ReadRecord2.get().getNumValueSites(IPVK_IndirectCallTarget)); std::unique_ptr 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: llvm/trunk/unittests/ProfileData/SampleProfTest.cpp =================================================================== --- llvm/trunk/unittests/ProfileData/SampleProfTest.cpp +++ llvm/trunk/unittests/ProfileData/SampleProfTest.cpp @@ -99,4 +99,34 @@ testRoundTrip(SampleProfileFormat::SPF_Binary); } +TEST_F(SampleProfTest, sample_overflow_saturation) { + const uint64_t Max = std::numeric_limits::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 BodySamples = FooSamples.findSamplesAt(10, 0); + ASSERT_FALSE(BodySamples.getError()); + ASSERT_EQ(BodySamples.get(), Max); +} + } // end anonymous namespace