Index: llvm/docs/CommandGuide/llvm-profdata.rst =================================================================== --- llvm/docs/CommandGuide/llvm-profdata.rst +++ llvm/docs/CommandGuide/llvm-profdata.rst @@ -17,6 +17,7 @@ * :ref:`merge ` * :ref:`show ` +* :ref:`overlap ` .. program:: llvm-profdata merge @@ -231,6 +232,71 @@ Only show context sensitive profile counts. The default is to filter all context sensitive profile counts. +.. program:: llvm-profdata overlap + +.. _profdata-overlap: + +OVERLAP +---- + +SYNOPSIS +^^^^^^^^ + +:program:`llvm-profdata overlap` [*options*] [*base profile file*] [*test profile file*] + +DESCRIPTION +^^^^^^^^^^^ + +:program:`llvm-profdata show` takes two profile data files and displays the +*overlap* information between the whole files and between any of the specified functions. + +In this command, *overlap* is defined the follows: +Suppose *base profile file* has the following counts: +{c1_1, c1_2, ..., c1_n, c1_u_1, c2_u_2, ..., c2_u_s}, +and *test profile file* has +{c2_1, c2_2, ..., c2_n, c2_v_1, c2_v_2, ..., c2_v_t}. +Here c{1|2}_i (i = 1 .. n) are matched counters and c1_u_i (i = 1 .. s)and +c2_v_i (i = 1 .. v) are unique to *base profile file* and *test profile file*, +respectively. +Let sum_1 = c1_1 + c1_2 + ... + c1_n + c1_u_1 + c2_u_2 + ... + c2_u_s, and +sum_2 = c2_1 + c2_2 + ... + c2_n + c2_v_1 + c2_v_2 + ... + c2_v_t. +*overlap* = min(c1_1/sum_1, c2_1/sum_2) + min(c1_2/sum_1, c2_2/sum_2) + ... + min(c1_n/sum_1, c2_n/sum_2). + +The result overlap is a percentage number, ranging from 0.0% to 100.0%, where +0.0% means there is not overlap and 100.0% means a perfect overlap. + +Here is an example, if *base profile file* has counts of +{400, 600}, and *test profile file* has matched counts of {60000, 40000}. +The *overlap* is 80%. + + +OPTIONS +^^^^^^^ + +.. option:: -function=string + + Print details for a function if the function's name contains the given string. + +.. option:: -help + + Print a summary of command line options. + +.. option:: -output=output, -o=output + + Specify the output file name. If *output* is ``-`` or it isn't specified, + then the output is sent to standard output. + +.. option:: -value-cutoff=n + + Show only those functions whose max count values are greater or equal to ``n``. + By default, the value-cutoff is set to max of unsigned long long. + +.. option:: -cs + + Only show overlap for the context sensitive profile counts. The default is to show + non-context sensitive profile counts. + EXIT STATUS ----------- Index: llvm/include/llvm/ProfileData/InstrProf.h =================================================================== --- llvm/include/llvm/ProfileData/InstrProf.h +++ llvm/include/llvm/ProfileData/InstrProf.h @@ -590,6 +590,54 @@ return PGOName.drop_front(S + 1); } +// To store the sums of profile count values, or the percentage of +// the sums of the total count values. +struct CountSumOrPercent { + uint64_t NumFuncs; + double EdgeCount; + double ValueCounts[IPVK_Last - IPVK_First + 1]; + CountSumOrPercent() : NumFuncs(0), EdgeCount(0.0f), ValueCounts() {} + void reset() { + NumFuncs = 0; + EdgeCount = 0.0f; + for (unsigned I = 0; I < IPVK_Last - IPVK_First + 1; I++) + ValueCounts[I] = 0.0f; + } +}; + +// Function level or program level overlap information. +struct OverlapStats { + // Sum of the total count values for the base profile. + CountSumOrPercent BaseSum; + // Sum of the total count values for the test profile. + CountSumOrPercent TestSum; + // Overlap lap score. Should be in range of [0.0f to 1.0f]. + CountSumOrPercent Overlap; + CountSumOrPercent Mismatch; + CountSumOrPercent Unique; + bool Valid; + void dump(raw_fd_ostream &OS) const; + + bool getCountSums(const std::string &BaseFilename, + const std::string &TestFilename, bool IsCS); + void addOneMismatch(const CountSumOrPercent &MismatchFunc); + void addOneUnique(const CountSumOrPercent &UniqueFunc); + + static inline double score(uint64_t Val1, uint64_t Val2, double Sum1, + double Sum2) { + if (Sum1 < 1.0f || Sum2 < 1.0f) + return 0.0f; + return std::min(Val1 / Sum1, Val2 / Sum2); + } +}; + +// This is used to filter the functions whose overlap information +// to be output. +struct OverlapFuncFilters { + uint64_t ValueCutoff; + const std::string NameFilter; +}; + struct InstrProfValueSiteRecord { /// Value profiling data pairs at a given value site. std::list ValueData; @@ -615,6 +663,10 @@ function_ref Warn); /// Scale up value profile data counts. void scale(uint64_t Weight, function_ref Warn); + + /// Compute the overlap b/w this record and Input record. + void overlap(InstrProfValueSiteRecord &Input, uint32_t ValueKind, + OverlapStats &Overlap, OverlapStats &FuncLevelOverlap); }; /// Profiling information for a single function. @@ -703,6 +755,18 @@ /// Clear value data entries void clearValueData() { ValueData = nullptr; } + /// Computer the sums of all counts and store in Sum. + void getCountSums(CountSumOrPercent &Sum) const; + + /// Compuer the overlap b/w this IntrprofRecord and Other. + void overlap(InstrProfRecord &Other, OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap, uint64_t ValueCutoff); + + /// Compuer the overlap of value profile counts. + void overlapValueProfData(uint32_t ValueKind, InstrProfRecord &Src, + OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap); + private: struct ValueProfData { std::vector IndirectCallSites; @@ -1060,5 +1124,4 @@ void createProfileFileNameVar(Module &M, StringRef InstrProfileOutput); } // end namespace llvm - #endif // LLVM_PROFILEDATA_INSTRPROF_H Index: llvm/include/llvm/ProfileData/InstrProfReader.h =================================================================== --- llvm/include/llvm/ProfileData/InstrProfReader.h +++ llvm/include/llvm/ProfileData/InstrProfReader.h @@ -91,6 +91,9 @@ /// compiler. virtual InstrProfSymtab &getSymtab() = 0; + /// Compute the sum of counts and return in Sum. + void getCountSums(CountSumOrPercent &Sum, bool IsCS); + protected: std::unique_ptr Symtab; Index: llvm/include/llvm/ProfileData/InstrProfWriter.h =================================================================== --- llvm/include/llvm/ProfileData/InstrProfWriter.h +++ llvm/include/llvm/ProfileData/InstrProfWriter.h @@ -100,6 +100,11 @@ // Internal interface for testing purpose only. void setValueProfDataEndianness(support::endianness Endianness); void setOutputSparse(bool Sparse); + // Compute the overlap b/w this object and Other. Program level result is + // stored in Overlap and function level result is stored in FuncLevelOverlap. + void overlapRecord(NamedInstrProfRecord &&Other, OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap, + const OverlapFuncFilters &FuncFilter); private: void addRecord(StringRef Name, uint64_t Hash, InstrProfRecord &&I, Index: llvm/lib/ProfileData/InstrProf.cpp =================================================================== --- llvm/lib/ProfileData/InstrProf.cpp +++ llvm/lib/ProfileData/InstrProf.cpp @@ -29,6 +29,7 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" +#include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" @@ -478,6 +479,126 @@ return Error::success(); } +void InstrProfRecord::getCountSums(CountSumOrPercent &Sum) const { + uint64_t FuncSum = 0; + Sum.NumFuncs += 1; + for (size_t F = 0, E = Counts.size(); F < E; ++F) + FuncSum += Counts[F]; + Sum.EdgeCount += FuncSum; + + for (uint32_t VK = IPVK_First; VK <= IPVK_Last; ++VK) { + uint64_t KindSum = 0; + uint32_t NumValueSites = getNumValueSites(VK); + for (size_t I = 0; I < NumValueSites; ++I) { + uint32_t NV = getNumValueDataForSite(VK, I); + std::unique_ptr VD = getValueForSite(VK, I); + for (uint32_t V = 0; V < NV; V++) + KindSum += VD[V].Count; + } + Sum.ValueCounts[VK] += KindSum; + } +} + +void InstrProfValueSiteRecord::overlap(InstrProfValueSiteRecord &Input, + uint32_t ValueKind, + OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap) { + this->sortByTargetValues(); + Input.sortByTargetValues(); + auto I = ValueData.begin(); + auto IE = ValueData.end(); + double Score = 0.0f, FuncLevelScore = 0.0f; + 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) { + Score += OverlapStats::score(I->Count, J->Count, + Overlap.BaseSum.ValueCounts[ValueKind], + Overlap.TestSum.ValueCounts[ValueKind]); + FuncLevelScore += OverlapStats::score( + I->Count, J->Count, FuncLevelOverlap.BaseSum.ValueCounts[ValueKind], + FuncLevelOverlap.TestSum.ValueCounts[ValueKind]); + ++I; + continue; + } + } + Overlap.Overlap.ValueCounts[ValueKind] += Score; + FuncLevelOverlap.Overlap.ValueCounts[ValueKind] += FuncLevelScore; +} + +// Return false on mismatch. +void InstrProfRecord::overlapValueProfData(uint32_t ValueKind, + InstrProfRecord &Other, + OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap) { + uint32_t ThisNumValueSites = getNumValueSites(ValueKind); + uint32_t OtherNumValueSites = Other.getNumValueSites(ValueKind); + assert(ThisNumValueSites == OtherNumValueSites); + if (!ThisNumValueSites) + return; + + std::vector &ThisSiteRecords = + getOrCreateValueSitesForKind(ValueKind); + MutableArrayRef OtherSiteRecords = + Other.getValueSitesForKind(ValueKind); + for (uint32_t I = 0; I < ThisNumValueSites; I++) + ThisSiteRecords[I].overlap(OtherSiteRecords[I], ValueKind, Overlap, + FuncLevelOverlap); +} + +void InstrProfRecord::overlap(InstrProfRecord &Other, OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap, + uint64_t ValueCutoff) { + // FuncLevel CountSum for other should already computed and nonzero. + assert(FuncLevelOverlap.TestSum.EdgeCount >= 1.0f); + getCountSums(FuncLevelOverlap.BaseSum); + bool Mismatch = (Counts.size() != Other.Counts.size()); + + // Check if the value profiles mismatch. + if (!Mismatch) { + for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) { + uint32_t ThisNumValueSites = getNumValueSites(Kind); + uint32_t OtherNumValueSites = Other.getNumValueSites(Kind); + if (ThisNumValueSites != OtherNumValueSites) { + Mismatch = true; + break; + } + } + } + if (Mismatch) { + Overlap.addOneMismatch(FuncLevelOverlap.TestSum); + return; + } + + // Compute overlap for value counts. + for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) + overlapValueProfData(Kind, Other, Overlap, FuncLevelOverlap); + + double Score = 0.0; + uint64_t MaxCount = 0; + // Compute overlap for edge counts. + for (size_t I = 0, E = Other.Counts.size(); I < E; ++I) { + Score += OverlapStats::score(Counts[I], Other.Counts[I], + Overlap.BaseSum.EdgeCount, + Overlap.TestSum.EdgeCount); + MaxCount = std::max(Other.Counts[I], MaxCount); + } + Overlap.Overlap.EdgeCount += Score; + Overlap.Overlap.NumFuncs += 1; + + if (MaxCount >= ValueCutoff) { + double FuncScore = 0.0; + for (size_t I = 0, E = Other.Counts.size(); I < E; ++I) + FuncScore += OverlapStats::score(Counts[I], Other.Counts[I], + FuncLevelOverlap.BaseSum.EdgeCount, + FuncLevelOverlap.TestSum.EdgeCount); + FuncLevelOverlap.Overlap.EdgeCount += FuncScore; + FuncLevelOverlap.Overlap.NumFuncs += 1; + FuncLevelOverlap.Valid = true; + } +} + void InstrProfValueSiteRecord::merge(InstrProfValueSiteRecord &Input, uint64_t Weight, function_ref Warn) { @@ -1046,4 +1167,83 @@ } } +bool OverlapStats::getCountSums(const std::string &BaseFilename, + const std::string &TestFilename, bool IsCS) { + auto getProfileSum = [IsCS](const std::string &Filename, + CountSumOrPercent &Sum) { + auto ReaderOrErr = InstrProfReader::create(Filename); + if (ReaderOrErr.takeError()) + return false; + auto Reader = std::move(ReaderOrErr.get()); + Reader->getCountSums(Sum, IsCS); + return true; + }; + bool Ret = getProfileSum(BaseFilename, BaseSum) && + getProfileSum(TestFilename, TestSum); + if (!Ret) + return false; + Valid = true; + return true; +} + +void OverlapStats::addOneMismatch(const CountSumOrPercent &MismatchFunc) { + Mismatch.NumFuncs += 1; + Mismatch.EdgeCount += MismatchFunc.EdgeCount / TestSum.EdgeCount; + for (unsigned I = 0; I < IPVK_Last - IPVK_First + 1; I++) { + if (TestSum.ValueCounts[I] >= 1.0f) + Mismatch.ValueCounts[I] += + MismatchFunc.ValueCounts[I] / TestSum.ValueCounts[I]; + } +} + +void OverlapStats::addOneUnique(const CountSumOrPercent &UniqueFunc) { + Unique.NumFuncs += 1; + Unique.EdgeCount += UniqueFunc.EdgeCount / TestSum.EdgeCount; + for (unsigned I = 0; I < IPVK_Last - IPVK_First + 1; I++) { + if (TestSum.ValueCounts[I] >= 1.0f) + Unique.ValueCounts[I] += + UniqueFunc.ValueCounts[I] / TestSum.ValueCounts[I]; + } +} + +void OverlapStats::dump(raw_fd_ostream &OS) const { + OS << "EdgeCount: (# of functions)\n"; + OS << " Overlap:\t" << format("%.3f%%", Overlap.EdgeCount * 100) << " (" + << Overlap.NumFuncs << ")\n"; + if (Mismatch.NumFuncs) + OS << " Mismatch:\t" << format("%.3f%%", Mismatch.EdgeCount * 100) << " (" + << Mismatch.NumFuncs << ")\n"; + if (Unique.NumFuncs) + OS << " Unique:\t" << format("%.3f%%", Unique.EdgeCount * 100) << " (" + << Unique.NumFuncs << ")\n"; + OS << " BaseCountSum:\t" << format("%.0f", BaseSum.EdgeCount) << "\n"; + OS << " TestCountSum:\t" << format("%.0f", TestSum.EdgeCount) << "\n"; + + for (unsigned I = 0; I < IPVK_Last - IPVK_First + 1; I++) { + if (BaseSum.ValueCounts[I] < 1.0f && TestSum.ValueCounts[I] < 1.0f) + continue; + switch (I) { + case IPVK_IndirectCallTarget: + OS << "IndirectCall:\n"; + break; + case IPVK_MemOPSize: + OS << "MemOP:\n"; + break; + default: + OS << "VP[" << I << "]:\n"; + break; + } + OS << " Overlap:\t" << format("%.3f%%", Overlap.ValueCounts[I] * 100) + << "\n"; + if (Mismatch.NumFuncs) + OS << " Mismatch:\t" << format("%.3f%%", Mismatch.ValueCounts[I] * 100) + << "\n"; + if (Unique.NumFuncs) + OS << " Unique:\t" << format("%.3f%%", Unique.ValueCounts[I] * 100) + << "\n"; + OS << " BaseCountSum:\t" << format("%.0f", BaseSum.ValueCounts[I]) << "\n"; + OS << " TestCountSum:\t" << format("%.0f", TestSum.ValueCounts[I]) << "\n"; + } +} + } // end namespace llvm Index: llvm/lib/ProfileData/InstrProfReader.cpp =================================================================== --- llvm/lib/ProfileData/InstrProfReader.cpp +++ llvm/lib/ProfileData/InstrProfReader.cpp @@ -900,3 +900,14 @@ } return success(); } + +void InstrProfReader::getCountSums(CountSumOrPercent &Sum, bool IsCS) { + for (const auto &Func : *this) { + if (isIRLevelProfile()) { + bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); + if (FuncIsCS != IsCS) + continue; + } + Func.getCountSums(Sum); + } +} Index: llvm/lib/ProfileData/InstrProfWriter.cpp =================================================================== --- llvm/lib/ProfileData/InstrProfWriter.cpp +++ llvm/lib/ProfileData/InstrProfWriter.cpp @@ -187,6 +187,40 @@ addRecord(Name, Hash, std::move(I), Weight, Warn); } +void InstrProfWriter::overlapRecord(NamedInstrProfRecord &&Other, + OverlapStats &Overlap, + OverlapStats &FuncLevelOverlap, + const OverlapFuncFilters &FuncFilter) { + auto Name = Other.Name; + auto Hash = Other.Hash; + Other.getCountSums(FuncLevelOverlap.TestSum); + if (FunctionData.find(Name) == FunctionData.end()) { + Overlap.addOneUnique(FuncLevelOverlap.TestSum); + return; + } + if (FuncLevelOverlap.TestSum.EdgeCount < 1.0f) { + Overlap.Overlap.NumFuncs += 1; + return; + } + auto &ProfileDataMap = FunctionData[Name]; + bool NewFunc; + ProfilingData::iterator Where; + std::tie(Where, NewFunc) = + ProfileDataMap.insert(std::make_pair(Hash, InstrProfRecord())); + if (NewFunc) { + Overlap.addOneMismatch(FuncLevelOverlap.TestSum); + return; + } + InstrProfRecord &Dest = Where->second; + + uint64_t ValueCutoff = FuncFilter.ValueCutoff; + if (!FuncFilter.NameFilter.empty() && + Name.find(FuncFilter.NameFilter) != Name.npos) + ValueCutoff = 0; + + Dest.overlap(Other, Overlap, FuncLevelOverlap, ValueCutoff); +} + void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash, InstrProfRecord &&I, uint64_t Weight, function_ref Warn) { Index: llvm/test/tools/llvm-profdata/Inputs/overlap_1.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/Inputs/overlap_1.proftext @@ -0,0 +1,36 @@ +# IR level Instrumentation Flag +:ir +bar +# Func Hash: +12884901887 +# Num Counters: +1 +# Counter Values: +100000 + +bar1 +# Func Hash: +12884901887 +# Num Counters: +1 +# Counter Values: +100000 + +foo +# Func Hash: +25571299074 +# Num Counters: +2 +# Counter Values: +40000 +60000 + +main +# Func Hash: +29212902728 +# Num Counters: +2 +# Counter Values: +200000 +0 + Index: llvm/test/tools/llvm-profdata/Inputs/overlap_1_cs.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/Inputs/overlap_1_cs.proftext @@ -0,0 +1,11 @@ +# CSIR level Instrumentation Flag +:csir +bar +# Func Hash: +1152921534274394772 +# Num Counters: +2 +# Counter Values: +6000 +4000 + Index: llvm/test/tools/llvm-profdata/Inputs/overlap_1_vp.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/Inputs/overlap_1_vp.proftext @@ -0,0 +1,25 @@ +:IR +foo +# Func Hash: +72057649435042473 +# Num Counters: +2 +# Counter Values: +40000 +60000 +# Num Value Kinds: +2 +# ValueKind = IPVK_IndirectCallTarget: +0 +# NumValueSites: +1 +2 +bar1:40000 +bar2:60000 +# ValueKind = IPVK_MemOPSize: +1 +# NumValueSites: +1 +2 +1:40000 +4:60000 Index: llvm/test/tools/llvm-profdata/Inputs/overlap_2.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/Inputs/overlap_2.proftext @@ -0,0 +1,36 @@ +# IR level Instrumentation Flag +:ir +bar +# Func Hash: +12884901887 +# Num Counters: +1 +# Counter Values: +10000 + +bar2 +# Func Hash: +12884901887 +# Num Counters: +1 +# Counter Values: +10000 + +foo +# Func Hash: +25571299075 +# Num Counters: +2 +# Counter Values: +4000 +6000 + +main +# Func Hash: +29212902728 +# Num Counters: +2 +# Counter Values: +20000 +0 + Index: llvm/test/tools/llvm-profdata/Inputs/overlap_2_cs.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/Inputs/overlap_2_cs.proftext @@ -0,0 +1,11 @@ +# CSIR level Instrumentation Flag +:csir +bar +# Func Hash: +1152921534274394772 +# Num Counters: +2 +# Counter Values: +4000 +6000 + Index: llvm/test/tools/llvm-profdata/Inputs/overlap_2_vp.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/Inputs/overlap_2_vp.proftext @@ -0,0 +1,25 @@ +:IR +foo +# Func Hash: +72057649435042473 +# Num Counters: +2 +# Counter Values: +60000 +40000 +# Num Value Kinds: +2 +# ValueKind = IPVK_IndirectCallTarget: +0 +# NumValueSites: +1 +2 +bar1:60000 +bar2:40000 +# ValueKind = IPVK_MemOPSize: +1 +# NumValueSites: +1 +2 +1:60000 +4:40000 Index: llvm/test/tools/llvm-profdata/overlap.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/overlap.test @@ -0,0 +1,23 @@ +RUN: llvm-profdata overlap %p/Inputs/overlap_1.proftext %p/Inputs/overlap_2.proftext | FileCheck %s -check-prefix=CHECK -check-prefix=RAW +RUN: llvm-profdata overlap -function=main %p/Inputs/overlap_1.proftext %p/Inputs/overlap_2.proftext | FileCheck %s -check-prefix=MAINFUNC -check-prefix=CHECK -check-prefix=RAW +RUN: llvm-profdata overlap -value-cutoff=15000 %p/Inputs/overlap_1.proftext %p/Inputs/overlap_2.proftext | FileCheck %s -check-prefix=MAINFUNC -check-prefix=CHECK -check-prefix=RAW +RUN: llvm-profdata merge %p/Inputs/overlap_1.proftext -o %t_1.profdata +RUN: llvm-profdata merge %p/Inputs/overlap_2.proftext -o %t_2.profdata +RUN: llvm-profdata overlap %t_1.profdata %t_2.profdata | FileCheck %s -check-prefix=CHECK -check-prefix=INDEX +MAINFUNC: Function level overlap info of main (hash=29212902728) +MAINFUNC: EdgeCount: (# of functions) +MAINFUNC: Overlap: 100.000% (1) +MAINFUNC: BaseCountSum: 200000 +MAINFUNC: TestCountSum: 20000 +CHECK: == Program level overlap info == +RAW: BaseFilename: {{.*}}overlap_1.proftext (# of functions: 4) +RAW: TestFilename: {{.*}}overlap_2.proftext (# of functions: 4) +INDEX: BaseFilename: {{.*}}_1.profdata (# of functions: 4) +INDEX: TestFilename: {{.*}}_2.profdata (# of functions: 4) +CHECK: EdgeCount: (# of functions) +CHECK: Overlap: 60.000% (2) +CHECK: Mismatch: 20.000% (1) +CHECK: Unique: 20.000% (1) +CHECK: BaseCountSum: 500000 +CHECK: TestCountSum: 50000 + Index: llvm/test/tools/llvm-profdata/overlap_cs.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/overlap_cs.test @@ -0,0 +1,13 @@ +RUN: llvm-profdata overlap -cs %p/Inputs/overlap_1_cs.proftext %p/Inputs/overlap_2_cs.proftext | FileCheck %s -check-prefix=OVERLAP -check-prefix=RAW +RUN: llvm-profdata merge %p/Inputs/overlap_1_cs.proftext -o %t_1_cs.profdata +RUN: llvm-profdata merge %p/Inputs/overlap_2_cs.proftext -o %t_2_cs.profdata +RUN: llvm-profdata overlap -cs %t_1_cs.profdata %t_2_cs.profdata | FileCheck %s -check-prefix=OVERLAP -check-prefix=INDEX +OVERLAP: == Program level overlap info == +RAW: BaseFilename: {{.*}}overlap_1_cs.proftext (# of functions: 1) +RAW: TestFilename: {{.*}}overlap_2_cs.proftext (# of functions: 1) +INDEX: BaseFilename: {{.*}}_1_cs.profdata (# of functions: 1) +INDEX: TestFilename: {{.*}}_2_cs.profdata (# of functions: 1) +OVERLAP: EdgeCount: (# of functions) +OVERLAP: Overlap: 80.000% (1) +OVERLAP: BaseCountSum: 10000 +OVERLAP: TestCountSum: 10000 Index: llvm/test/tools/llvm-profdata/overlap_vp.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-profdata/overlap_vp.test @@ -0,0 +1,21 @@ +RUN: llvm-profdata overlap %p/Inputs/overlap_1_vp.proftext %p/Inputs/overlap_2_vp.proftext | FileCheck %s -check-prefix=CHECK -check-prefix=RAW +RUN: llvm-profdata merge %p/Inputs/overlap_1_vp.proftext -o %t_1_vp.profdata +RUN: llvm-profdata merge %p/Inputs/overlap_2_vp.proftext -o %t_2_vp.profdata +RUN: llvm-profdata overlap %t_1_vp.profdata %t_2_vp.profdata | FileCheck %s -check-prefix=CHECK -check-prefix=INDEX +CHECK: == Program level overlap info == +RAW: BaseFilename: {{.*}}overlap_1_vp.proftext (# of functions: 1) +RAW: TestFilename: {{.*}}overlap_2_vp.proftext (# of functions: 1) +INDEX: BaseFilename: {{.*}}_1_vp.profdata (# of functions: 1) +INDEX: TestFilename: {{.*}}_2_vp.profdata (# of functions: 1) +CHECK: EdgeCount: (# of functions) +CHECK: Overlap: 80.000% (1) +CHECK: BaseCountSum: 100000 +CHECK: TestCountSum: 100000 +CHECK: IndirectCall: +CHECK: Overlap: 80.000% +CHECK: BaseCountSum: 100000 +CHECK: TestCountSum: 100000 +CHECK: MemOP: +CHECK: Overlap: 80.000% +CHECK: BaseCountSum: 100000 +CHECK: TestCountSum: 100000 Index: llvm/tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- llvm/tools/llvm-profdata/llvm-profdata.cpp +++ llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -200,9 +200,42 @@ } } +/// Computer the overlap b/w profile BaseFilename and TestFileName, +/// and store the program level result to Overlap. +static void overlapInput(const std::string &BaseFilename, + const std::string &TestFilename, WriterContext *WC, + OverlapStats &Overlap, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + auto ReaderOrErr = InstrProfReader::create(TestFilename); + if (Error E = ReaderOrErr.takeError()) { + // Skip the empty profiles by returning sliently. + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (IPE != instrprof_error::empty_raw_profile) + WC->Err = make_error(IPE); + } + + auto Reader = std::move(ReaderOrErr.get()); + for (auto &I : *Reader) { + OverlapStats FuncOverlap; + WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); + if (FuncOverlap.Valid) { + OS << "Function level overlap info of " << I.Name << " (hash=" << I.Hash + << ")\n"; + FuncOverlap.dump(OS); + } + } + OS << "== Program level overlap info ==\n" + << " BaseFilename: " << BaseFilename + << " (# of functions: " << Overlap.BaseSum.NumFuncs << ")\n" + << " TestFilename: " << TestFilename + << " (# of functions: " << Overlap.TestSum.NumFuncs << ")\n"; + Overlap.dump(OS); +} + /// Load an input into a writer context. static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, - WriterContext *WC) { + WriterContext *WC, bool IsCS) { std::unique_lock CtxGuard{WC->Lock}; // If there's a pending hard error, don't do more work. @@ -312,14 +345,14 @@ if (NumThreads == 1) { for (const auto &Input : Inputs) - loadInput(Input, Remapper, Contexts[0].get()); + loadInput(Input, Remapper, Contexts[0].get(), false); } else { ThreadPool Pool(NumThreads); // Load the inputs in parallel (N/NumThreads serial steps). unsigned Ctx = 0; for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); + Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get(), false); Ctx = (Ctx + 1) % NumThreads; } Pool.wait(); @@ -608,6 +641,64 @@ return 0; } +/// Computer the overlap b/w profile BaseFilename and profile TestFilename. +static void overlapInstrProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + WriterContext Context(false, ErrorLock, WriterErrorCodes); + WeightedFile WeightedInput{BaseFilename, 1}; + OverlapStats Overlap; + if (!Overlap.getCountSums(BaseFilename, TestFilename, IsCS)) + exitWithError("Error in getting profile count sums"); + if (Overlap.BaseSum.EdgeCount < 1.0f) { + OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; + exit(0); + } + if (Overlap.TestSum.EdgeCount < 1.0f) { + OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; + exit(0); + } + loadInput(WeightedInput, nullptr, &Context, IsCS); + overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, + IsCS); +} + +static int overlap_main(int argc, const char *argv[]) { + cl::opt BaseFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt TestFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt Output("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputA("o", cl::desc("Alias for --output"), + cl::aliasopt(Output)); + cl::opt IsCS("cs", cl::init(false), + cl::desc("For context sensitive counts")); + cl::opt ValueCutoff( + "value-cutoff", cl::init(-1), + cl::desc( + "Function level overlap information for every function in test " + "profile with max count value greater then the parameter value")); + cl::opt FuncNameFilter( + "function", + cl::desc("Function level overlap information for matching functions")); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); + + std::error_code EC; + raw_fd_ostream OS(Output.data(), EC, sys::fs::F_Text); + if (EC) + exitWithErrorCode(EC, Output); + + overlapInstrProfile(BaseFilename, TestFilename, + OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, + IsCS); + + return 0; +} + typedef struct ValueSitesStats { ValueSitesStats() : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), @@ -882,7 +973,7 @@ } static int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, + cl::opt Filename(cl::Positional, cl::Required, cl::desc("")); cl::opt ShowCounts("counts", cl::init(false), @@ -965,6 +1056,8 @@ func = merge_main; else if (strcmp(argv[1], "show") == 0) func = show_main; + else if (strcmp(argv[1], "overlap") == 0) + func = overlap_main; if (func) { std::string Invocation(ProgName.str() + " " + argv[1]); @@ -979,7 +1072,7 @@ << "USAGE: " << ProgName << " [args...]\n" << "USAGE: " << ProgName << " -help\n\n" << "See each individual command --help for more details.\n" - << "Available commands: merge, show\n"; + << "Available commands: merge, show, overlap\n"; return 0; } } @@ -989,6 +1082,6 @@ else errs() << ProgName << ": Unknown command!\n"; - errs() << "USAGE: " << ProgName << " [args...]\n"; + errs() << "USAGE: " << ProgName << " [args...]\n"; return 1; }