1846a627fSDuncan P. N. Exon Smith //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
2846a627fSDuncan P. N. Exon Smith //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6846a627fSDuncan P. N. Exon Smith //
7846a627fSDuncan P. N. Exon Smith //===----------------------------------------------------------------------===//
8846a627fSDuncan P. N. Exon Smith //
9846a627fSDuncan P. N. Exon Smith // llvm-profdata merges .profdata files.
10846a627fSDuncan P. N. Exon Smith //
11846a627fSDuncan P. N. Exon Smith //===----------------------------------------------------------------------===//
12846a627fSDuncan P. N. Exon Smith 
13c21a44daSNathan Slingerland #include "llvm/ADT/SmallSet.h"
147f5b47ddSNathan Slingerland #include "llvm/ADT/SmallVector.h"
15846a627fSDuncan P. N. Exon Smith #include "llvm/ADT/StringRef.h"
1665d7fd02SEllis Hoag #include "llvm/DebugInfo/DWARF/DWARFContext.h"
17d9903888SChandler Carruth #include "llvm/IR/LLVMContext.h"
1865d7fd02SEllis Hoag #include "llvm/Object/Binary.h"
1965d7fd02SEllis Hoag #include "llvm/ProfileData/InstrProfCorrelator.h"
20f8d79198SJustin Bogner #include "llvm/ProfileData/InstrProfReader.h"
21b9bd7f85SJustin Bogner #include "llvm/ProfileData/InstrProfWriter.h"
22216575e5SSnehasish Kumar #include "llvm/ProfileData/MemProf.h"
23d68aae24SEaswaran Raman #include "llvm/ProfileData/ProfileCommon.h"
247cca33b4SSnehasish Kumar #include "llvm/ProfileData/RawMemProfReader.h"
25d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfReader.h"
26d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfWriter.h"
27846a627fSDuncan P. N. Exon Smith #include "llvm/Support/CommandLine.h"
288d581857SRong Xu #include "llvm/Support/Discriminator.h"
297f5b47ddSNathan Slingerland #include "llvm/Support/Errc.h"
30d59664f4SBenjamin Kramer #include "llvm/Support/FileSystem.h"
31423380f9SJustin Bogner #include "llvm/Support/Format.h"
3253cf5302Sweihe #include "llvm/Support/FormattedStream.h"
33197194b6SRui Ueyama #include "llvm/Support/InitLLVM.h"
34846a627fSDuncan P. N. Exon Smith #include "llvm/Support/MemoryBuffer.h"
3516132e6fSBenjamin Kramer #include "llvm/Support/Path.h"
365342dd6bSBruno Ricci #include "llvm/Support/ThreadPool.h"
3753cf5302Sweihe #include "llvm/Support/Threading.h"
38ef598759SFangrui Song #include "llvm/Support/WithColor.h"
39846a627fSDuncan P. N. Exon Smith #include "llvm/Support/raw_ostream.h"
407f5b47ddSNathan Slingerland #include <algorithm>
418ef5710eSLuboš Luňák #include <queue>
42846a627fSDuncan P. N. Exon Smith 
43846a627fSDuncan P. N. Exon Smith using namespace llvm;
44846a627fSDuncan P. N. Exon Smith 
45a0c0857eSWei Mi enum ProfileFormat {
46a0c0857eSWei Mi   PF_None = 0,
47a0c0857eSWei Mi   PF_Text,
48a0c0857eSWei Mi   PF_Compact_Binary,
49be907324SWei Mi   PF_Ext_Binary,
50a0c0857eSWei Mi   PF_GCC,
51d9be2c7eSWei Mi   PF_Binary
52a0c0857eSWei Mi };
536f7c19a4SXinliang David Li 
warn(Twine Message,std::string Whence="",std::string Hint="")54e46b7565SJonas Devlieghere static void warn(Twine Message, std::string Whence = "",
55faaa42adSVedant Kumar                  std::string Hint = "") {
56e46b7565SJonas Devlieghere   WithColor::warning();
57f8d79198SJustin Bogner   if (!Whence.empty())
58f8d79198SJustin Bogner     errs() << Whence << ": ";
59f8d79198SJustin Bogner   errs() << Message << "\n";
604f823667SNathan Slingerland   if (!Hint.empty())
61e46b7565SJonas Devlieghere     WithColor::note() << Hint << "\n";
62188efda5SVedant Kumar }
63188efda5SVedant Kumar 
warn(Error E,StringRef Whence="")646da7d314SMatthew Voss static void warn(Error E, StringRef Whence = "") {
656da7d314SMatthew Voss   if (E.isA<InstrProfError>()) {
666da7d314SMatthew Voss     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
676da7d314SMatthew Voss       warn(IPE.message(), std::string(Whence), std::string(""));
686da7d314SMatthew Voss     });
696da7d314SMatthew Voss   }
706da7d314SMatthew Voss }
716da7d314SMatthew Voss 
exitWithError(Twine Message,std::string Whence="",std::string Hint="")72188efda5SVedant Kumar static void exitWithError(Twine Message, std::string Whence = "",
73188efda5SVedant Kumar                           std::string Hint = "") {
74e46b7565SJonas Devlieghere   WithColor::error();
75e46b7565SJonas Devlieghere   if (!Whence.empty())
76e46b7565SJonas Devlieghere     errs() << Whence << ": ";
77e46b7565SJonas Devlieghere   errs() << Message << "\n";
78e46b7565SJonas Devlieghere   if (!Hint.empty())
79e46b7565SJonas Devlieghere     WithColor::note() << Hint << "\n";
80846a627fSDuncan P. N. Exon Smith   ::exit(1);
81846a627fSDuncan P. N. Exon Smith }
82846a627fSDuncan P. N. Exon Smith 
exitWithError(Error E,StringRef Whence="")839152fd17SVedant Kumar static void exitWithError(Error E, StringRef Whence = "") {
849152fd17SVedant Kumar   if (E.isA<InstrProfError>()) {
859152fd17SVedant Kumar     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
869152fd17SVedant Kumar       instrprof_error instrError = IPE.get();
879152fd17SVedant Kumar       StringRef Hint = "";
884f823667SNathan Slingerland       if (instrError == instrprof_error::unrecognized_format) {
897cca33b4SSnehasish Kumar         // Hint in case user missed specifying the profile type.
907cca33b4SSnehasish Kumar         Hint = "Perhaps you forgot to use the --sample or --memory option?";
914f823667SNathan Slingerland       }
92adcd0268SBenjamin Kramer       exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
939152fd17SVedant Kumar     });
9434a62f96SFangrui Song     return;
954f823667SNathan Slingerland   }
969152fd17SVedant Kumar 
97adcd0268SBenjamin Kramer   exitWithError(toString(std::move(E)), std::string(Whence));
989152fd17SVedant Kumar }
999152fd17SVedant Kumar 
exitWithErrorCode(std::error_code EC,StringRef Whence="")1009152fd17SVedant Kumar static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
101adcd0268SBenjamin Kramer   exitWithError(EC.message(), std::string(Whence));
1024f823667SNathan Slingerland }
1034f823667SNathan Slingerland 
10402b6fa90SDuncan P. N. Exon Smith namespace {
1057cca33b4SSnehasish Kumar enum ProfileKinds { instr, sample, memory };
1060fcfe897SVedant Kumar enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid };
1070fcfe897SVedant Kumar }
1080fcfe897SVedant Kumar 
warnOrExitGivenError(FailureMode FailMode,std::error_code EC,StringRef Whence="")1090fcfe897SVedant Kumar static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
1100fcfe897SVedant Kumar                                  StringRef Whence = "") {
1110fcfe897SVedant Kumar   if (FailMode == failIfAnyAreInvalid)
1120fcfe897SVedant Kumar     exitWithErrorCode(EC, Whence);
1130fcfe897SVedant Kumar   else
114adcd0268SBenjamin Kramer     warn(EC.message(), std::string(Whence));
11502b6fa90SDuncan P. N. Exon Smith }
116618bcea7SJustin Bogner 
handleMergeWriterError(Error E,StringRef WhenceFile="",StringRef WhenceFunction="",bool ShowHint=true)1179152fd17SVedant Kumar static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
118e6e30d5eSNathan Slingerland                                    StringRef WhenceFunction = "",
119d3babdbcSDiego Novillo                                    bool ShowHint = true) {
120e6e30d5eSNathan Slingerland   if (!WhenceFile.empty())
121e6e30d5eSNathan Slingerland     errs() << WhenceFile << ": ";
122e6e30d5eSNathan Slingerland   if (!WhenceFunction.empty())
123e6e30d5eSNathan Slingerland     errs() << WhenceFunction << ": ";
1249152fd17SVedant Kumar 
1259152fd17SVedant Kumar   auto IPE = instrprof_error::success;
1269152fd17SVedant Kumar   E = handleErrors(std::move(E),
1279152fd17SVedant Kumar                    [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
1289152fd17SVedant Kumar                      IPE = E->get();
1299152fd17SVedant Kumar                      return Error(std::move(E));
1309152fd17SVedant Kumar                    });
1319152fd17SVedant Kumar   errs() << toString(std::move(E)) << "\n";
132e6e30d5eSNathan Slingerland 
133e6e30d5eSNathan Slingerland   if (ShowHint) {
134e6e30d5eSNathan Slingerland     StringRef Hint = "";
1359152fd17SVedant Kumar     if (IPE != instrprof_error::success) {
1369152fd17SVedant Kumar       switch (IPE) {
13711c938d1SNathan Slingerland       case instrprof_error::hash_mismatch:
13811c938d1SNathan Slingerland       case instrprof_error::count_mismatch:
13911c938d1SNathan Slingerland       case instrprof_error::value_site_count_mismatch:
140d3babdbcSDiego Novillo         Hint = "Make sure that all profile data to be merged is generated "
141e6e30d5eSNathan Slingerland                "from the same binary.";
14211c938d1SNathan Slingerland         break;
143b2d95f0dSNathan Slingerland       default:
144b2d95f0dSNathan Slingerland         break;
145e6e30d5eSNathan Slingerland       }
146e6e30d5eSNathan Slingerland     }
147e6e30d5eSNathan Slingerland 
148e6e30d5eSNathan Slingerland     if (!Hint.empty())
149e6e30d5eSNathan Slingerland       errs() << Hint << "\n";
150e6e30d5eSNathan Slingerland   }
151e6e30d5eSNathan Slingerland }
152e6e30d5eSNathan Slingerland 
1533164fcfdSRichard Smith namespace {
1543164fcfdSRichard Smith /// A remapper from original symbol names to new symbol names based on a file
1553164fcfdSRichard Smith /// containing a list of mappings from old name to new name.
1563164fcfdSRichard Smith class SymbolRemapper {
1573164fcfdSRichard Smith   std::unique_ptr<MemoryBuffer> File;
1583164fcfdSRichard Smith   DenseMap<StringRef, StringRef> RemappingTable;
1593164fcfdSRichard Smith 
1603164fcfdSRichard Smith public:
1613164fcfdSRichard Smith   /// Build a SymbolRemapper from a file containing a list of old/new symbols.
create(StringRef InputFile)1623164fcfdSRichard Smith   static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
1633164fcfdSRichard Smith     auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
1643164fcfdSRichard Smith     if (!BufOrError)
1653164fcfdSRichard Smith       exitWithErrorCode(BufOrError.getError(), InputFile);
1663164fcfdSRichard Smith 
1670eaee545SJonas Devlieghere     auto Remapper = std::make_unique<SymbolRemapper>();
1683164fcfdSRichard Smith     Remapper->File = std::move(BufOrError.get());
1693164fcfdSRichard Smith 
1703164fcfdSRichard Smith     for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
1713164fcfdSRichard Smith          !LineIt.is_at_eof(); ++LineIt) {
1723164fcfdSRichard Smith       std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
1733164fcfdSRichard Smith       if (Parts.first.empty() || Parts.second.empty() ||
1743164fcfdSRichard Smith           Parts.second.count(' ')) {
1753164fcfdSRichard Smith         exitWithError("unexpected line in remapping file",
1763164fcfdSRichard Smith                       (InputFile + ":" + Twine(LineIt.line_number())).str(),
1773164fcfdSRichard Smith                       "expected 'old_symbol new_symbol'");
1783164fcfdSRichard Smith       }
1793164fcfdSRichard Smith       Remapper->RemappingTable.insert(Parts);
1803164fcfdSRichard Smith     }
1813164fcfdSRichard Smith     return Remapper;
1823164fcfdSRichard Smith   }
1833164fcfdSRichard Smith 
1843164fcfdSRichard Smith   /// Attempt to map the given old symbol into a new symbol.
1853164fcfdSRichard Smith   ///
1863164fcfdSRichard Smith   /// \return The new symbol, or \p Name if no such symbol was found.
operator ()(StringRef Name)1873164fcfdSRichard Smith   StringRef operator()(StringRef Name) {
1883164fcfdSRichard Smith     StringRef New = RemappingTable.lookup(Name);
1893164fcfdSRichard Smith     return New.empty() ? Name : New;
1903164fcfdSRichard Smith   }
1913164fcfdSRichard Smith };
1923164fcfdSRichard Smith }
1933164fcfdSRichard Smith 
1947f5b47ddSNathan Slingerland struct WeightedFile {
1959a1bfcfaSXinliang David Li   std::string Filename;
1967f5b47ddSNathan Slingerland   uint64_t Weight;
1977f5b47ddSNathan Slingerland };
1987f5b47ddSNathan Slingerland typedef SmallVector<WeightedFile, 5> WeightedFileVector;
1997f5b47ddSNathan Slingerland 
200e3a0bf50SVedant Kumar /// Keep track of merged data and reported errors.
201e3a0bf50SVedant Kumar struct WriterContext {
202e3a0bf50SVedant Kumar   std::mutex Lock;
203e3a0bf50SVedant Kumar   InstrProfWriter Writer;
2040fcfe897SVedant Kumar   std::vector<std::pair<Error, std::string>> Errors;
205e3a0bf50SVedant Kumar   std::mutex &ErrLock;
206e3a0bf50SVedant Kumar   SmallSet<instrprof_error, 4> &WriterErrorCodes;
207e3a0bf50SVedant Kumar 
WriterContextWriterContext208e3a0bf50SVedant Kumar   WriterContext(bool IsSparse, std::mutex &ErrLock,
209e3a0bf50SVedant Kumar                 SmallSet<instrprof_error, 4> &WriterErrorCodes)
210f44473ecSKazu Hirata       : Writer(IsSparse), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {
211f44473ecSKazu Hirata   }
212e3a0bf50SVedant Kumar };
213e3a0bf50SVedant Kumar 
214998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and TestFileName,
215998b97f6SRong Xu /// and store the program level result to Overlap.
overlapInput(const std::string & BaseFilename,const std::string & TestFilename,WriterContext * WC,OverlapStats & Overlap,const OverlapFuncFilters & FuncFilter,raw_fd_ostream & OS,bool IsCS)216998b97f6SRong Xu static void overlapInput(const std::string &BaseFilename,
217998b97f6SRong Xu                          const std::string &TestFilename, WriterContext *WC,
218998b97f6SRong Xu                          OverlapStats &Overlap,
219998b97f6SRong Xu                          const OverlapFuncFilters &FuncFilter,
220998b97f6SRong Xu                          raw_fd_ostream &OS, bool IsCS) {
221998b97f6SRong Xu   auto ReaderOrErr = InstrProfReader::create(TestFilename);
222998b97f6SRong Xu   if (Error E = ReaderOrErr.takeError()) {
223998b97f6SRong Xu     // Skip the empty profiles by returning sliently.
224998b97f6SRong Xu     instrprof_error IPE = InstrProfError::take(std::move(E));
225998b97f6SRong Xu     if (IPE != instrprof_error::empty_raw_profile)
2260fcfe897SVedant Kumar       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename);
227998b97f6SRong Xu     return;
228998b97f6SRong Xu   }
229998b97f6SRong Xu 
230998b97f6SRong Xu   auto Reader = std::move(ReaderOrErr.get());
231998b97f6SRong Xu   for (auto &I : *Reader) {
232998b97f6SRong Xu     OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
233998b97f6SRong Xu     FuncOverlap.setFuncInfo(I.Name, I.Hash);
234998b97f6SRong Xu 
235998b97f6SRong Xu     WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
236998b97f6SRong Xu     FuncOverlap.dump(OS);
237998b97f6SRong Xu   }
238998b97f6SRong Xu }
239998b97f6SRong Xu 
240e3a0bf50SVedant Kumar /// Load an input into a writer context.
loadInput(const WeightedFile & Input,SymbolRemapper * Remapper,const InstrProfCorrelator * Correlator,const StringRef ProfiledBinary,WriterContext * WC)2413164fcfdSRichard Smith static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
24265d7fd02SEllis Hoag                       const InstrProfCorrelator *Correlator,
2430a418490SSnehasish Kumar                       const StringRef ProfiledBinary, WriterContext *WC) {
244e3a0bf50SVedant Kumar   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
245e3a0bf50SVedant Kumar 
246faaa42adSVedant Kumar   // Copy the filename, because llvm::ThreadPool copied the input "const
247faaa42adSVedant Kumar   // WeightedFile &" by value, making a reference to the filename within it
248faaa42adSVedant Kumar   // invalid outside of this packaged task.
2490fcfe897SVedant Kumar   std::string Filename = Input.Filename;
250e3a0bf50SVedant Kumar 
2510a418490SSnehasish Kumar   using ::llvm::memprof::RawMemProfReader;
2520a418490SSnehasish Kumar   if (RawMemProfReader::hasFormat(Input.Filename)) {
2530a418490SSnehasish Kumar     auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);
2540a418490SSnehasish Kumar     if (!ReaderOrErr) {
2550a418490SSnehasish Kumar       exitWithError(ReaderOrErr.takeError(), Input.Filename);
2560a418490SSnehasish Kumar     }
2570a418490SSnehasish Kumar     std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());
2580a418490SSnehasish Kumar     // Check if the profile types can be merged, e.g. clang frontend profiles
2590a418490SSnehasish Kumar     // should not be merged with memprof profiles.
2600a418490SSnehasish Kumar     if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
2610a418490SSnehasish Kumar       consumeError(std::move(E));
2620a418490SSnehasish Kumar       WC->Errors.emplace_back(
2630a418490SSnehasish Kumar           make_error<StringError>(
2640a418490SSnehasish Kumar               "Cannot merge MemProf profile with Clang generated profile.",
2650a418490SSnehasish Kumar               std::error_code()),
2660a418490SSnehasish Kumar           Filename);
2670a418490SSnehasish Kumar       return;
2680a418490SSnehasish Kumar     }
2690a418490SSnehasish Kumar 
2706dd6a616SSnehasish Kumar     auto MemProfError = [&](Error E) {
2710a418490SSnehasish Kumar       instrprof_error IPE = InstrProfError::take(std::move(E));
2720a418490SSnehasish Kumar       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
2736dd6a616SSnehasish Kumar     };
2746dd6a616SSnehasish Kumar 
2756dd6a616SSnehasish Kumar     // Add the frame mappings into the writer context.
2766dd6a616SSnehasish Kumar     const auto &IdToFrame = Reader->getFrameMapping();
2776dd6a616SSnehasish Kumar     for (const auto &I : IdToFrame) {
2786dd6a616SSnehasish Kumar       bool Succeeded = WC->Writer.addMemProfFrame(
2796dd6a616SSnehasish Kumar           /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
2806dd6a616SSnehasish Kumar       // If we weren't able to add the frame mappings then it doesn't make sense
2816dd6a616SSnehasish Kumar       // to try to add the records from this profile.
2826dd6a616SSnehasish Kumar       if (!Succeeded)
2836dd6a616SSnehasish Kumar         return;
2846dd6a616SSnehasish Kumar     }
2856dd6a616SSnehasish Kumar     const auto &FunctionProfileData = Reader->getProfileData();
2866dd6a616SSnehasish Kumar     // Add the memprof records into the writer context.
2876dd6a616SSnehasish Kumar     for (const auto &I : FunctionProfileData) {
2886dd6a616SSnehasish Kumar       WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second);
2890a418490SSnehasish Kumar     }
2900a418490SSnehasish Kumar     return;
2910a418490SSnehasish Kumar   }
2920a418490SSnehasish Kumar 
29365d7fd02SEllis Hoag   auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator);
2942c684cfdSRong Xu   if (Error E = ReaderOrErr.takeError()) {
2952c684cfdSRong Xu     // Skip the empty profiles by returning sliently.
2962c684cfdSRong Xu     instrprof_error IPE = InstrProfError::take(std::move(E));
2972c684cfdSRong Xu     if (IPE != instrprof_error::empty_raw_profile)
2980fcfe897SVedant Kumar       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
299e3a0bf50SVedant Kumar     return;
3002c684cfdSRong Xu   }
301e3a0bf50SVedant Kumar 
302e3a0bf50SVedant Kumar   auto Reader = std::move(ReaderOrErr.get());
30313d89477SSnehasish Kumar   if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
304142d522dSMarkus Böck     consumeError(std::move(E));
3050fcfe897SVedant Kumar     WC->Errors.emplace_back(
3060fcfe897SVedant Kumar         make_error<StringError>(
307e3a0bf50SVedant Kumar             "Merge IR generated profile with Clang generated profile.",
3080fcfe897SVedant Kumar             std::error_code()),
3090fcfe897SVedant Kumar         Filename);
310e3a0bf50SVedant Kumar     return;
311e3a0bf50SVedant Kumar   }
312e3a0bf50SVedant Kumar 
313e3a0bf50SVedant Kumar   for (auto &I : *Reader) {
3143164fcfdSRichard Smith     if (Remapper)
3153164fcfdSRichard Smith       I.Name = (*Remapper)(I.Name);
316fe90d86cSRong Xu     const StringRef FuncName = I.Name;
31798cce003SDavid Blaikie     bool Reported = false;
31898cce003SDavid Blaikie     WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
31998cce003SDavid Blaikie       if (Reported) {
32098cce003SDavid Blaikie         consumeError(std::move(E));
32198cce003SDavid Blaikie         return;
32298cce003SDavid Blaikie       }
32398cce003SDavid Blaikie       Reported = true;
324e3a0bf50SVedant Kumar       // Only show hint the first time an error occurs.
325e3a0bf50SVedant Kumar       instrprof_error IPE = InstrProfError::take(std::move(E));
326e3a0bf50SVedant Kumar       std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
327e3a0bf50SVedant Kumar       bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
328e3a0bf50SVedant Kumar       handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
329fe90d86cSRong Xu                              FuncName, firstTime);
33098cce003SDavid Blaikie     });
331e3a0bf50SVedant Kumar   }
3320fcfe897SVedant Kumar   if (Reader->hasError())
3330fcfe897SVedant Kumar     if (Error E = Reader->getError())
3340fcfe897SVedant Kumar       WC->Errors.emplace_back(std::move(E), Filename);
335e3a0bf50SVedant Kumar }
336e3a0bf50SVedant Kumar 
337e3a0bf50SVedant Kumar /// Merge the \p Src writer context into \p Dst.
mergeWriterContexts(WriterContext * Dst,WriterContext * Src)338e3a0bf50SVedant Kumar static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
3390fcfe897SVedant Kumar   for (auto &ErrorPair : Src->Errors)
3400fcfe897SVedant Kumar     Dst->Errors.push_back(std::move(ErrorPair));
3410fcfe897SVedant Kumar   Src->Errors.clear();
342faaa42adSVedant Kumar 
34398cce003SDavid Blaikie   Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
3440fcfe897SVedant Kumar     instrprof_error IPE = InstrProfError::take(std::move(E));
3450fcfe897SVedant Kumar     std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
3460fcfe897SVedant Kumar     bool firstTime = Dst->WriterErrorCodes.insert(IPE).second;
3470fcfe897SVedant Kumar     if (firstTime)
3480fcfe897SVedant Kumar       warn(toString(make_error<InstrProfError>(IPE)));
34998cce003SDavid Blaikie   });
350e3a0bf50SVedant Kumar }
351e3a0bf50SVedant Kumar 
writeInstrProfile(StringRef OutputFilename,ProfileFormat OutputFormat,InstrProfWriter & Writer)35278fe6a3eSWei Mi static void writeInstrProfile(StringRef OutputFilename,
35378fe6a3eSWei Mi                               ProfileFormat OutputFormat,
35478fe6a3eSWei Mi                               InstrProfWriter &Writer) {
35578fe6a3eSWei Mi   std::error_code EC;
3568ad998a6SAbhina Sreeskantharajan   raw_fd_ostream Output(OutputFilename.data(), EC,
35782b3e28eSAbhina Sreeskantharajan                         OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
3588ad998a6SAbhina Sreeskantharajan                                                 : sys::fs::OF_None);
35978fe6a3eSWei Mi   if (EC)
36078fe6a3eSWei Mi     exitWithErrorCode(EC, OutputFilename);
36178fe6a3eSWei Mi 
36278fe6a3eSWei Mi   if (OutputFormat == PF_Text) {
36378fe6a3eSWei Mi     if (Error E = Writer.writeText(Output))
3646da7d314SMatthew Voss       warn(std::move(E));
36578fe6a3eSWei Mi   } else {
366ea23c38dSFangrui Song     if (Output.is_displayed())
367ea23c38dSFangrui Song       exitWithError("cannot write a non-text format profile to the terminal");
3686da7d314SMatthew Voss     if (Error E = Writer.write(Output))
3696da7d314SMatthew Voss       warn(std::move(E));
37078fe6a3eSWei Mi   }
37178fe6a3eSWei Mi }
37278fe6a3eSWei Mi 
mergeInstrProfile(const WeightedFileVector & Inputs,StringRef DebugInfoFilename,SymbolRemapper * Remapper,StringRef OutputFilename,ProfileFormat OutputFormat,bool OutputSparse,unsigned NumThreads,FailureMode FailMode,const StringRef ProfiledBinary)3737f5b47ddSNathan Slingerland static void mergeInstrProfile(const WeightedFileVector &Inputs,
37465d7fd02SEllis Hoag                               StringRef DebugInfoFilename,
3753164fcfdSRichard Smith                               SymbolRemapper *Remapper,
3766f7c19a4SXinliang David Li                               StringRef OutputFilename,
377e3a0bf50SVedant Kumar                               ProfileFormat OutputFormat, bool OutputSparse,
3780a418490SSnehasish Kumar                               unsigned NumThreads, FailureMode FailMode,
3790a418490SSnehasish Kumar                               const StringRef ProfiledBinary) {
380d9be2c7eSWei Mi   if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
381be907324SWei Mi       OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text)
3828ea2a58aSFangrui Song     exitWithError("unknown format is specified");
3836f7c19a4SXinliang David Li 
38465d7fd02SEllis Hoag   std::unique_ptr<InstrProfCorrelator> Correlator;
38565d7fd02SEllis Hoag   if (!DebugInfoFilename.empty()) {
38665d7fd02SEllis Hoag     if (auto Err =
38765d7fd02SEllis Hoag             InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator))
38865d7fd02SEllis Hoag       exitWithError(std::move(Err), DebugInfoFilename);
38965d7fd02SEllis Hoag     if (auto Err = Correlator->correlateProfileData())
39065d7fd02SEllis Hoag       exitWithError(std::move(Err), DebugInfoFilename);
39165d7fd02SEllis Hoag   }
39265d7fd02SEllis Hoag 
393e3a0bf50SVedant Kumar   std::mutex ErrorLock;
3949152fd17SVedant Kumar   SmallSet<instrprof_error, 4> WriterErrorCodes;
395e3a0bf50SVedant Kumar 
396e3a0bf50SVedant Kumar   // If NumThreads is not specified, auto-detect a good default.
397e3a0bf50SVedant Kumar   if (NumThreads == 0)
3988404aeb5SAlexandre Ganea     NumThreads = std::min(hardware_concurrency().compute_thread_count(),
3998404aeb5SAlexandre Ganea                           unsigned((Inputs.size() + 1) / 2));
4008404aeb5SAlexandre Ganea   // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails
4018404aeb5SAlexandre Ganea   // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't
4028404aeb5SAlexandre Ganea   // merged, thus the emitted file ends up with a PF_Unknown kind.
403e3a0bf50SVedant Kumar 
404e3a0bf50SVedant Kumar   // Initialize the writer contexts.
405e3a0bf50SVedant Kumar   SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
406e3a0bf50SVedant Kumar   for (unsigned I = 0; I < NumThreads; ++I)
4070eaee545SJonas Devlieghere     Contexts.emplace_back(std::make_unique<WriterContext>(
408e3a0bf50SVedant Kumar         OutputSparse, ErrorLock, WriterErrorCodes));
409e3a0bf50SVedant Kumar 
410e3a0bf50SVedant Kumar   if (NumThreads == 1) {
411e3a0bf50SVedant Kumar     for (const auto &Input : Inputs)
4120a418490SSnehasish Kumar       loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
4130a418490SSnehasish Kumar                 Contexts[0].get());
414e3a0bf50SVedant Kumar   } else {
4158404aeb5SAlexandre Ganea     ThreadPool Pool(hardware_concurrency(NumThreads));
416e3a0bf50SVedant Kumar 
417e3a0bf50SVedant Kumar     // Load the inputs in parallel (N/NumThreads serial steps).
418e3a0bf50SVedant Kumar     unsigned Ctx = 0;
4197f5b47ddSNathan Slingerland     for (const auto &Input : Inputs) {
4200a418490SSnehasish Kumar       Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
42165d7fd02SEllis Hoag                  Contexts[Ctx].get());
422e3a0bf50SVedant Kumar       Ctx = (Ctx + 1) % NumThreads;
423e3a0bf50SVedant Kumar     }
424e3a0bf50SVedant Kumar     Pool.wait();
42521ab20e0SVedant Kumar 
426e3a0bf50SVedant Kumar     // Merge the writer contexts together (~ lg(NumThreads) serial steps).
427e3a0bf50SVedant Kumar     unsigned Mid = Contexts.size() / 2;
428e3a0bf50SVedant Kumar     unsigned End = Contexts.size();
429e3a0bf50SVedant Kumar     assert(Mid > 0 && "Expected more than one context");
430e3a0bf50SVedant Kumar     do {
431e3a0bf50SVedant Kumar       for (unsigned I = 0; I < Mid; ++I)
432e3a0bf50SVedant Kumar         Pool.async(mergeWriterContexts, Contexts[I].get(),
433e3a0bf50SVedant Kumar                    Contexts[I + Mid].get());
434e3a0bf50SVedant Kumar       Pool.wait();
435e3a0bf50SVedant Kumar       if (End & 1) {
436e3a0bf50SVedant Kumar         Pool.async(mergeWriterContexts, Contexts[0].get(),
437e3a0bf50SVedant Kumar                    Contexts[End - 1].get());
438e3a0bf50SVedant Kumar         Pool.wait();
439e3a0bf50SVedant Kumar       }
440e3a0bf50SVedant Kumar       End = Mid;
441e3a0bf50SVedant Kumar       Mid /= 2;
442e3a0bf50SVedant Kumar     } while (Mid > 0);
443e3a0bf50SVedant Kumar   }
44421ab20e0SVedant Kumar 
4450fcfe897SVedant Kumar   // Handle deferred errors encountered during merging. If the number of errors
4460fcfe897SVedant Kumar   // is equal to the number of inputs the merge failed.
4470fcfe897SVedant Kumar   unsigned NumErrors = 0;
448188efda5SVedant Kumar   for (std::unique_ptr<WriterContext> &WC : Contexts) {
4490fcfe897SVedant Kumar     for (auto &ErrorPair : WC->Errors) {
4500fcfe897SVedant Kumar       ++NumErrors;
4510fcfe897SVedant Kumar       warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
452188efda5SVedant Kumar     }
4530fcfe897SVedant Kumar   }
4540fcfe897SVedant Kumar   if (NumErrors == Inputs.size() ||
4550fcfe897SVedant Kumar       (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
4568ea2a58aSFangrui Song     exitWithError("no profile can be merged");
457188efda5SVedant Kumar 
45878fe6a3eSWei Mi   writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
459b5794ca9SVedant Kumar }
460d5336ae2SDiego Novillo 
461a23f6234SWei Mi /// The profile entry for a function in instrumentation profile.
462a23f6234SWei Mi struct InstrProfileEntry {
463a23f6234SWei Mi   uint64_t MaxCount = 0;
464a23f6234SWei Mi   float ZeroCounterRatio = 0.0;
465a23f6234SWei Mi   InstrProfRecord *ProfRecord;
466a23f6234SWei Mi   InstrProfileEntry(InstrProfRecord *Record);
467a23f6234SWei Mi   InstrProfileEntry() = default;
468a23f6234SWei Mi };
469a23f6234SWei Mi 
InstrProfileEntry(InstrProfRecord * Record)470a23f6234SWei Mi InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
471a23f6234SWei Mi   ProfRecord = Record;
472a23f6234SWei Mi   uint64_t CntNum = Record->Counts.size();
473a23f6234SWei Mi   uint64_t ZeroCntNum = 0;
474a23f6234SWei Mi   for (size_t I = 0; I < CntNum; ++I) {
475a23f6234SWei Mi     MaxCount = std::max(MaxCount, Record->Counts[I]);
476a23f6234SWei Mi     ZeroCntNum += !Record->Counts[I];
477a23f6234SWei Mi   }
478a23f6234SWei Mi   ZeroCounterRatio = (float)ZeroCntNum / CntNum;
479a23f6234SWei Mi }
480a23f6234SWei Mi 
481a23f6234SWei Mi /// Either set all the counters in the instr profile entry \p IFE to -1
482a23f6234SWei Mi /// in order to drop the profile or scale up the counters in \p IFP to
483a23f6234SWei Mi /// be above hot threshold. We use the ratio of zero counters in the
484a23f6234SWei Mi /// profile of a function to decide the profile is helpful or harmful
485a23f6234SWei Mi /// for performance, and to choose whether to scale up or drop it.
updateInstrProfileEntry(InstrProfileEntry & IFE,uint64_t HotInstrThreshold,float ZeroCounterThreshold)486a23f6234SWei Mi static void updateInstrProfileEntry(InstrProfileEntry &IFE,
487a23f6234SWei Mi                                     uint64_t HotInstrThreshold,
488a23f6234SWei Mi                                     float ZeroCounterThreshold) {
489a23f6234SWei Mi   InstrProfRecord *ProfRecord = IFE.ProfRecord;
490a23f6234SWei Mi   if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
491a23f6234SWei Mi     // If all or most of the counters of the function are zero, the
492a23f6234SWei Mi     // profile is unaccountable and shuld be dropped. Reset all the
493a23f6234SWei Mi     // counters to be -1 and PGO profile-use will drop the profile.
494a23f6234SWei Mi     // All counters being -1 also implies that the function is hot so
495a23f6234SWei Mi     // PGO profile-use will also set the entry count metadata to be
496a23f6234SWei Mi     // above hot threshold.
497a23f6234SWei Mi     for (size_t I = 0; I < ProfRecord->Counts.size(); ++I)
498a23f6234SWei Mi       ProfRecord->Counts[I] = -1;
499a23f6234SWei Mi     return;
500a23f6234SWei Mi   }
501a23f6234SWei Mi 
502a23f6234SWei Mi   // Scale up the MaxCount to be multiple times above hot threshold.
503a23f6234SWei Mi   const unsigned MultiplyFactor = 3;
504a23f6234SWei Mi   uint64_t Numerator = HotInstrThreshold * MultiplyFactor;
505a23f6234SWei Mi   uint64_t Denominator = IFE.MaxCount;
506a23f6234SWei Mi   ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
507a23f6234SWei Mi     warn(toString(make_error<InstrProfError>(E)));
508a23f6234SWei Mi   });
509a23f6234SWei Mi }
510a23f6234SWei Mi 
511a23f6234SWei Mi const uint64_t ColdPercentileIdx = 15;
512a23f6234SWei Mi const uint64_t HotPercentileIdx = 11;
513a23f6234SWei Mi 
5148d581857SRong Xu using sampleprof::FSDiscriminatorPass;
5158d581857SRong Xu 
5168d581857SRong Xu // Internal options to set FSDiscriminatorPass. Used in merge and show
5178d581857SRong Xu // commands.
5188d581857SRong Xu static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
5198d581857SRong Xu     "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
5208d581857SRong Xu     cl::desc("Zero out the discriminator bits for the FS discrimiantor "
5218d581857SRong Xu              "pass beyond this value. The enum values are defined in "
5228d581857SRong Xu              "Support/Discriminator.h"),
5238d581857SRong Xu     cl::values(clEnumVal(Base, "Use base discriminators only"),
5248d581857SRong Xu                clEnumVal(Pass1, "Use base and pass 1 discriminators"),
5258d581857SRong Xu                clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
5268d581857SRong Xu                clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
5278d581857SRong Xu                clEnumVal(PassLast, "Use all discriminator bits (default)")));
5288d581857SRong Xu 
getDiscriminatorMask()5298d581857SRong Xu static unsigned getDiscriminatorMask() {
5308d581857SRong Xu   return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
5318d581857SRong Xu }
5328d581857SRong Xu 
533a23f6234SWei Mi /// Adjust the instr profile in \p WC based on the sample profile in
534a23f6234SWei Mi /// \p Reader.
535a23f6234SWei Mi static void
adjustInstrProfile(std::unique_ptr<WriterContext> & WC,std::unique_ptr<sampleprof::SampleProfileReader> & Reader,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)536a23f6234SWei Mi adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
537a23f6234SWei Mi                    std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
538a23f6234SWei Mi                    unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
539a23f6234SWei Mi                    unsigned InstrProfColdThreshold) {
540a23f6234SWei Mi   // Function to its entry in instr profile.
541a23f6234SWei Mi   StringMap<InstrProfileEntry> InstrProfileMap;
542a23f6234SWei Mi   InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
543a23f6234SWei Mi   for (auto &PD : WC->Writer.getProfileData()) {
544a23f6234SWei Mi     // Populate IPBuilder.
545a23f6234SWei Mi     for (const auto &PDV : PD.getValue()) {
546a23f6234SWei Mi       InstrProfRecord Record = PDV.second;
547a23f6234SWei Mi       IPBuilder.addRecord(Record);
548a23f6234SWei Mi     }
549a23f6234SWei Mi 
550a23f6234SWei Mi     // If a function has multiple entries in instr profile, skip it.
551a23f6234SWei Mi     if (PD.getValue().size() != 1)
552a23f6234SWei Mi       continue;
553a23f6234SWei Mi 
554a23f6234SWei Mi     // Initialize InstrProfileMap.
555a23f6234SWei Mi     InstrProfRecord *R = &PD.getValue().begin()->second;
556a23f6234SWei Mi     InstrProfileMap[PD.getKey()] = InstrProfileEntry(R);
557a23f6234SWei Mi   }
558a23f6234SWei Mi 
559a23f6234SWei Mi   ProfileSummary InstrPS = *IPBuilder.getSummary();
560a23f6234SWei Mi   ProfileSummary SamplePS = Reader->getSummary();
561a23f6234SWei Mi 
562a23f6234SWei Mi   // Compute cold thresholds for instr profile and sample profile.
563a23f6234SWei Mi   uint64_t ColdSampleThreshold =
564a23f6234SWei Mi       ProfileSummaryBuilder::getEntryForPercentile(
565a23f6234SWei Mi           SamplePS.getDetailedSummary(),
566a23f6234SWei Mi           ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
567a23f6234SWei Mi           .MinCount;
568a23f6234SWei Mi   uint64_t HotInstrThreshold =
569a23f6234SWei Mi       ProfileSummaryBuilder::getEntryForPercentile(
570a23f6234SWei Mi           InstrPS.getDetailedSummary(),
571a23f6234SWei Mi           ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
572a23f6234SWei Mi           .MinCount;
573a23f6234SWei Mi   uint64_t ColdInstrThreshold =
574a23f6234SWei Mi       InstrProfColdThreshold
575a23f6234SWei Mi           ? InstrProfColdThreshold
576a23f6234SWei Mi           : ProfileSummaryBuilder::getEntryForPercentile(
577a23f6234SWei Mi                 InstrPS.getDetailedSummary(),
578a23f6234SWei Mi                 ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
579a23f6234SWei Mi                 .MinCount;
580a23f6234SWei Mi 
581a23f6234SWei Mi   // Find hot/warm functions in sample profile which is cold in instr profile
582a23f6234SWei Mi   // and adjust the profiles of those functions in the instr profile.
583a23f6234SWei Mi   for (const auto &PD : Reader->getProfiles()) {
584b9db7036SHongtao Yu     auto &FContext = PD.first;
585b9db7036SHongtao Yu     const sampleprof::FunctionSamples &FS = PD.second;
586b9db7036SHongtao Yu     auto It = InstrProfileMap.find(FContext.toString());
587a23f6234SWei Mi     if (FS.getHeadSamples() > ColdSampleThreshold &&
588a23f6234SWei Mi         It != InstrProfileMap.end() &&
589a23f6234SWei Mi         It->second.MaxCount <= ColdInstrThreshold &&
590a23f6234SWei Mi         FS.getBodySamples().size() >= SupplMinSizeThreshold) {
591a23f6234SWei Mi       updateInstrProfileEntry(It->second, HotInstrThreshold,
592a23f6234SWei Mi                               ZeroCounterThreshold);
593a23f6234SWei Mi     }
594a23f6234SWei Mi   }
595a23f6234SWei Mi }
596a23f6234SWei Mi 
597a23f6234SWei Mi /// The main function to supplement instr profile with sample profile.
598a23f6234SWei Mi /// \Inputs contains the instr profile. \p SampleFilename specifies the
599a23f6234SWei Mi /// sample profile. \p OutputFilename specifies the output profile name.
600a23f6234SWei Mi /// \p OutputFormat specifies the output profile format. \p OutputSparse
601a23f6234SWei Mi /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
602a23f6234SWei Mi /// specifies the minimal size for the functions whose profile will be
603a23f6234SWei Mi /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
604a23f6234SWei Mi /// a function contains too many zero counters and whether its profile
605a23f6234SWei Mi /// should be dropped. \p InstrProfColdThreshold is the user specified
606a23f6234SWei Mi /// cold threshold which will override the cold threshold got from the
607a23f6234SWei Mi /// instr profile summary.
supplementInstrProfile(const WeightedFileVector & Inputs,StringRef SampleFilename,StringRef OutputFilename,ProfileFormat OutputFormat,bool OutputSparse,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)608a23f6234SWei Mi static void supplementInstrProfile(
609a23f6234SWei Mi     const WeightedFileVector &Inputs, StringRef SampleFilename,
610a23f6234SWei Mi     StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse,
611a23f6234SWei Mi     unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
612a23f6234SWei Mi     unsigned InstrProfColdThreshold) {
613a23f6234SWei Mi   if (OutputFilename.compare("-") == 0)
6148ea2a58aSFangrui Song     exitWithError("cannot write indexed profdata format to stdout");
615a23f6234SWei Mi   if (Inputs.size() != 1)
6168ea2a58aSFangrui Song     exitWithError("expect one input to be an instr profile");
617a23f6234SWei Mi   if (Inputs[0].Weight != 1)
6188ea2a58aSFangrui Song     exitWithError("expect instr profile doesn't have weight");
619a23f6234SWei Mi 
620a23f6234SWei Mi   StringRef InstrFilename = Inputs[0].Filename;
621a23f6234SWei Mi 
622a23f6234SWei Mi   // Read sample profile.
623a23f6234SWei Mi   LLVMContext Context;
6248d581857SRong Xu   auto ReaderOrErr = sampleprof::SampleProfileReader::create(
6258d581857SRong Xu       SampleFilename.str(), Context, FSDiscriminatorPassOption);
626a23f6234SWei Mi   if (std::error_code EC = ReaderOrErr.getError())
627a23f6234SWei Mi     exitWithErrorCode(EC, SampleFilename);
628a23f6234SWei Mi   auto Reader = std::move(ReaderOrErr.get());
629a23f6234SWei Mi   if (std::error_code EC = Reader->read())
630a23f6234SWei Mi     exitWithErrorCode(EC, SampleFilename);
631a23f6234SWei Mi 
632a23f6234SWei Mi   // Read instr profile.
633a23f6234SWei Mi   std::mutex ErrorLock;
634a23f6234SWei Mi   SmallSet<instrprof_error, 4> WriterErrorCodes;
635a23f6234SWei Mi   auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
636a23f6234SWei Mi                                             WriterErrorCodes);
6370a418490SSnehasish Kumar   loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());
638a23f6234SWei Mi   if (WC->Errors.size() > 0)
639a23f6234SWei Mi     exitWithError(std::move(WC->Errors[0].first), InstrFilename);
640a23f6234SWei Mi 
641a23f6234SWei Mi   adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
642a23f6234SWei Mi                      InstrProfColdThreshold);
643a23f6234SWei Mi   writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
644a23f6234SWei Mi }
645a23f6234SWei Mi 
6463164fcfdSRichard Smith /// Make a copy of the given function samples with all symbol names remapped
6473164fcfdSRichard Smith /// by the provided symbol remapper.
6483164fcfdSRichard Smith static sampleprof::FunctionSamples
remapSamples(const sampleprof::FunctionSamples & Samples,SymbolRemapper & Remapper,sampleprof_error & Error)6493164fcfdSRichard Smith remapSamples(const sampleprof::FunctionSamples &Samples,
6503164fcfdSRichard Smith              SymbolRemapper &Remapper, sampleprof_error &Error) {
6513164fcfdSRichard Smith   sampleprof::FunctionSamples Result;
6523164fcfdSRichard Smith   Result.setName(Remapper(Samples.getName()));
6533164fcfdSRichard Smith   Result.addTotalSamples(Samples.getTotalSamples());
6543164fcfdSRichard Smith   Result.addHeadSamples(Samples.getHeadSamples());
6553164fcfdSRichard Smith   for (const auto &BodySample : Samples.getBodySamples()) {
6568d581857SRong Xu     uint32_t MaskedDiscriminator =
6578d581857SRong Xu         BodySample.first.Discriminator & getDiscriminatorMask();
6588d581857SRong Xu     Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
6593164fcfdSRichard Smith                           BodySample.second.getSamples());
6603164fcfdSRichard Smith     for (const auto &Target : BodySample.second.getCallTargets()) {
6613164fcfdSRichard Smith       Result.addCalledTargetSamples(BodySample.first.LineOffset,
6628d581857SRong Xu                                     MaskedDiscriminator,
6633164fcfdSRichard Smith                                     Remapper(Target.first()), Target.second);
6643164fcfdSRichard Smith     }
6653164fcfdSRichard Smith   }
6663164fcfdSRichard Smith   for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
6673164fcfdSRichard Smith     sampleprof::FunctionSamplesMap &Target =
6683164fcfdSRichard Smith         Result.functionSamplesAt(CallsiteSamples.first);
6693164fcfdSRichard Smith     for (const auto &Callsite : CallsiteSamples.second) {
6703164fcfdSRichard Smith       sampleprof::FunctionSamples Remapped =
6713164fcfdSRichard Smith           remapSamples(Callsite.second, Remapper, Error);
672adcd0268SBenjamin Kramer       MergeResult(Error,
673adcd0268SBenjamin Kramer                   Target[std::string(Remapped.getName())].merge(Remapped));
6743164fcfdSRichard Smith     }
6753164fcfdSRichard Smith   }
6763164fcfdSRichard Smith   return Result;
6773164fcfdSRichard Smith }
6783164fcfdSRichard Smith 
6796f7c19a4SXinliang David Li static sampleprof::SampleProfileFormat FormatMap[] = {
680be907324SWei Mi     sampleprof::SPF_None,
681be907324SWei Mi     sampleprof::SPF_Text,
682be907324SWei Mi     sampleprof::SPF_Compact_Binary,
683be907324SWei Mi     sampleprof::SPF_Ext_Binary,
684be907324SWei Mi     sampleprof::SPF_GCC,
685be907324SWei Mi     sampleprof::SPF_Binary};
6866f7c19a4SXinliang David Li 
687798e59b8SWei Mi static std::unique_ptr<MemoryBuffer>
getInputFileBuf(const StringRef & InputFile)688798e59b8SWei Mi getInputFileBuf(const StringRef &InputFile) {
689798e59b8SWei Mi   if (InputFile == "")
690798e59b8SWei Mi     return {};
691798e59b8SWei Mi 
692798e59b8SWei Mi   auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
693798e59b8SWei Mi   if (!BufOrError)
694798e59b8SWei Mi     exitWithErrorCode(BufOrError.getError(), InputFile);
695798e59b8SWei Mi 
696798e59b8SWei Mi   return std::move(*BufOrError);
697798e59b8SWei Mi }
698798e59b8SWei Mi 
populateProfileSymbolList(MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & PSL)699798e59b8SWei Mi static void populateProfileSymbolList(MemoryBuffer *Buffer,
700798e59b8SWei Mi                                       sampleprof::ProfileSymbolList &PSL) {
701798e59b8SWei Mi   if (!Buffer)
702798e59b8SWei Mi     return;
703798e59b8SWei Mi 
704798e59b8SWei Mi   SmallVector<StringRef, 32> SymbolVec;
705798e59b8SWei Mi   StringRef Data = Buffer->getBuffer();
706798e59b8SWei Mi   Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
707798e59b8SWei Mi 
70891337e90SChris Bieneman   for (StringRef SymbolStr : SymbolVec)
70991337e90SChris Bieneman     PSL.add(SymbolStr.trim());
710798e59b8SWei Mi }
711798e59b8SWei Mi 
handleExtBinaryWriter(sampleprof::SampleProfileWriter & Writer,ProfileFormat OutputFormat,MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & WriterList,bool CompressAllSections,bool UseMD5,bool GenPartialProfile)712b523790aSWei Mi static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
713b523790aSWei Mi                                   ProfileFormat OutputFormat,
714b523790aSWei Mi                                   MemoryBuffer *Buffer,
715b523790aSWei Mi                                   sampleprof::ProfileSymbolList &WriterList,
716b49eac71SWei Mi                                   bool CompressAllSections, bool UseMD5,
71756926ae0SWei Mi                                   bool GenPartialProfile) {
718b523790aSWei Mi   populateProfileSymbolList(Buffer, WriterList);
719b523790aSWei Mi   if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
720b523790aSWei Mi     warn("Profile Symbol list is not empty but the output format is not "
721b523790aSWei Mi          "ExtBinary format. The list will be lost in the output. ");
722b523790aSWei Mi 
723b523790aSWei Mi   Writer.setProfileSymbolList(&WriterList);
724b523790aSWei Mi 
725b523790aSWei Mi   if (CompressAllSections) {
726ebad6788SWei Mi     if (OutputFormat != PF_Ext_Binary)
727b523790aSWei Mi       warn("-compress-all-section is ignored. Specify -extbinary to enable it");
728ebad6788SWei Mi     else
729ebad6788SWei Mi       Writer.setToCompressAllSections();
730b523790aSWei Mi   }
731ebad6788SWei Mi   if (UseMD5) {
732ebad6788SWei Mi     if (OutputFormat != PF_Ext_Binary)
733ebad6788SWei Mi       warn("-use-md5 is ignored. Specify -extbinary to enable it");
734ebad6788SWei Mi     else
735ebad6788SWei Mi       Writer.setUseMD5();
736b523790aSWei Mi   }
73756926ae0SWei Mi   if (GenPartialProfile) {
738b49eac71SWei Mi     if (OutputFormat != PF_Ext_Binary)
73956926ae0SWei Mi       warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
740b49eac71SWei Mi     else
741b49eac71SWei Mi       Writer.setPartialProfile();
742b49eac71SWei Mi   }
743b523790aSWei Mi }
744b523790aSWei Mi 
745ebad6788SWei Mi static void
mergeSampleProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,StringRef OutputFilename,ProfileFormat OutputFormat,StringRef ProfileSymbolListFile,bool CompressAllSections,bool UseMD5,bool GenPartialProfile,bool GenCSNestedProfile,bool SampleMergeColdContext,bool SampleTrimColdContext,bool SampleColdContextFrameDepth,FailureMode FailMode)746ebad6788SWei Mi mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
747ebad6788SWei Mi                    StringRef OutputFilename, ProfileFormat OutputFormat,
748ebad6788SWei Mi                    StringRef ProfileSymbolListFile, bool CompressAllSections,
7495740bb80SHongtao Yu                    bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile,
750dff83158SWenlei He                    bool SampleMergeColdContext, bool SampleTrimColdContext,
751863184ddSwlei                    bool SampleColdContextFrameDepth, FailureMode FailMode) {
752d5336ae2SDiego Novillo   using namespace sampleprof;
753b9db7036SHongtao Yu   SampleProfileMap ProfileMap;
754aae1ed8eSDiego Novillo   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
75503b42e41SMehdi Amini   LLVMContext Context;
756798e59b8SWei Mi   sampleprof::ProfileSymbolList WriterList;
757ac068e01SHongtao Yu   Optional<bool> ProfileIsProbeBased;
758e36786d1SHongtao Yu   Optional<bool> ProfileIsCS;
7597f5b47ddSNathan Slingerland   for (const auto &Input : Inputs) {
7608d581857SRong Xu     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context,
7618d581857SRong Xu                                                    FSDiscriminatorPassOption);
7620fcfe897SVedant Kumar     if (std::error_code EC = ReaderOrErr.getError()) {
7630fcfe897SVedant Kumar       warnOrExitGivenError(FailMode, EC, Input.Filename);
7640fcfe897SVedant Kumar       continue;
7650fcfe897SVedant Kumar     }
766d5336ae2SDiego Novillo 
767aae1ed8eSDiego Novillo     // We need to keep the readers around until after all the files are
768aae1ed8eSDiego Novillo     // read so that we do not lose the function names stored in each
769aae1ed8eSDiego Novillo     // reader's memory. The function names are needed to write out the
770aae1ed8eSDiego Novillo     // merged profile map.
771aae1ed8eSDiego Novillo     Readers.push_back(std::move(ReaderOrErr.get()));
772aae1ed8eSDiego Novillo     const auto Reader = Readers.back().get();
7730fcfe897SVedant Kumar     if (std::error_code EC = Reader->read()) {
7740fcfe897SVedant Kumar       warnOrExitGivenError(FailMode, EC, Input.Filename);
7750fcfe897SVedant Kumar       Readers.pop_back();
7760fcfe897SVedant Kumar       continue;
7770fcfe897SVedant Kumar     }
778d5336ae2SDiego Novillo 
779b9db7036SHongtao Yu     SampleProfileMap &Profiles = Reader->getProfiles();
780a7938c74SKazu Hirata     if (ProfileIsProbeBased &&
781ac068e01SHongtao Yu         ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
782ac068e01SHongtao Yu       exitWithError(
783ac068e01SHongtao Yu           "cannot merge probe-based profile with non-probe-based profile");
784ac068e01SHongtao Yu     ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
785a7938c74SKazu Hirata     if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)
786e68fafa4SHongtao Yu       exitWithError("cannot merge CS profile with non-CS profile");
787e36786d1SHongtao Yu     ProfileIsCS = FunctionSamples::ProfileIsCS;
788b9db7036SHongtao Yu     for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
789d5336ae2SDiego Novillo          I != E; ++I) {
7903164fcfdSRichard Smith       sampleprof_error Result = sampleprof_error::success;
7913164fcfdSRichard Smith       FunctionSamples Remapped =
7923164fcfdSRichard Smith           Remapper ? remapSamples(I->second, *Remapper, Result)
7933164fcfdSRichard Smith                    : FunctionSamples();
7943164fcfdSRichard Smith       FunctionSamples &Samples = Remapper ? Remapped : I->second;
795b9db7036SHongtao Yu       SampleContext FContext = Samples.getContext();
796b9db7036SHongtao Yu       MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight));
79748dd080cSNathan Slingerland       if (Result != sampleprof_error::success) {
79848dd080cSNathan Slingerland         std::error_code EC = make_error_code(Result);
799b9db7036SHongtao Yu         handleMergeWriterError(errorCodeToError(EC), Input.Filename,
800b9db7036SHongtao Yu                                FContext.toString());
80148dd080cSNathan Slingerland       }
802d5336ae2SDiego Novillo     }
803798e59b8SWei Mi 
804798e59b8SWei Mi     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
805798e59b8SWei Mi         Reader->getProfileSymbolList();
806798e59b8SWei Mi     if (ReaderList)
807798e59b8SWei Mi       WriterList.merge(*ReaderList);
808d5336ae2SDiego Novillo   }
809dff83158SWenlei He 
810e36786d1SHongtao Yu   if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
811dff83158SWenlei He     // Use threshold calculated from profile summary unless specified.
812dff83158SWenlei He     SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
813dff83158SWenlei He     auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
814dff83158SWenlei He     uint64_t SampleProfColdThreshold =
815dff83158SWenlei He         ProfileSummaryBuilder::getColdCountThreshold(
816dff83158SWenlei He             (Summary->getDetailedSummary()));
817dff83158SWenlei He 
818dff83158SWenlei He     // Trim and merge cold context profile using cold threshold above;
819dff83158SWenlei He     SampleContextTrimmer(ProfileMap)
820863184ddSwlei         .trimAndMergeColdContextProfiles(
821863184ddSwlei             SampleProfColdThreshold, SampleTrimColdContext,
822259e4c56SHongtao Yu             SampleMergeColdContext, SampleColdContextFrameDepth, false);
823dff83158SWenlei He   }
824dff83158SWenlei He 
825e36786d1SHongtao Yu   if (ProfileIsCS && GenCSNestedProfile) {
8265740bb80SHongtao Yu     CSProfileConverter CSConverter(ProfileMap);
8275740bb80SHongtao Yu     CSConverter.convertProfiles();
828e36786d1SHongtao Yu     ProfileIsCS = FunctionSamples::ProfileIsCS = false;
8295740bb80SHongtao Yu   }
8305740bb80SHongtao Yu 
831f0d3dcecSRong Xu   auto WriterOrErr =
832f0d3dcecSRong Xu       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
833f0d3dcecSRong Xu   if (std::error_code EC = WriterOrErr.getError())
834f0d3dcecSRong Xu     exitWithErrorCode(EC, OutputFilename);
835f0d3dcecSRong Xu 
836b523790aSWei Mi   auto Writer = std::move(WriterOrErr.get());
837798e59b8SWei Mi   // WriterList will have StringRef refering to string in Buffer.
838798e59b8SWei Mi   // Make sure Buffer lives as long as WriterList.
839798e59b8SWei Mi   auto Buffer = getInputFileBuf(ProfileSymbolListFile);
840b523790aSWei Mi   handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
84156926ae0SWei Mi                         CompressAllSections, UseMD5, GenPartialProfile);
84200ef28efSWenlei He   if (std::error_code EC = Writer->write(ProfileMap))
84300ef28efSWenlei He     exitWithErrorCode(std::move(EC));
844d5336ae2SDiego Novillo }
845d5336ae2SDiego Novillo 
parseWeightedFile(const StringRef & WeightedFilename)8467f5b47ddSNathan Slingerland static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
8478d0e861eSVedant Kumar   StringRef WeightStr, FileName;
8488d0e861eSVedant Kumar   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
849d5336ae2SDiego Novillo 
8507f5b47ddSNathan Slingerland   uint64_t Weight;
8517f5b47ddSNathan Slingerland   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
8528ea2a58aSFangrui Song     exitWithError("input weight must be a positive integer");
8537f5b47ddSNathan Slingerland 
854adcd0268SBenjamin Kramer   return {std::string(FileName), Weight};
8557f5b47ddSNathan Slingerland }
8567f5b47ddSNathan Slingerland 
addWeightedInput(WeightedFileVector & WNI,const WeightedFile & WF)8579a1bfcfaSXinliang David Li static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
8589a1bfcfaSXinliang David Li   StringRef Filename = WF.Filename;
8599a1bfcfaSXinliang David Li   uint64_t Weight = WF.Weight;
860a81f4728SBenjamin Kramer 
861a81f4728SBenjamin Kramer   // If it's STDIN just pass it on.
862a81f4728SBenjamin Kramer   if (Filename == "-") {
863adcd0268SBenjamin Kramer     WNI.push_back({std::string(Filename), Weight});
864a81f4728SBenjamin Kramer     return;
865a81f4728SBenjamin Kramer   }
866a81f4728SBenjamin Kramer 
8679a1bfcfaSXinliang David Li   llvm::sys::fs::file_status Status;
8689a1bfcfaSXinliang David Li   llvm::sys::fs::status(Filename, Status);
8699a1bfcfaSXinliang David Li   if (!llvm::sys::fs::exists(Status))
8709a1bfcfaSXinliang David Li     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
8719a1bfcfaSXinliang David Li                       Filename);
8729a1bfcfaSXinliang David Li   // If it's a source file, collect it.
8739a1bfcfaSXinliang David Li   if (llvm::sys::fs::is_regular_file(Status)) {
874adcd0268SBenjamin Kramer     WNI.push_back({std::string(Filename), Weight});
8759a1bfcfaSXinliang David Li     return;
8769a1bfcfaSXinliang David Li   }
8779a1bfcfaSXinliang David Li 
8789a1bfcfaSXinliang David Li   if (llvm::sys::fs::is_directory(Status)) {
8799a1bfcfaSXinliang David Li     std::error_code EC;
8809a1bfcfaSXinliang David Li     for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
8819a1bfcfaSXinliang David Li          F != E && !EC; F.increment(EC)) {
8829a1bfcfaSXinliang David Li       if (llvm::sys::fs::is_regular_file(F->path())) {
8839a1bfcfaSXinliang David Li         addWeightedInput(WNI, {F->path(), Weight});
8849a1bfcfaSXinliang David Li       }
8859a1bfcfaSXinliang David Li     }
8869a1bfcfaSXinliang David Li     if (EC)
8879a1bfcfaSXinliang David Li       exitWithErrorCode(EC, Filename);
8889a1bfcfaSXinliang David Li   }
8899a1bfcfaSXinliang David Li }
8909a1bfcfaSXinliang David Li 
parseInputFilenamesFile(MemoryBuffer * Buffer,WeightedFileVector & WFV)891cef4360aSVedant Kumar static void parseInputFilenamesFile(MemoryBuffer *Buffer,
892cef4360aSVedant Kumar                                     WeightedFileVector &WFV) {
893cef4360aSVedant Kumar   if (!Buffer)
894cef4360aSVedant Kumar     return;
895cef4360aSVedant Kumar 
896cef4360aSVedant Kumar   SmallVector<StringRef, 8> Entries;
897cef4360aSVedant Kumar   StringRef Data = Buffer->getBuffer();
898cef4360aSVedant Kumar   Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
899cef4360aSVedant Kumar   for (const StringRef &FileWeightEntry : Entries) {
900cef4360aSVedant Kumar     StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
901cef4360aSVedant Kumar     // Skip comments.
902cef4360aSVedant Kumar     if (SanitizedEntry.startswith("#"))
903cef4360aSVedant Kumar       continue;
904cef4360aSVedant Kumar     // If there's no comma, it's an unweighted profile.
9054e3eebc6SKazu Hirata     else if (!SanitizedEntry.contains(','))
906adcd0268SBenjamin Kramer       addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
907cef4360aSVedant Kumar     else
9089a1bfcfaSXinliang David Li       addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
909cef4360aSVedant Kumar   }
910cef4360aSVedant Kumar }
911cef4360aSVedant Kumar 
merge_main(int argc,const char * argv[])9127f5b47ddSNathan Slingerland static int merge_main(int argc, const char *argv[]) {
9137f5b47ddSNathan Slingerland   cl::list<std::string> InputFilenames(cl::Positional,
9147f5b47ddSNathan Slingerland                                        cl::desc("<filename...>"));
9157f5b47ddSNathan Slingerland   cl::list<std::string> WeightedInputFilenames("weighted-input",
9167f5b47ddSNathan Slingerland                                                cl::desc("<weight>,<filename>"));
917cef4360aSVedant Kumar   cl::opt<std::string> InputFilenamesFile(
918cef4360aSVedant Kumar       "input-files", cl::init(""),
919cef4360aSVedant Kumar       cl::desc("Path to file containing newline-separated "
920cef4360aSVedant Kumar                "[<weight>,]<filename> entries"));
921cef4360aSVedant Kumar   cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
922cef4360aSVedant Kumar                                 cl::aliasopt(InputFilenamesFile));
923cef4360aSVedant Kumar   cl::opt<bool> DumpInputFileList(
924cef4360aSVedant Kumar       "dump-input-file-list", cl::init(false), cl::Hidden,
925cef4360aSVedant Kumar       cl::desc("Dump the list of input files and their weights, then exit"));
9263164fcfdSRichard Smith   cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
9273164fcfdSRichard Smith                                      cl::desc("Symbol remapping file"));
9283164fcfdSRichard Smith   cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
9293164fcfdSRichard Smith                            cl::aliasopt(RemappingFile));
930d5336ae2SDiego Novillo   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
931ea23c38dSFangrui Song                                       cl::init("-"), cl::desc("Output file"));
932d5336ae2SDiego Novillo   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
933d5336ae2SDiego Novillo                             cl::aliasopt(OutputFilename));
934d5336ae2SDiego Novillo   cl::opt<ProfileKinds> ProfileKind(
935d5336ae2SDiego Novillo       cl::desc("Profile kind:"), cl::init(instr),
936d5336ae2SDiego Novillo       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
937732afdd0SMehdi Amini                  clEnumVal(sample, "Sample profile")));
9386f7c19a4SXinliang David Li   cl::opt<ProfileFormat> OutputFormat(
939d9be2c7eSWei Mi       cl::desc("Format of output profile"), cl::init(PF_Binary),
940be907324SWei Mi       cl::values(
941be907324SWei Mi           clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
942a0c0857eSWei Mi           clEnumValN(PF_Compact_Binary, "compbinary",
943432db3b4SWei Mi                      "Compact binary encoding"),
944be907324SWei Mi           clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"),
9456f7c19a4SXinliang David Li           clEnumValN(PF_Text, "text", "Text encoding"),
9466f7c19a4SXinliang David Li           clEnumValN(PF_GCC, "gcc",
947732afdd0SMehdi Amini                      "GCC encoding (only meaningful for -sample)")));
9480fcfe897SVedant Kumar   cl::opt<FailureMode> FailureMode(
9490fcfe897SVedant Kumar       "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"),
9500fcfe897SVedant Kumar       cl::values(clEnumValN(failIfAnyAreInvalid, "any",
9510fcfe897SVedant Kumar                             "Fail if any profile is invalid."),
9520fcfe897SVedant Kumar                  clEnumValN(failIfAllAreInvalid, "all",
9530fcfe897SVedant Kumar                             "Fail only if all profiles are invalid.")));
95400dab228SVedant Kumar   cl::opt<bool> OutputSparse("sparse", cl::init(false),
95500dab228SVedant Kumar       cl::desc("Generate a sparse profile (only meaningful for -instr)"));
956e3a0bf50SVedant Kumar   cl::opt<unsigned> NumThreads(
957e3a0bf50SVedant Kumar       "num-threads", cl::init(0),
958e3a0bf50SVedant Kumar       cl::desc("Number of merge threads to use (default: autodetect)"));
959e3a0bf50SVedant Kumar   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
960e3a0bf50SVedant Kumar                         cl::aliasopt(NumThreads));
961798e59b8SWei Mi   cl::opt<std::string> ProfileSymbolListFile(
962798e59b8SWei Mi       "prof-sym-list", cl::init(""),
963798e59b8SWei Mi       cl::desc("Path to file containing the list of function symbols "
964798e59b8SWei Mi                "used to populate profile symbol list"));
965b523790aSWei Mi   cl::opt<bool> CompressAllSections(
966b523790aSWei Mi       "compress-all-sections", cl::init(false), cl::Hidden,
967b523790aSWei Mi       cl::desc("Compress all sections when writing the profile (only "
968b523790aSWei Mi                "meaningful for -extbinary)"));
969ebad6788SWei Mi   cl::opt<bool> UseMD5(
970ebad6788SWei Mi       "use-md5", cl::init(false), cl::Hidden,
971ebad6788SWei Mi       cl::desc("Choose to use MD5 to represent string in name table (only "
972ebad6788SWei Mi                "meaningful for -extbinary)"));
973dff83158SWenlei He   cl::opt<bool> SampleMergeColdContext(
974dff83158SWenlei He       "sample-merge-cold-context", cl::init(false), cl::Hidden,
975dff83158SWenlei He       cl::desc(
976dff83158SWenlei He           "Merge context sample profiles whose count is below cold threshold"));
977dff83158SWenlei He   cl::opt<bool> SampleTrimColdContext(
978dff83158SWenlei He       "sample-trim-cold-context", cl::init(false), cl::Hidden,
979dff83158SWenlei He       cl::desc(
980dff83158SWenlei He           "Trim context sample profiles whose count is below cold threshold"));
981863184ddSwlei   cl::opt<uint32_t> SampleColdContextFrameDepth(
982557efc9aSFangrui Song       "sample-frame-depth-for-cold-context", cl::init(1),
983863184ddSwlei       cl::desc("Keep the last K frames while merging cold profile. 1 means the "
984863184ddSwlei                "context-less base profile"));
98556926ae0SWei Mi   cl::opt<bool> GenPartialProfile(
98656926ae0SWei Mi       "gen-partial-profile", cl::init(false), cl::Hidden,
98756926ae0SWei Mi       cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
988a23f6234SWei Mi   cl::opt<std::string> SupplInstrWithSample(
989a23f6234SWei Mi       "supplement-instr-with-sample", cl::init(""), cl::Hidden,
990a23f6234SWei Mi       cl::desc("Supplement an instr profile with sample profile, to correct "
991a23f6234SWei Mi                "the profile unrepresentativeness issue. The sample "
992a23f6234SWei Mi                "profile is the input of the flag. Output will be in instr "
993a23f6234SWei Mi                "format (The flag only works with -instr)"));
994a23f6234SWei Mi   cl::opt<float> ZeroCounterThreshold(
995a23f6234SWei Mi       "zero-counter-threshold", cl::init(0.7), cl::Hidden,
996a23f6234SWei Mi       cl::desc("For the function which is cold in instr profile but hot in "
997a23f6234SWei Mi                "sample profile, if the ratio of the number of zero counters "
99887a55137SBrian Tracy                "divided by the total number of counters is above the "
999a23f6234SWei Mi                "threshold, the profile of the function will be regarded as "
1000a23f6234SWei Mi                "being harmful for performance and will be dropped."));
1001a23f6234SWei Mi   cl::opt<unsigned> SupplMinSizeThreshold(
1002a23f6234SWei Mi       "suppl-min-size-threshold", cl::init(10), cl::Hidden,
1003a23f6234SWei Mi       cl::desc("If the size of a function is smaller than the threshold, "
1004a23f6234SWei Mi                "assume it can be inlined by PGO early inliner and it won't "
1005a23f6234SWei Mi                "be adjusted based on sample profile."));
1006a23f6234SWei Mi   cl::opt<unsigned> InstrProfColdThreshold(
1007a23f6234SWei Mi       "instr-prof-cold-threshold", cl::init(0), cl::Hidden,
1008a23f6234SWei Mi       cl::desc("User specified cold threshold for instr profile which will "
1009a23f6234SWei Mi                "override the cold threshold got from profile summary. "));
10105740bb80SHongtao Yu   cl::opt<bool> GenCSNestedProfile(
10115740bb80SHongtao Yu       "gen-cs-nested-profile", cl::Hidden, cl::init(false),
10125740bb80SHongtao Yu       cl::desc("Generate nested function profiles for CSSPGO"));
101365d7fd02SEllis Hoag   cl::opt<std::string> DebugInfoFilename(
10144ecf15b7SKyungwoo Lee       "debug-info", cl::init(""),
101565d7fd02SEllis Hoag       cl::desc("Use the provided debug info to correlate the raw profile."));
10160a418490SSnehasish Kumar   cl::opt<std::string> ProfiledBinary(
10170a418490SSnehasish Kumar       "profiled-binary", cl::init(""),
10180a418490SSnehasish Kumar       cl::desc("Path to binary from which the profile was collected."));
101900dab228SVedant Kumar 
1020d5336ae2SDiego Novillo   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
1021d5336ae2SDiego Novillo 
1022cef4360aSVedant Kumar   WeightedFileVector WeightedInputs;
1023cef4360aSVedant Kumar   for (StringRef Filename : InputFilenames)
1024adcd0268SBenjamin Kramer     addWeightedInput(WeightedInputs, {std::string(Filename), 1});
1025cef4360aSVedant Kumar   for (StringRef WeightedFilename : WeightedInputFilenames)
10269a1bfcfaSXinliang David Li     addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
1027cef4360aSVedant Kumar 
1028cef4360aSVedant Kumar   // Make sure that the file buffer stays alive for the duration of the
1029cef4360aSVedant Kumar   // weighted input vector's lifetime.
1030798e59b8SWei Mi   auto Buffer = getInputFileBuf(InputFilenamesFile);
1031cef4360aSVedant Kumar   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
1032cef4360aSVedant Kumar 
1033cef4360aSVedant Kumar   if (WeightedInputs.empty())
10348ea2a58aSFangrui Song     exitWithError("no input files specified. See " +
10350c30f89cSChandler Carruth                   sys::path::filename(argv[0]) + " -help");
10360c30f89cSChandler Carruth 
1037cef4360aSVedant Kumar   if (DumpInputFileList) {
1038cef4360aSVedant Kumar     for (auto &WF : WeightedInputs)
1039cef4360aSVedant Kumar       outs() << WF.Weight << "," << WF.Filename << "\n";
1040cef4360aSVedant Kumar     return 0;
1041cef4360aSVedant Kumar   }
1042f771a050SVedant Kumar 
10433164fcfdSRichard Smith   std::unique_ptr<SymbolRemapper> Remapper;
10443164fcfdSRichard Smith   if (!RemappingFile.empty())
10453164fcfdSRichard Smith     Remapper = SymbolRemapper::create(RemappingFile);
10463164fcfdSRichard Smith 
1047a23f6234SWei Mi   if (!SupplInstrWithSample.empty()) {
1048a23f6234SWei Mi     if (ProfileKind != instr)
1049a23f6234SWei Mi       exitWithError(
1050a23f6234SWei Mi           "-supplement-instr-with-sample can only work with -instr. ");
1051a23f6234SWei Mi 
1052a23f6234SWei Mi     supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename,
1053a23f6234SWei Mi                            OutputFormat, OutputSparse, SupplMinSizeThreshold,
1054a23f6234SWei Mi                            ZeroCounterThreshold, InstrProfColdThreshold);
1055a23f6234SWei Mi     return 0;
1056a23f6234SWei Mi   }
1057a23f6234SWei Mi 
1058d5336ae2SDiego Novillo   if (ProfileKind == instr)
105965d7fd02SEllis Hoag     mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(),
106065d7fd02SEllis Hoag                       OutputFilename, OutputFormat, OutputSparse, NumThreads,
10610a418490SSnehasish Kumar                       FailureMode, ProfiledBinary);
1062d5336ae2SDiego Novillo   else
10633164fcfdSRichard Smith     mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
1064b523790aSWei Mi                        OutputFormat, ProfileSymbolListFile, CompressAllSections,
10655740bb80SHongtao Yu                        UseMD5, GenPartialProfile, GenCSNestedProfile,
10665740bb80SHongtao Yu                        SampleMergeColdContext, SampleTrimColdContext,
10675740bb80SHongtao Yu                        SampleColdContextFrameDepth, FailureMode);
1068bfee8d49SJustin Bogner   return 0;
1069bfee8d49SJustin Bogner }
1070618bcea7SJustin Bogner 
1071998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and profile TestFilename.
overlapInstrProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,raw_fd_ostream & OS,bool IsCS)1072998b97f6SRong Xu static void overlapInstrProfile(const std::string &BaseFilename,
1073998b97f6SRong Xu                                 const std::string &TestFilename,
1074998b97f6SRong Xu                                 const OverlapFuncFilters &FuncFilter,
1075998b97f6SRong Xu                                 raw_fd_ostream &OS, bool IsCS) {
1076998b97f6SRong Xu   std::mutex ErrorLock;
1077998b97f6SRong Xu   SmallSet<instrprof_error, 4> WriterErrorCodes;
1078998b97f6SRong Xu   WriterContext Context(false, ErrorLock, WriterErrorCodes);
1079998b97f6SRong Xu   WeightedFile WeightedInput{BaseFilename, 1};
1080998b97f6SRong Xu   OverlapStats Overlap;
1081e0fa2689SRong Xu   Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
1082998b97f6SRong Xu   if (E)
10838ea2a58aSFangrui Song     exitWithError(std::move(E), "error in getting profile count sums");
1084998b97f6SRong Xu   if (Overlap.Base.CountSum < 1.0f) {
1085998b97f6SRong Xu     OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
1086998b97f6SRong Xu     exit(0);
1087998b97f6SRong Xu   }
1088998b97f6SRong Xu   if (Overlap.Test.CountSum < 1.0f) {
1089998b97f6SRong Xu     OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
1090998b97f6SRong Xu     exit(0);
1091998b97f6SRong Xu   }
10920a418490SSnehasish Kumar   loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);
1093998b97f6SRong Xu   overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
1094998b97f6SRong Xu                IsCS);
1095998b97f6SRong Xu   Overlap.dump(OS);
1096998b97f6SRong Xu }
1097998b97f6SRong Xu 
1098540489deSweihe namespace {
1099540489deSweihe struct SampleOverlapStats {
1100b9db7036SHongtao Yu   SampleContext BaseName;
1101b9db7036SHongtao Yu   SampleContext TestName;
1102540489deSweihe   // Number of overlap units
1103540489deSweihe   uint64_t OverlapCount;
1104540489deSweihe   // Total samples of overlap units
1105540489deSweihe   uint64_t OverlapSample;
1106540489deSweihe   // Number of and total samples of units that only present in base or test
1107540489deSweihe   // profile
1108540489deSweihe   uint64_t BaseUniqueCount;
1109540489deSweihe   uint64_t BaseUniqueSample;
1110540489deSweihe   uint64_t TestUniqueCount;
1111540489deSweihe   uint64_t TestUniqueSample;
1112540489deSweihe   // Number of units and total samples in base or test profile
1113540489deSweihe   uint64_t BaseCount;
1114540489deSweihe   uint64_t BaseSample;
1115540489deSweihe   uint64_t TestCount;
1116540489deSweihe   uint64_t TestSample;
1117540489deSweihe   // Number of and total samples of units that present in at least one profile
1118540489deSweihe   uint64_t UnionCount;
1119540489deSweihe   uint64_t UnionSample;
1120540489deSweihe   // Weighted similarity
1121540489deSweihe   double Similarity;
1122540489deSweihe   // For SampleOverlapStats instances representing functions, weights of the
1123540489deSweihe   // function in base and test profiles
1124540489deSweihe   double BaseWeight;
1125540489deSweihe   double TestWeight;
1126540489deSweihe 
SampleOverlapStats__anon73ab2c7b0a11::SampleOverlapStats1127540489deSweihe   SampleOverlapStats()
1128540489deSweihe       : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0),
1129540489deSweihe         BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0),
1130540489deSweihe         BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0),
1131540489deSweihe         UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {}
1132540489deSweihe };
1133540489deSweihe } // end anonymous namespace
1134540489deSweihe 
1135540489deSweihe namespace {
1136540489deSweihe struct FuncSampleStats {
1137540489deSweihe   uint64_t SampleSum;
1138540489deSweihe   uint64_t MaxSample;
1139540489deSweihe   uint64_t HotBlockCount;
FuncSampleStats__anon73ab2c7b0b11::FuncSampleStats1140540489deSweihe   FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {}
FuncSampleStats__anon73ab2c7b0b11::FuncSampleStats1141540489deSweihe   FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
1142540489deSweihe                   uint64_t HotBlockCount)
1143540489deSweihe       : SampleSum(SampleSum), MaxSample(MaxSample),
1144540489deSweihe         HotBlockCount(HotBlockCount) {}
1145540489deSweihe };
1146540489deSweihe } // end anonymous namespace
1147540489deSweihe 
1148540489deSweihe namespace {
1149540489deSweihe enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1150540489deSweihe 
1151540489deSweihe // Class for updating merging steps for two sorted maps. The class should be
1152540489deSweihe // instantiated with a map iterator type.
1153540489deSweihe template <class T> class MatchStep {
1154540489deSweihe public:
1155540489deSweihe   MatchStep() = delete;
1156540489deSweihe 
MatchStep(T FirstIter,T FirstEnd,T SecondIter,T SecondEnd)1157540489deSweihe   MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1158540489deSweihe       : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1159540489deSweihe         SecondEnd(SecondEnd), Status(MS_None) {}
1160540489deSweihe 
areBothFinished() const1161540489deSweihe   bool areBothFinished() const {
1162540489deSweihe     return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1163540489deSweihe   }
1164540489deSweihe 
isFirstFinished() const1165540489deSweihe   bool isFirstFinished() const { return FirstIter == FirstEnd; }
1166540489deSweihe 
isSecondFinished() const1167540489deSweihe   bool isSecondFinished() const { return SecondIter == SecondEnd; }
1168540489deSweihe 
1169540489deSweihe   /// Advance one step based on the previous match status unless the previous
1170540489deSweihe   /// status is MS_None. Then update Status based on the comparison between two
1171540489deSweihe   /// container iterators at the current step. If the previous status is
1172540489deSweihe   /// MS_None, it means two iterators are at the beginning and no comparison has
1173540489deSweihe   /// been made, so we simply update Status without advancing the iterators.
1174540489deSweihe   void updateOneStep();
1175540489deSweihe 
getFirstIter() const1176540489deSweihe   T getFirstIter() const { return FirstIter; }
1177540489deSweihe 
getSecondIter() const1178540489deSweihe   T getSecondIter() const { return SecondIter; }
1179540489deSweihe 
getMatchStatus() const1180540489deSweihe   MatchStatus getMatchStatus() const { return Status; }
1181540489deSweihe 
1182540489deSweihe private:
1183540489deSweihe   // Current iterator and end iterator of the first container.
1184540489deSweihe   T FirstIter;
1185540489deSweihe   T FirstEnd;
1186540489deSweihe   // Current iterator and end iterator of the second container.
1187540489deSweihe   T SecondIter;
1188540489deSweihe   T SecondEnd;
1189540489deSweihe   // Match status of the current step.
1190540489deSweihe   MatchStatus Status;
1191540489deSweihe };
1192540489deSweihe } // end anonymous namespace
1193540489deSweihe 
updateOneStep()1194540489deSweihe template <class T> void MatchStep<T>::updateOneStep() {
1195540489deSweihe   switch (Status) {
1196540489deSweihe   case MS_Match:
1197540489deSweihe     ++FirstIter;
1198540489deSweihe     ++SecondIter;
1199540489deSweihe     break;
1200540489deSweihe   case MS_FirstUnique:
1201540489deSweihe     ++FirstIter;
1202540489deSweihe     break;
1203540489deSweihe   case MS_SecondUnique:
1204540489deSweihe     ++SecondIter;
1205540489deSweihe     break;
1206540489deSweihe   case MS_None:
1207540489deSweihe     break;
1208540489deSweihe   }
1209540489deSweihe 
1210540489deSweihe   // Update Status according to iterators at the current step.
1211540489deSweihe   if (areBothFinished())
1212540489deSweihe     return;
1213540489deSweihe   if (FirstIter != FirstEnd &&
1214540489deSweihe       (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1215540489deSweihe     Status = MS_FirstUnique;
1216540489deSweihe   else if (SecondIter != SecondEnd &&
1217540489deSweihe            (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1218540489deSweihe     Status = MS_SecondUnique;
1219540489deSweihe   else
1220540489deSweihe     Status = MS_Match;
1221540489deSweihe }
1222540489deSweihe 
1223540489deSweihe // Return the sum of line/block samples, the max line/block sample, and the
1224540489deSweihe // number of line/block samples above the given threshold in a function
1225540489deSweihe // including its inlinees.
getFuncSampleStats(const sampleprof::FunctionSamples & Func,FuncSampleStats & FuncStats,uint64_t HotThreshold)1226540489deSweihe static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1227540489deSweihe                                FuncSampleStats &FuncStats,
1228540489deSweihe                                uint64_t HotThreshold) {
1229540489deSweihe   for (const auto &L : Func.getBodySamples()) {
1230540489deSweihe     uint64_t Sample = L.second.getSamples();
1231540489deSweihe     FuncStats.SampleSum += Sample;
1232540489deSweihe     FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1233540489deSweihe     if (Sample >= HotThreshold)
1234540489deSweihe       ++FuncStats.HotBlockCount;
1235540489deSweihe   }
1236540489deSweihe 
1237540489deSweihe   for (const auto &C : Func.getCallsiteSamples()) {
1238540489deSweihe     for (const auto &F : C.second)
1239540489deSweihe       getFuncSampleStats(F.second, FuncStats, HotThreshold);
1240540489deSweihe   }
1241540489deSweihe }
1242540489deSweihe 
1243540489deSweihe /// Predicate that determines if a function is hot with a given threshold. We
1244540489deSweihe /// keep it separate from its callsites for possible extension in the future.
isFunctionHot(const FuncSampleStats & FuncStats,uint64_t HotThreshold)1245540489deSweihe static bool isFunctionHot(const FuncSampleStats &FuncStats,
1246540489deSweihe                           uint64_t HotThreshold) {
1247540489deSweihe   // We intentionally compare the maximum sample count in a function with the
1248540489deSweihe   // HotThreshold to get an approximate determination on hot functions.
1249540489deSweihe   return (FuncStats.MaxSample >= HotThreshold);
1250540489deSweihe }
1251540489deSweihe 
1252540489deSweihe namespace {
1253540489deSweihe class SampleOverlapAggregator {
1254540489deSweihe public:
SampleOverlapAggregator(const std::string & BaseFilename,const std::string & TestFilename,double LowSimilarityThreshold,double Epsilon,const OverlapFuncFilters & FuncFilter)1255540489deSweihe   SampleOverlapAggregator(const std::string &BaseFilename,
1256540489deSweihe                           const std::string &TestFilename,
1257540489deSweihe                           double LowSimilarityThreshold, double Epsilon,
1258540489deSweihe                           const OverlapFuncFilters &FuncFilter)
1259540489deSweihe       : BaseFilename(BaseFilename), TestFilename(TestFilename),
1260540489deSweihe         LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1261540489deSweihe         FuncFilter(FuncFilter) {}
1262540489deSweihe 
1263540489deSweihe   /// Detect 0-sample input profile and report to output stream. This interface
1264540489deSweihe   /// should be called after loadProfiles().
1265540489deSweihe   bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1266540489deSweihe 
1267540489deSweihe   /// Write out function-level similarity statistics for functions specified by
1268540489deSweihe   /// options --function, --value-cutoff, and --similarity-cutoff.
1269540489deSweihe   void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1270540489deSweihe 
1271540489deSweihe   /// Write out program-level similarity and overlap statistics.
1272540489deSweihe   void dumpProgramSummary(raw_fd_ostream &OS) const;
1273540489deSweihe 
1274540489deSweihe   /// Write out hot-function and hot-block statistics for base_profile,
1275540489deSweihe   /// test_profile, and their overlap. For both cases, the overlap HO is
1276540489deSweihe   /// calculated as follows:
1277540489deSweihe   ///    Given the number of functions (or blocks) that are hot in both profiles
1278540489deSweihe   ///    HCommon and the number of functions (or blocks) that are hot in at
1279540489deSweihe   ///    least one profile HUnion, HO = HCommon / HUnion.
1280540489deSweihe   void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1281540489deSweihe 
1282540489deSweihe   /// This function tries matching functions in base and test profiles. For each
1283540489deSweihe   /// pair of matched functions, it aggregates the function-level
1284540489deSweihe   /// similarity into a profile-level similarity. It also dump function-level
1285540489deSweihe   /// similarity information of functions specified by --function,
1286540489deSweihe   /// --value-cutoff, and --similarity-cutoff options. The program-level
1287540489deSweihe   /// similarity PS is computed as follows:
1288540489deSweihe   ///     Given function-level similarity FS(A) for all function A, the
1289540489deSweihe   ///     weight of function A in base profile WB(A), and the weight of function
1290540489deSweihe   ///     A in test profile WT(A), compute PS(base_profile, test_profile) =
1291540489deSweihe   ///     sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1292540489deSweihe   ///     meaning no-overlap.
1293540489deSweihe   void computeSampleProfileOverlap(raw_fd_ostream &OS);
1294540489deSweihe 
1295540489deSweihe   /// Initialize ProfOverlap with the sum of samples in base and test
1296540489deSweihe   /// profiles. This function also computes and keeps the sum of samples and
1297540489deSweihe   /// max sample counts of each function in BaseStats and TestStats for later
1298540489deSweihe   /// use to avoid re-computations.
1299540489deSweihe   void initializeSampleProfileOverlap();
1300540489deSweihe 
1301540489deSweihe   /// Load profiles specified by BaseFilename and TestFilename.
1302540489deSweihe   std::error_code loadProfiles();
1303540489deSweihe 
1304b9db7036SHongtao Yu   using FuncSampleStatsMap =
1305b9db7036SHongtao Yu       std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
1306b9db7036SHongtao Yu 
1307540489deSweihe private:
1308540489deSweihe   SampleOverlapStats ProfOverlap;
1309540489deSweihe   SampleOverlapStats HotFuncOverlap;
1310540489deSweihe   SampleOverlapStats HotBlockOverlap;
1311540489deSweihe   std::string BaseFilename;
1312540489deSweihe   std::string TestFilename;
1313540489deSweihe   std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1314540489deSweihe   std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1315540489deSweihe   // BaseStats and TestStats hold FuncSampleStats for each function, with
1316540489deSweihe   // function name as the key.
1317b9db7036SHongtao Yu   FuncSampleStatsMap BaseStats;
1318b9db7036SHongtao Yu   FuncSampleStatsMap TestStats;
1319540489deSweihe   // Low similarity threshold in floating point number
1320540489deSweihe   double LowSimilarityThreshold;
1321540489deSweihe   // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1322540489deSweihe   // for tracking hot blocks.
1323540489deSweihe   uint64_t BaseHotThreshold;
1324540489deSweihe   uint64_t TestHotThreshold;
1325540489deSweihe   // A small threshold used to round the results of floating point accumulations
1326540489deSweihe   // to resolve imprecision.
1327540489deSweihe   const double Epsilon;
1328540489deSweihe   std::multimap<double, SampleOverlapStats, std::greater<double>>
1329540489deSweihe       FuncSimilarityDump;
1330540489deSweihe   // FuncFilter carries specifications in options --value-cutoff and
1331540489deSweihe   // --function.
1332540489deSweihe   OverlapFuncFilters FuncFilter;
1333540489deSweihe   // Column offsets for printing the function-level details table.
1334540489deSweihe   static const unsigned int TestWeightCol = 15;
1335540489deSweihe   static const unsigned int SimilarityCol = 30;
1336540489deSweihe   static const unsigned int OverlapCol = 43;
1337540489deSweihe   static const unsigned int BaseUniqueCol = 53;
1338540489deSweihe   static const unsigned int TestUniqueCol = 67;
1339540489deSweihe   static const unsigned int BaseSampleCol = 81;
1340540489deSweihe   static const unsigned int TestSampleCol = 96;
1341540489deSweihe   static const unsigned int FuncNameCol = 111;
1342540489deSweihe 
1343540489deSweihe   /// Return a similarity of two line/block sample counters in the same
1344540489deSweihe   /// function in base and test profiles. The line/block-similarity BS(i) is
1345540489deSweihe   /// computed as follows:
1346540489deSweihe   ///    For an offsets i, given the sample count at i in base profile BB(i),
1347540489deSweihe   ///    the sample count at i in test profile BT(i), the sum of sample counts
1348540489deSweihe   ///    in this function in base profile SB, and the sum of sample counts in
1349540489deSweihe   ///    this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1350540489deSweihe   ///    BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1351540489deSweihe   double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1352540489deSweihe                                 const SampleOverlapStats &FuncOverlap) const;
1353540489deSweihe 
1354540489deSweihe   void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1355540489deSweihe                              uint64_t HotBlockCount);
1356540489deSweihe 
1357b9db7036SHongtao Yu   void getHotFunctions(const FuncSampleStatsMap &ProfStats,
1358b9db7036SHongtao Yu                        FuncSampleStatsMap &HotFunc,
1359540489deSweihe                        uint64_t HotThreshold) const;
1360540489deSweihe 
1361540489deSweihe   void computeHotFuncOverlap();
1362540489deSweihe 
1363540489deSweihe   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1364540489deSweihe   /// Difference for two sample units in a matched function according to the
1365540489deSweihe   /// given match status.
1366540489deSweihe   void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
1367540489deSweihe                                      uint64_t HotBlockCount,
1368540489deSweihe                                      SampleOverlapStats &FuncOverlap,
1369540489deSweihe                                      double &Difference, MatchStatus Status);
1370540489deSweihe 
1371540489deSweihe   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1372540489deSweihe   /// Difference for unmatched callees that only present in one profile in a
1373540489deSweihe   /// matched caller function.
1374540489deSweihe   void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
1375540489deSweihe                                 SampleOverlapStats &FuncOverlap,
1376540489deSweihe                                 double &Difference, MatchStatus Status);
1377540489deSweihe 
1378540489deSweihe   /// This function updates sample overlap statistics of an overlap function in
1379540489deSweihe   /// base and test profile. It also calculates a function-internal similarity
1380540489deSweihe   /// FIS as follows:
1381540489deSweihe   ///    For offsets i that have samples in at least one profile in this
1382540489deSweihe   ///    function A, given BS(i) returned by computeBlockSimilarity(), compute
1383540489deSweihe   ///    FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
1384540489deSweihe   ///    0.0 meaning no overlap.
1385540489deSweihe   double computeSampleFunctionInternalOverlap(
1386540489deSweihe       const sampleprof::FunctionSamples &BaseFunc,
1387540489deSweihe       const sampleprof::FunctionSamples &TestFunc,
1388540489deSweihe       SampleOverlapStats &FuncOverlap);
1389540489deSweihe 
1390540489deSweihe   /// Function-level similarity (FS) is a weighted value over function internal
1391540489deSweihe   /// similarity (FIS). This function computes a function's FS from its FIS by
1392540489deSweihe   /// applying the weight.
1393540489deSweihe   double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
1394540489deSweihe                                  uint64_t TestFuncSample) const;
1395540489deSweihe 
1396540489deSweihe   /// The function-level similarity FS(A) for a function A is computed as
1397540489deSweihe   /// follows:
1398540489deSweihe   ///     Compute a function-internal similarity FIS(A) by
1399540489deSweihe   ///     computeSampleFunctionInternalOverlap(). Then, with the weight of
1400540489deSweihe   ///     function A in base profile WB(A), and the weight of function A in test
1401540489deSweihe   ///     profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
1402540489deSweihe   ///     ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
1403540489deSweihe   double
1404540489deSweihe   computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
1405540489deSweihe                                const sampleprof::FunctionSamples *TestFunc,
1406540489deSweihe                                SampleOverlapStats *FuncOverlap,
1407540489deSweihe                                uint64_t BaseFuncSample,
1408540489deSweihe                                uint64_t TestFuncSample);
1409540489deSweihe 
1410540489deSweihe   /// Profile-level similarity (PS) is a weighted aggregate over function-level
1411540489deSweihe   /// similarities (FS). This method weights the FS value by the function
1412540489deSweihe   /// weights in the base and test profiles for the aggregation.
1413540489deSweihe   double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
1414540489deSweihe                             uint64_t TestFuncSample) const;
1415540489deSweihe };
1416540489deSweihe } // end anonymous namespace
1417540489deSweihe 
detectZeroSampleProfile(raw_fd_ostream & OS) const1418540489deSweihe bool SampleOverlapAggregator::detectZeroSampleProfile(
1419540489deSweihe     raw_fd_ostream &OS) const {
1420540489deSweihe   bool HaveZeroSample = false;
1421540489deSweihe   if (ProfOverlap.BaseSample == 0) {
1422540489deSweihe     OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
1423540489deSweihe     HaveZeroSample = true;
1424540489deSweihe   }
1425540489deSweihe   if (ProfOverlap.TestSample == 0) {
1426540489deSweihe     OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
1427540489deSweihe     HaveZeroSample = true;
1428540489deSweihe   }
1429540489deSweihe   return HaveZeroSample;
1430540489deSweihe }
1431540489deSweihe 
computeBlockSimilarity(uint64_t BaseSample,uint64_t TestSample,const SampleOverlapStats & FuncOverlap) const1432540489deSweihe double SampleOverlapAggregator::computeBlockSimilarity(
1433540489deSweihe     uint64_t BaseSample, uint64_t TestSample,
1434540489deSweihe     const SampleOverlapStats &FuncOverlap) const {
1435540489deSweihe   double BaseFrac = 0.0;
1436540489deSweihe   double TestFrac = 0.0;
1437540489deSweihe   if (FuncOverlap.BaseSample > 0)
1438540489deSweihe     BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
1439540489deSweihe   if (FuncOverlap.TestSample > 0)
1440540489deSweihe     TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
1441540489deSweihe   return 1.0 - std::fabs(BaseFrac - TestFrac);
1442540489deSweihe }
1443540489deSweihe 
updateHotBlockOverlap(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount)1444540489deSweihe void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
1445540489deSweihe                                                     uint64_t TestSample,
1446540489deSweihe                                                     uint64_t HotBlockCount) {
1447540489deSweihe   bool IsBaseHot = (BaseSample >= BaseHotThreshold);
1448540489deSweihe   bool IsTestHot = (TestSample >= TestHotThreshold);
1449540489deSweihe   if (!IsBaseHot && !IsTestHot)
1450540489deSweihe     return;
1451540489deSweihe 
1452540489deSweihe   HotBlockOverlap.UnionCount += HotBlockCount;
1453540489deSweihe   if (IsBaseHot)
1454540489deSweihe     HotBlockOverlap.BaseCount += HotBlockCount;
1455540489deSweihe   if (IsTestHot)
1456540489deSweihe     HotBlockOverlap.TestCount += HotBlockCount;
1457540489deSweihe   if (IsBaseHot && IsTestHot)
1458540489deSweihe     HotBlockOverlap.OverlapCount += HotBlockCount;
1459540489deSweihe }
1460540489deSweihe 
getHotFunctions(const FuncSampleStatsMap & ProfStats,FuncSampleStatsMap & HotFunc,uint64_t HotThreshold) const1461540489deSweihe void SampleOverlapAggregator::getHotFunctions(
1462b9db7036SHongtao Yu     const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
1463b9db7036SHongtao Yu     uint64_t HotThreshold) const {
1464540489deSweihe   for (const auto &F : ProfStats) {
1465540489deSweihe     if (isFunctionHot(F.second, HotThreshold))
1466b9db7036SHongtao Yu       HotFunc.emplace(F.first, F.second);
1467540489deSweihe   }
1468540489deSweihe }
1469540489deSweihe 
computeHotFuncOverlap()1470540489deSweihe void SampleOverlapAggregator::computeHotFuncOverlap() {
1471b9db7036SHongtao Yu   FuncSampleStatsMap BaseHotFunc;
1472540489deSweihe   getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
1473540489deSweihe   HotFuncOverlap.BaseCount = BaseHotFunc.size();
1474540489deSweihe 
1475b9db7036SHongtao Yu   FuncSampleStatsMap TestHotFunc;
1476540489deSweihe   getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
1477540489deSweihe   HotFuncOverlap.TestCount = TestHotFunc.size();
1478540489deSweihe   HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
1479540489deSweihe 
1480540489deSweihe   for (const auto &F : BaseHotFunc) {
1481b9db7036SHongtao Yu     if (TestHotFunc.count(F.first))
1482540489deSweihe       ++HotFuncOverlap.OverlapCount;
1483540489deSweihe     else
1484540489deSweihe       ++HotFuncOverlap.UnionCount;
1485540489deSweihe   }
1486540489deSweihe }
1487540489deSweihe 
updateOverlapStatsForFunction(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)1488540489deSweihe void SampleOverlapAggregator::updateOverlapStatsForFunction(
1489540489deSweihe     uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
1490540489deSweihe     SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
1491540489deSweihe   assert(Status != MS_None &&
1492540489deSweihe          "Match status should be updated before updating overlap statistics");
1493540489deSweihe   if (Status == MS_FirstUnique) {
1494540489deSweihe     TestSample = 0;
1495540489deSweihe     FuncOverlap.BaseUniqueSample += BaseSample;
1496540489deSweihe   } else if (Status == MS_SecondUnique) {
1497540489deSweihe     BaseSample = 0;
1498540489deSweihe     FuncOverlap.TestUniqueSample += TestSample;
1499540489deSweihe   } else {
1500540489deSweihe     ++FuncOverlap.OverlapCount;
1501540489deSweihe   }
1502540489deSweihe 
1503540489deSweihe   FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
1504540489deSweihe   FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
1505540489deSweihe   Difference +=
1506540489deSweihe       1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
1507540489deSweihe   updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
1508540489deSweihe }
1509540489deSweihe 
updateForUnmatchedCallee(const sampleprof::FunctionSamples & Func,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)1510540489deSweihe void SampleOverlapAggregator::updateForUnmatchedCallee(
1511540489deSweihe     const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
1512540489deSweihe     double &Difference, MatchStatus Status) {
1513540489deSweihe   assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
1514540489deSweihe          "Status must be either of the two unmatched cases");
1515540489deSweihe   FuncSampleStats FuncStats;
1516540489deSweihe   if (Status == MS_FirstUnique) {
1517540489deSweihe     getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
1518540489deSweihe     updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
1519540489deSweihe                                   FuncStats.HotBlockCount, FuncOverlap,
1520540489deSweihe                                   Difference, Status);
1521540489deSweihe   } else {
1522540489deSweihe     getFuncSampleStats(Func, FuncStats, TestHotThreshold);
1523540489deSweihe     updateOverlapStatsForFunction(0, FuncStats.SampleSum,
1524540489deSweihe                                   FuncStats.HotBlockCount, FuncOverlap,
1525540489deSweihe                                   Difference, Status);
1526540489deSweihe   }
1527540489deSweihe }
1528540489deSweihe 
computeSampleFunctionInternalOverlap(const sampleprof::FunctionSamples & BaseFunc,const sampleprof::FunctionSamples & TestFunc,SampleOverlapStats & FuncOverlap)1529540489deSweihe double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
1530540489deSweihe     const sampleprof::FunctionSamples &BaseFunc,
1531540489deSweihe     const sampleprof::FunctionSamples &TestFunc,
1532540489deSweihe     SampleOverlapStats &FuncOverlap) {
1533540489deSweihe 
1534540489deSweihe   using namespace sampleprof;
1535540489deSweihe 
1536540489deSweihe   double Difference = 0;
1537540489deSweihe 
1538540489deSweihe   // Accumulate Difference for regular line/block samples in the function.
1539540489deSweihe   // We match them through sort-merge join algorithm because
1540540489deSweihe   // FunctionSamples::getBodySamples() returns a map of sample counters ordered
1541540489deSweihe   // by their offsets.
1542540489deSweihe   MatchStep<BodySampleMap::const_iterator> BlockIterStep(
1543540489deSweihe       BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
1544540489deSweihe       TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
1545540489deSweihe   BlockIterStep.updateOneStep();
1546540489deSweihe   while (!BlockIterStep.areBothFinished()) {
1547540489deSweihe     uint64_t BaseSample =
1548540489deSweihe         BlockIterStep.isFirstFinished()
1549540489deSweihe             ? 0
1550540489deSweihe             : BlockIterStep.getFirstIter()->second.getSamples();
1551540489deSweihe     uint64_t TestSample =
1552540489deSweihe         BlockIterStep.isSecondFinished()
1553540489deSweihe             ? 0
1554540489deSweihe             : BlockIterStep.getSecondIter()->second.getSamples();
1555540489deSweihe     updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
1556540489deSweihe                                   Difference, BlockIterStep.getMatchStatus());
1557540489deSweihe 
1558540489deSweihe     BlockIterStep.updateOneStep();
1559540489deSweihe   }
1560540489deSweihe 
1561540489deSweihe   // Accumulate Difference for callsite lines in the function. We match
1562540489deSweihe   // them through sort-merge algorithm because
1563540489deSweihe   // FunctionSamples::getCallsiteSamples() returns a map of callsite records
1564540489deSweihe   // ordered by their offsets.
1565540489deSweihe   MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
1566540489deSweihe       BaseFunc.getCallsiteSamples().cbegin(),
1567540489deSweihe       BaseFunc.getCallsiteSamples().cend(),
1568540489deSweihe       TestFunc.getCallsiteSamples().cbegin(),
1569540489deSweihe       TestFunc.getCallsiteSamples().cend());
1570540489deSweihe   CallsiteIterStep.updateOneStep();
1571540489deSweihe   while (!CallsiteIterStep.areBothFinished()) {
1572540489deSweihe     MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
1573540489deSweihe     assert(CallsiteStepStatus != MS_None &&
1574540489deSweihe            "Match status should be updated before entering loop body");
1575540489deSweihe 
1576540489deSweihe     if (CallsiteStepStatus != MS_Match) {
1577540489deSweihe       auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
1578540489deSweihe                           ? CallsiteIterStep.getFirstIter()
1579540489deSweihe                           : CallsiteIterStep.getSecondIter();
1580540489deSweihe       for (const auto &F : Callsite->second)
1581540489deSweihe         updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
1582540489deSweihe                                  CallsiteStepStatus);
1583540489deSweihe     } else {
1584540489deSweihe       // There may be multiple inlinees at the same offset, so we need to try
1585540489deSweihe       // matching all of them. This match is implemented through sort-merge
1586540489deSweihe       // algorithm because callsite records at the same offset are ordered by
1587540489deSweihe       // function names.
1588540489deSweihe       MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
1589540489deSweihe           CallsiteIterStep.getFirstIter()->second.cbegin(),
1590540489deSweihe           CallsiteIterStep.getFirstIter()->second.cend(),
1591540489deSweihe           CallsiteIterStep.getSecondIter()->second.cbegin(),
1592540489deSweihe           CallsiteIterStep.getSecondIter()->second.cend());
1593540489deSweihe       CalleeIterStep.updateOneStep();
1594540489deSweihe       while (!CalleeIterStep.areBothFinished()) {
1595540489deSweihe         MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
1596540489deSweihe         if (CalleeStepStatus != MS_Match) {
1597540489deSweihe           auto Callee = (CalleeStepStatus == MS_FirstUnique)
1598540489deSweihe                             ? CalleeIterStep.getFirstIter()
1599540489deSweihe                             : CalleeIterStep.getSecondIter();
1600540489deSweihe           updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
1601540489deSweihe                                    CalleeStepStatus);
1602540489deSweihe         } else {
1603540489deSweihe           // An inlined function can contain other inlinees inside, so compute
1604540489deSweihe           // the Difference recursively.
1605540489deSweihe           Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
1606540489deSweihe                                       CalleeIterStep.getFirstIter()->second,
1607540489deSweihe                                       CalleeIterStep.getSecondIter()->second,
1608540489deSweihe                                       FuncOverlap);
1609540489deSweihe         }
1610540489deSweihe         CalleeIterStep.updateOneStep();
1611540489deSweihe       }
1612540489deSweihe     }
1613540489deSweihe     CallsiteIterStep.updateOneStep();
1614540489deSweihe   }
1615540489deSweihe 
1616540489deSweihe   // Difference reflects the total differences of line/block samples in this
1617540489deSweihe   // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
1618540489deSweihe   // reflect the similarity between function profiles in [0.0f to 1.0f].
1619540489deSweihe   return (2.0 - Difference) / 2;
1620540489deSweihe }
1621540489deSweihe 
weightForFuncSimilarity(double FuncInternalSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const1622540489deSweihe double SampleOverlapAggregator::weightForFuncSimilarity(
1623540489deSweihe     double FuncInternalSimilarity, uint64_t BaseFuncSample,
1624540489deSweihe     uint64_t TestFuncSample) const {
1625540489deSweihe   // Compute the weight as the distance between the function weights in two
1626540489deSweihe   // profiles.
1627540489deSweihe   double BaseFrac = 0.0;
1628540489deSweihe   double TestFrac = 0.0;
1629540489deSweihe   assert(ProfOverlap.BaseSample > 0 &&
1630540489deSweihe          "Total samples in base profile should be greater than 0");
1631540489deSweihe   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
1632540489deSweihe   assert(ProfOverlap.TestSample > 0 &&
1633540489deSweihe          "Total samples in test profile should be greater than 0");
1634540489deSweihe   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
1635540489deSweihe   double WeightDistance = std::fabs(BaseFrac - TestFrac);
1636540489deSweihe 
1637540489deSweihe   // Take WeightDistance into the similarity.
1638540489deSweihe   return FuncInternalSimilarity * (1 - WeightDistance);
1639540489deSweihe }
1640540489deSweihe 
1641540489deSweihe double
weightByImportance(double FuncSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const1642540489deSweihe SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
1643540489deSweihe                                             uint64_t BaseFuncSample,
1644540489deSweihe                                             uint64_t TestFuncSample) const {
1645540489deSweihe 
1646540489deSweihe   double BaseFrac = 0.0;
1647540489deSweihe   double TestFrac = 0.0;
1648540489deSweihe   assert(ProfOverlap.BaseSample > 0 &&
1649540489deSweihe          "Total samples in base profile should be greater than 0");
1650540489deSweihe   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
1651540489deSweihe   assert(ProfOverlap.TestSample > 0 &&
1652540489deSweihe          "Total samples in test profile should be greater than 0");
1653540489deSweihe   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
1654540489deSweihe   return FuncSimilarity * (BaseFrac + TestFrac);
1655540489deSweihe }
1656540489deSweihe 
computeSampleFunctionOverlap(const sampleprof::FunctionSamples * BaseFunc,const sampleprof::FunctionSamples * TestFunc,SampleOverlapStats * FuncOverlap,uint64_t BaseFuncSample,uint64_t TestFuncSample)1657540489deSweihe double SampleOverlapAggregator::computeSampleFunctionOverlap(
1658540489deSweihe     const sampleprof::FunctionSamples *BaseFunc,
1659540489deSweihe     const sampleprof::FunctionSamples *TestFunc,
1660540489deSweihe     SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
1661540489deSweihe     uint64_t TestFuncSample) {
1662540489deSweihe   // Default function internal similarity before weighted, meaning two functions
1663540489deSweihe   // has no overlap.
1664540489deSweihe   const double DefaultFuncInternalSimilarity = 0;
1665540489deSweihe   double FuncSimilarity;
1666540489deSweihe   double FuncInternalSimilarity;
1667540489deSweihe 
1668540489deSweihe   // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
1669540489deSweihe   // In this case, we use DefaultFuncInternalSimilarity as the function internal
1670540489deSweihe   // similarity.
1671540489deSweihe   if (!BaseFunc || !TestFunc) {
1672540489deSweihe     FuncInternalSimilarity = DefaultFuncInternalSimilarity;
1673540489deSweihe   } else {
1674540489deSweihe     assert(FuncOverlap != nullptr &&
1675540489deSweihe            "FuncOverlap should be provided in this case");
1676540489deSweihe     FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
1677540489deSweihe         *BaseFunc, *TestFunc, *FuncOverlap);
1678540489deSweihe     // Now, FuncInternalSimilarity may be a little less than 0 due to
1679540489deSweihe     // imprecision of floating point accumulations. Make it zero if the
1680540489deSweihe     // difference is below Epsilon.
1681540489deSweihe     FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
1682540489deSweihe                                  ? 0
1683540489deSweihe                                  : FuncInternalSimilarity;
1684540489deSweihe   }
1685540489deSweihe   FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
1686540489deSweihe                                            BaseFuncSample, TestFuncSample);
1687540489deSweihe   return FuncSimilarity;
1688540489deSweihe }
1689540489deSweihe 
computeSampleProfileOverlap(raw_fd_ostream & OS)1690540489deSweihe void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
1691540489deSweihe   using namespace sampleprof;
1692540489deSweihe 
1693b9db7036SHongtao Yu   std::unordered_map<SampleContext, const FunctionSamples *,
1694b9db7036SHongtao Yu                      SampleContext::Hash>
1695b9db7036SHongtao Yu       BaseFuncProf;
1696540489deSweihe   const auto &BaseProfiles = BaseReader->getProfiles();
1697540489deSweihe   for (const auto &BaseFunc : BaseProfiles) {
1698b9db7036SHongtao Yu     BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
1699540489deSweihe   }
1700540489deSweihe   ProfOverlap.UnionCount = BaseFuncProf.size();
1701540489deSweihe 
1702540489deSweihe   const auto &TestProfiles = TestReader->getProfiles();
1703540489deSweihe   for (const auto &TestFunc : TestProfiles) {
1704540489deSweihe     SampleOverlapStats FuncOverlap;
1705b9db7036SHongtao Yu     FuncOverlap.TestName = TestFunc.second.getContext();
1706540489deSweihe     assert(TestStats.count(FuncOverlap.TestName) &&
1707540489deSweihe            "TestStats should have records for all functions in test profile "
1708540489deSweihe            "except inlinees");
1709540489deSweihe     FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
1710540489deSweihe 
17114ef88031SWenlei He     bool Matched = false;
1712540489deSweihe     const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
1713540489deSweihe     if (Match == BaseFuncProf.end()) {
1714540489deSweihe       const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
1715540489deSweihe       ++ProfOverlap.TestUniqueCount;
1716540489deSweihe       ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
1717540489deSweihe       FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
1718540489deSweihe 
1719540489deSweihe       updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
1720540489deSweihe 
1721540489deSweihe       double FuncSimilarity = computeSampleFunctionOverlap(
1722540489deSweihe           nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
1723540489deSweihe       ProfOverlap.Similarity +=
1724540489deSweihe           weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
1725540489deSweihe 
1726540489deSweihe       ++ProfOverlap.UnionCount;
1727540489deSweihe       ProfOverlap.UnionSample += FuncStats.SampleSum;
1728540489deSweihe     } else {
1729540489deSweihe       ++ProfOverlap.OverlapCount;
1730540489deSweihe 
1731540489deSweihe       // Two functions match with each other. Compute function-level overlap and
1732540489deSweihe       // aggregate them into profile-level overlap.
1733b9db7036SHongtao Yu       FuncOverlap.BaseName = Match->second->getContext();
1734540489deSweihe       assert(BaseStats.count(FuncOverlap.BaseName) &&
1735540489deSweihe              "BaseStats should have records for all functions in base profile "
1736540489deSweihe              "except inlinees");
1737540489deSweihe       FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
1738540489deSweihe 
1739540489deSweihe       FuncOverlap.Similarity = computeSampleFunctionOverlap(
1740540489deSweihe           Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
1741540489deSweihe           FuncOverlap.TestSample);
1742540489deSweihe       ProfOverlap.Similarity +=
1743540489deSweihe           weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
1744540489deSweihe                              FuncOverlap.TestSample);
1745540489deSweihe       ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
1746540489deSweihe       ProfOverlap.UnionSample += FuncOverlap.UnionSample;
1747540489deSweihe 
1748540489deSweihe       // Accumulate the percentage of base unique and test unique samples into
1749540489deSweihe       // ProfOverlap.
1750540489deSweihe       ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
1751540489deSweihe       ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
1752540489deSweihe 
1753540489deSweihe       // Remove matched base functions for later reporting functions not found
1754540489deSweihe       // in test profile.
1755540489deSweihe       BaseFuncProf.erase(Match);
17564ef88031SWenlei He       Matched = true;
1757540489deSweihe     }
1758540489deSweihe 
1759540489deSweihe     // Print function-level similarity information if specified by options.
1760540489deSweihe     assert(TestStats.count(FuncOverlap.TestName) &&
1761540489deSweihe            "TestStats should have records for all functions in test profile "
1762540489deSweihe            "except inlinees");
1763540489deSweihe     if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
17644ef88031SWenlei He         (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
17654ef88031SWenlei He         (Matched && !FuncFilter.NameFilter.empty() &&
1766b9db7036SHongtao Yu          FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
1767b9db7036SHongtao Yu              std::string::npos)) {
1768540489deSweihe       assert(ProfOverlap.BaseSample > 0 &&
1769540489deSweihe              "Total samples in base profile should be greater than 0");
1770540489deSweihe       FuncOverlap.BaseWeight =
1771540489deSweihe           static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
1772540489deSweihe       assert(ProfOverlap.TestSample > 0 &&
1773540489deSweihe              "Total samples in test profile should be greater than 0");
1774540489deSweihe       FuncOverlap.TestWeight =
1775540489deSweihe           static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
1776540489deSweihe       FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
1777540489deSweihe     }
1778540489deSweihe   }
1779540489deSweihe 
1780540489deSweihe   // Traverse through functions in base profile but not in test profile.
1781540489deSweihe   for (const auto &F : BaseFuncProf) {
1782b9db7036SHongtao Yu     assert(BaseStats.count(F.second->getContext()) &&
1783540489deSweihe            "BaseStats should have records for all functions in base profile "
1784540489deSweihe            "except inlinees");
1785b9db7036SHongtao Yu     const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
1786540489deSweihe     ++ProfOverlap.BaseUniqueCount;
1787540489deSweihe     ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
1788540489deSweihe 
1789540489deSweihe     updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
1790540489deSweihe 
1791540489deSweihe     double FuncSimilarity = computeSampleFunctionOverlap(
1792540489deSweihe         nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
1793540489deSweihe     ProfOverlap.Similarity +=
1794540489deSweihe         weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
1795540489deSweihe 
1796540489deSweihe     ProfOverlap.UnionSample += FuncStats.SampleSum;
1797540489deSweihe   }
1798540489deSweihe 
1799540489deSweihe   // Now, ProfSimilarity may be a little greater than 1 due to imprecision
1800540489deSweihe   // of floating point accumulations. Make it 1.0 if the difference is below
1801540489deSweihe   // Epsilon.
1802540489deSweihe   ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
1803540489deSweihe                                ? 1
1804540489deSweihe                                : ProfOverlap.Similarity;
1805540489deSweihe 
1806540489deSweihe   computeHotFuncOverlap();
1807540489deSweihe }
1808540489deSweihe 
initializeSampleProfileOverlap()1809540489deSweihe void SampleOverlapAggregator::initializeSampleProfileOverlap() {
1810540489deSweihe   const auto &BaseProf = BaseReader->getProfiles();
1811540489deSweihe   for (const auto &I : BaseProf) {
1812540489deSweihe     ++ProfOverlap.BaseCount;
1813540489deSweihe     FuncSampleStats FuncStats;
1814540489deSweihe     getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
1815540489deSweihe     ProfOverlap.BaseSample += FuncStats.SampleSum;
1816b9db7036SHongtao Yu     BaseStats.emplace(I.second.getContext(), FuncStats);
1817540489deSweihe   }
1818540489deSweihe 
1819540489deSweihe   const auto &TestProf = TestReader->getProfiles();
1820540489deSweihe   for (const auto &I : TestProf) {
1821540489deSweihe     ++ProfOverlap.TestCount;
1822540489deSweihe     FuncSampleStats FuncStats;
1823540489deSweihe     getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
1824540489deSweihe     ProfOverlap.TestSample += FuncStats.SampleSum;
1825b9db7036SHongtao Yu     TestStats.emplace(I.second.getContext(), FuncStats);
1826540489deSweihe   }
1827540489deSweihe 
1828540489deSweihe   ProfOverlap.BaseName = StringRef(BaseFilename);
1829540489deSweihe   ProfOverlap.TestName = StringRef(TestFilename);
1830540489deSweihe }
1831540489deSweihe 
dumpFuncSimilarity(raw_fd_ostream & OS) const1832540489deSweihe void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
1833540489deSweihe   using namespace sampleprof;
1834540489deSweihe 
1835540489deSweihe   if (FuncSimilarityDump.empty())
1836540489deSweihe     return;
1837540489deSweihe 
1838540489deSweihe   formatted_raw_ostream FOS(OS);
1839540489deSweihe   FOS << "Function-level details:\n";
1840540489deSweihe   FOS << "Base weight";
1841540489deSweihe   FOS.PadToColumn(TestWeightCol);
1842540489deSweihe   FOS << "Test weight";
1843540489deSweihe   FOS.PadToColumn(SimilarityCol);
1844540489deSweihe   FOS << "Similarity";
1845540489deSweihe   FOS.PadToColumn(OverlapCol);
1846540489deSweihe   FOS << "Overlap";
1847540489deSweihe   FOS.PadToColumn(BaseUniqueCol);
1848540489deSweihe   FOS << "Base unique";
1849540489deSweihe   FOS.PadToColumn(TestUniqueCol);
1850540489deSweihe   FOS << "Test unique";
1851540489deSweihe   FOS.PadToColumn(BaseSampleCol);
1852540489deSweihe   FOS << "Base samples";
1853540489deSweihe   FOS.PadToColumn(TestSampleCol);
1854540489deSweihe   FOS << "Test samples";
1855540489deSweihe   FOS.PadToColumn(FuncNameCol);
1856540489deSweihe   FOS << "Function name\n";
1857540489deSweihe   for (const auto &F : FuncSimilarityDump) {
1858540489deSweihe     double OverlapPercent =
1859540489deSweihe         F.second.UnionSample > 0
1860540489deSweihe             ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
1861540489deSweihe             : 0;
1862540489deSweihe     double BaseUniquePercent =
1863540489deSweihe         F.second.BaseSample > 0
1864540489deSweihe             ? static_cast<double>(F.second.BaseUniqueSample) /
1865540489deSweihe                   F.second.BaseSample
1866540489deSweihe             : 0;
1867540489deSweihe     double TestUniquePercent =
1868540489deSweihe         F.second.TestSample > 0
1869540489deSweihe             ? static_cast<double>(F.second.TestUniqueSample) /
1870540489deSweihe                   F.second.TestSample
1871540489deSweihe             : 0;
1872540489deSweihe 
1873540489deSweihe     FOS << format("%.2f%%", F.second.BaseWeight * 100);
1874540489deSweihe     FOS.PadToColumn(TestWeightCol);
1875540489deSweihe     FOS << format("%.2f%%", F.second.TestWeight * 100);
1876540489deSweihe     FOS.PadToColumn(SimilarityCol);
1877540489deSweihe     FOS << format("%.2f%%", F.second.Similarity * 100);
1878540489deSweihe     FOS.PadToColumn(OverlapCol);
1879540489deSweihe     FOS << format("%.2f%%", OverlapPercent * 100);
1880540489deSweihe     FOS.PadToColumn(BaseUniqueCol);
1881540489deSweihe     FOS << format("%.2f%%", BaseUniquePercent * 100);
1882540489deSweihe     FOS.PadToColumn(TestUniqueCol);
1883540489deSweihe     FOS << format("%.2f%%", TestUniquePercent * 100);
1884540489deSweihe     FOS.PadToColumn(BaseSampleCol);
1885540489deSweihe     FOS << F.second.BaseSample;
1886540489deSweihe     FOS.PadToColumn(TestSampleCol);
1887540489deSweihe     FOS << F.second.TestSample;
1888540489deSweihe     FOS.PadToColumn(FuncNameCol);
1889b9db7036SHongtao Yu     FOS << F.second.TestName.toString() << "\n";
1890540489deSweihe   }
1891540489deSweihe }
1892540489deSweihe 
dumpProgramSummary(raw_fd_ostream & OS) const1893540489deSweihe void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
1894b9db7036SHongtao Yu   OS << "Profile overlap infomation for base_profile: "
1895b9db7036SHongtao Yu      << ProfOverlap.BaseName.toString()
1896b9db7036SHongtao Yu      << " and test_profile: " << ProfOverlap.TestName.toString()
1897b9db7036SHongtao Yu      << "\nProgram level:\n";
1898540489deSweihe 
1899540489deSweihe   OS << "  Whole program profile similarity: "
1900540489deSweihe      << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
1901540489deSweihe 
1902540489deSweihe   assert(ProfOverlap.UnionSample > 0 &&
1903540489deSweihe          "Total samples in two profile should be greater than 0");
1904540489deSweihe   double OverlapPercent =
1905540489deSweihe       static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
1906540489deSweihe   assert(ProfOverlap.BaseSample > 0 &&
1907540489deSweihe          "Total samples in base profile should be greater than 0");
1908540489deSweihe   double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
1909540489deSweihe                              ProfOverlap.BaseSample;
1910540489deSweihe   assert(ProfOverlap.TestSample > 0 &&
1911540489deSweihe          "Total samples in test profile should be greater than 0");
1912540489deSweihe   double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
1913540489deSweihe                              ProfOverlap.TestSample;
1914540489deSweihe 
1915540489deSweihe   OS << "  Whole program sample overlap: "
1916540489deSweihe      << format("%.3f%%", OverlapPercent * 100) << "\n";
1917540489deSweihe   OS << "    percentage of samples unique in base profile: "
1918540489deSweihe      << format("%.3f%%", BaseUniquePercent * 100) << "\n";
1919540489deSweihe   OS << "    percentage of samples unique in test profile: "
1920540489deSweihe      << format("%.3f%%", TestUniquePercent * 100) << "\n";
1921540489deSweihe   OS << "    total samples in base profile: " << ProfOverlap.BaseSample << "\n"
1922540489deSweihe      << "    total samples in test profile: " << ProfOverlap.TestSample << "\n";
1923540489deSweihe 
1924540489deSweihe   assert(ProfOverlap.UnionCount > 0 &&
1925540489deSweihe          "There should be at least one function in two input profiles");
1926540489deSweihe   double FuncOverlapPercent =
1927540489deSweihe       static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
1928540489deSweihe   OS << "  Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
1929540489deSweihe      << "\n";
1930540489deSweihe   OS << "    overlap functions: " << ProfOverlap.OverlapCount << "\n";
1931540489deSweihe   OS << "    functions unique in base profile: " << ProfOverlap.BaseUniqueCount
1932540489deSweihe      << "\n";
1933540489deSweihe   OS << "    functions unique in test profile: " << ProfOverlap.TestUniqueCount
1934540489deSweihe      << "\n";
1935540489deSweihe }
1936540489deSweihe 
dumpHotFuncAndBlockOverlap(raw_fd_ostream & OS) const1937540489deSweihe void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
1938540489deSweihe     raw_fd_ostream &OS) const {
1939540489deSweihe   assert(HotFuncOverlap.UnionCount > 0 &&
1940540489deSweihe          "There should be at least one hot function in two input profiles");
1941540489deSweihe   OS << "  Hot-function overlap: "
1942540489deSweihe      << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
1943540489deSweihe                              HotFuncOverlap.UnionCount * 100)
1944540489deSweihe      << "\n";
1945540489deSweihe   OS << "    overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
1946540489deSweihe   OS << "    hot functions unique in base profile: "
1947540489deSweihe      << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
1948540489deSweihe   OS << "    hot functions unique in test profile: "
1949540489deSweihe      << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
1950540489deSweihe 
1951540489deSweihe   assert(HotBlockOverlap.UnionCount > 0 &&
1952540489deSweihe          "There should be at least one hot block in two input profiles");
1953540489deSweihe   OS << "  Hot-block overlap: "
1954540489deSweihe      << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
1955540489deSweihe                              HotBlockOverlap.UnionCount * 100)
1956540489deSweihe      << "\n";
1957540489deSweihe   OS << "    overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
1958540489deSweihe   OS << "    hot blocks unique in base profile: "
1959540489deSweihe      << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
1960540489deSweihe   OS << "    hot blocks unique in test profile: "
1961540489deSweihe      << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
1962540489deSweihe }
1963540489deSweihe 
loadProfiles()1964540489deSweihe std::error_code SampleOverlapAggregator::loadProfiles() {
1965540489deSweihe   using namespace sampleprof;
1966540489deSweihe 
1967540489deSweihe   LLVMContext Context;
19688d581857SRong Xu   auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context,
19698d581857SRong Xu                                                      FSDiscriminatorPassOption);
1970540489deSweihe   if (std::error_code EC = BaseReaderOrErr.getError())
1971540489deSweihe     exitWithErrorCode(EC, BaseFilename);
1972540489deSweihe 
19738d581857SRong Xu   auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context,
19748d581857SRong Xu                                                      FSDiscriminatorPassOption);
1975540489deSweihe   if (std::error_code EC = TestReaderOrErr.getError())
1976540489deSweihe     exitWithErrorCode(EC, TestFilename);
1977540489deSweihe 
1978540489deSweihe   BaseReader = std::move(BaseReaderOrErr.get());
1979540489deSweihe   TestReader = std::move(TestReaderOrErr.get());
1980540489deSweihe 
1981540489deSweihe   if (std::error_code EC = BaseReader->read())
1982540489deSweihe     exitWithErrorCode(EC, BaseFilename);
1983540489deSweihe   if (std::error_code EC = TestReader->read())
1984540489deSweihe     exitWithErrorCode(EC, TestFilename);
1985ac068e01SHongtao Yu   if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
1986ac068e01SHongtao Yu     exitWithError(
1987ac068e01SHongtao Yu         "cannot compare probe-based profile with non-probe-based profile");
1988e36786d1SHongtao Yu   if (BaseReader->profileIsCS() != TestReader->profileIsCS())
1989e68fafa4SHongtao Yu     exitWithError("cannot compare CS profile with non-CS profile");
1990540489deSweihe 
1991540489deSweihe   // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
1992540489deSweihe   // profile summary.
1993540489deSweihe   ProfileSummary &BasePS = BaseReader->getSummary();
1994540489deSweihe   ProfileSummary &TestPS = TestReader->getSummary();
19959978e0e4SWenlei He   BaseHotThreshold =
19969978e0e4SWenlei He       ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
19979978e0e4SWenlei He   TestHotThreshold =
19989978e0e4SWenlei He       ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
19999978e0e4SWenlei He 
2000540489deSweihe   return std::error_code();
2001540489deSweihe }
2002540489deSweihe 
overlapSampleProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,uint64_t SimilarityCutoff,raw_fd_ostream & OS)2003540489deSweihe void overlapSampleProfile(const std::string &BaseFilename,
2004540489deSweihe                           const std::string &TestFilename,
2005540489deSweihe                           const OverlapFuncFilters &FuncFilter,
2006540489deSweihe                           uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
2007540489deSweihe   using namespace sampleprof;
2008540489deSweihe 
2009540489deSweihe   // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
2010540489deSweihe   // report 2--3 places after decimal point in percentage numbers.
2011540489deSweihe   SampleOverlapAggregator OverlapAggr(
2012540489deSweihe       BaseFilename, TestFilename,
2013540489deSweihe       static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
2014540489deSweihe   if (std::error_code EC = OverlapAggr.loadProfiles())
2015540489deSweihe     exitWithErrorCode(EC);
2016540489deSweihe 
2017540489deSweihe   OverlapAggr.initializeSampleProfileOverlap();
2018540489deSweihe   if (OverlapAggr.detectZeroSampleProfile(OS))
2019540489deSweihe     return;
2020540489deSweihe 
2021540489deSweihe   OverlapAggr.computeSampleProfileOverlap(OS);
2022540489deSweihe 
2023540489deSweihe   OverlapAggr.dumpProgramSummary(OS);
2024540489deSweihe   OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
2025540489deSweihe   OverlapAggr.dumpFuncSimilarity(OS);
2026540489deSweihe }
2027540489deSweihe 
overlap_main(int argc,const char * argv[])2028998b97f6SRong Xu static int overlap_main(int argc, const char *argv[]) {
2029998b97f6SRong Xu   cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
2030998b97f6SRong Xu                                     cl::desc("<base profile file>"));
2031998b97f6SRong Xu   cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
2032998b97f6SRong Xu                                     cl::desc("<test profile file>"));
2033998b97f6SRong Xu   cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"),
2034998b97f6SRong Xu                               cl::desc("Output file"));
2035998b97f6SRong Xu   cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output));
2036e68fafa4SHongtao Yu   cl::opt<bool> IsCS(
2037e68fafa4SHongtao Yu       "cs", cl::init(false),
2038e68fafa4SHongtao Yu       cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."));
2039998b97f6SRong Xu   cl::opt<unsigned long long> ValueCutoff(
2040998b97f6SRong Xu       "value-cutoff", cl::init(-1),
2041998b97f6SRong Xu       cl::desc(
2042e68fafa4SHongtao Yu           "Function level overlap information for every function (with calling "
2043e68fafa4SHongtao Yu           "context for csspgo) in test "
2044998b97f6SRong Xu           "profile with max count value greater then the parameter value"));
2045998b97f6SRong Xu   cl::opt<std::string> FuncNameFilter(
2046998b97f6SRong Xu       "function",
2047e68fafa4SHongtao Yu       cl::desc("Function level overlap information for matching functions. For "
2048e68fafa4SHongtao Yu                "CSSPGO this takes a a function name with calling context"));
2049540489deSweihe   cl::opt<unsigned long long> SimilarityCutoff(
2050540489deSweihe       "similarity-cutoff", cl::init(0),
2051e68fafa4SHongtao Yu       cl::desc("For sample profiles, list function names (with calling context "
2052e68fafa4SHongtao Yu                "for csspgo) for overlapped functions "
2053540489deSweihe                "with similarities below the cutoff (percentage times 10000)."));
2054540489deSweihe   cl::opt<ProfileKinds> ProfileKind(
2055540489deSweihe       cl::desc("Profile kind:"), cl::init(instr),
2056540489deSweihe       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
2057540489deSweihe                  clEnumVal(sample, "Sample profile")));
2058998b97f6SRong Xu   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n");
2059998b97f6SRong Xu 
2060998b97f6SRong Xu   std::error_code EC;
206182b3e28eSAbhina Sreeskantharajan   raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF);
2062998b97f6SRong Xu   if (EC)
2063998b97f6SRong Xu     exitWithErrorCode(EC, Output);
2064998b97f6SRong Xu 
2065540489deSweihe   if (ProfileKind == instr)
2066998b97f6SRong Xu     overlapInstrProfile(BaseFilename, TestFilename,
2067998b97f6SRong Xu                         OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS,
2068998b97f6SRong Xu                         IsCS);
2069540489deSweihe   else
2070540489deSweihe     overlapSampleProfile(BaseFilename, TestFilename,
2071540489deSweihe                          OverlapFuncFilters{ValueCutoff, FuncNameFilter},
2072540489deSweihe                          SimilarityCutoff, OS);
2073998b97f6SRong Xu 
2074998b97f6SRong Xu   return 0;
2075998b97f6SRong Xu }
2076998b97f6SRong Xu 
207759d90fe8SFangrui Song namespace {
207859d90fe8SFangrui Song struct ValueSitesStats {
ValueSitesStats__anon73ab2c7b0e11::ValueSitesStats20790cf1f56aSRong Xu   ValueSitesStats()
20800cf1f56aSRong Xu       : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
20810cf1f56aSRong Xu         TotalNumValues(0) {}
20820cf1f56aSRong Xu   uint64_t TotalNumValueSites;
20830cf1f56aSRong Xu   uint64_t TotalNumValueSitesWithValueProfile;
20840cf1f56aSRong Xu   uint64_t TotalNumValues;
20850cf1f56aSRong Xu   std::vector<unsigned> ValueSitesHistogram;
208659d90fe8SFangrui Song };
208759d90fe8SFangrui Song } // namespace
20880cf1f56aSRong Xu 
traverseAllValueSites(const InstrProfRecord & Func,uint32_t VK,ValueSitesStats & Stats,raw_fd_ostream & OS,InstrProfSymtab * Symtab)20890cf1f56aSRong Xu static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
20900cf1f56aSRong Xu                                   ValueSitesStats &Stats, raw_fd_ostream &OS,
209160faea19SRong Xu                                   InstrProfSymtab *Symtab) {
20920cf1f56aSRong Xu   uint32_t NS = Func.getNumValueSites(VK);
20930cf1f56aSRong Xu   Stats.TotalNumValueSites += NS;
20940cf1f56aSRong Xu   for (size_t I = 0; I < NS; ++I) {
20950cf1f56aSRong Xu     uint32_t NV = Func.getNumValueDataForSite(VK, I);
20960cf1f56aSRong Xu     std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
20970cf1f56aSRong Xu     Stats.TotalNumValues += NV;
20980cf1f56aSRong Xu     if (NV) {
20990cf1f56aSRong Xu       Stats.TotalNumValueSitesWithValueProfile++;
21000cf1f56aSRong Xu       if (NV > Stats.ValueSitesHistogram.size())
21010cf1f56aSRong Xu         Stats.ValueSitesHistogram.resize(NV, 0);
21020cf1f56aSRong Xu       Stats.ValueSitesHistogram[NV - 1]++;
21030cf1f56aSRong Xu     }
210452aa224aSRong Xu 
210552aa224aSRong Xu     uint64_t SiteSum = 0;
210652aa224aSRong Xu     for (uint32_t V = 0; V < NV; V++)
210752aa224aSRong Xu       SiteSum += VD[V].Count;
210852aa224aSRong Xu     if (SiteSum == 0)
210952aa224aSRong Xu       SiteSum = 1;
211052aa224aSRong Xu 
21110cf1f56aSRong Xu     for (uint32_t V = 0; V < NV; V++) {
211252aa224aSRong Xu       OS << "\t[ " << format("%2u", I) << ", ";
211360faea19SRong Xu       if (Symtab == nullptr)
211440a7f63cSPetar Jovanovic         OS << format("%4" PRIu64, VD[V].Value);
211560faea19SRong Xu       else
211660faea19SRong Xu         OS << Symtab->getFuncName(VD[V].Value);
211752aa224aSRong Xu       OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
211852aa224aSRong Xu          << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
21190cf1f56aSRong Xu     }
21200cf1f56aSRong Xu   }
21210cf1f56aSRong Xu }
21220cf1f56aSRong Xu 
showValueSitesStats(raw_fd_ostream & OS,uint32_t VK,ValueSitesStats & Stats)21230cf1f56aSRong Xu static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
21240cf1f56aSRong Xu                                 ValueSitesStats &Stats) {
21250cf1f56aSRong Xu   OS << "  Total number of sites: " << Stats.TotalNumValueSites << "\n";
21260cf1f56aSRong Xu   OS << "  Total number of sites with values: "
21270cf1f56aSRong Xu      << Stats.TotalNumValueSitesWithValueProfile << "\n";
21280cf1f56aSRong Xu   OS << "  Total number of profiled values: " << Stats.TotalNumValues << "\n";
21290cf1f56aSRong Xu 
21300cf1f56aSRong Xu   OS << "  Value sites histogram:\n\tNumTargets, SiteCount\n";
21310cf1f56aSRong Xu   for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
21320cf1f56aSRong Xu     if (Stats.ValueSitesHistogram[I] > 0)
21330cf1f56aSRong Xu       OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
21340cf1f56aSRong Xu   }
21350cf1f56aSRong Xu }
21360cf1f56aSRong Xu 
showInstrProfile(const std::string & Filename,bool ShowCounts,uint32_t TopN,bool ShowIndirectCallTargets,bool ShowMemOPSizes,bool ShowDetailedSummary,std::vector<uint32_t> DetailedSummaryCutoffs,bool ShowAllFunctions,bool ShowCS,uint64_t ValueCutoff,bool OnlyListBelow,const std::string & ShowFunction,bool TextFormat,bool ShowBinaryIds,bool ShowCovered,raw_fd_ostream & OS)21371afc1de4SBenjamin Kramer static int showInstrProfile(const std::string &Filename, bool ShowCounts,
2138801b5319SXinliang David Li                             uint32_t TopN, bool ShowIndirectCallTargets,
2139801b5319SXinliang David Li                             bool ShowMemOPSizes, bool ShowDetailedSummary,
2140183ebbe0SEaswaran Raman                             std::vector<uint32_t> DetailedSummaryCutoffs,
2141a6ff69f6SRong Xu                             bool ShowAllFunctions, bool ShowCS,
2142a6ff69f6SRong Xu                             uint64_t ValueCutoff, bool OnlyListBelow,
2143a6ff69f6SRong Xu                             const std::string &ShowFunction, bool TextFormat,
214411d30742SEllis Hoag                             bool ShowBinaryIds, bool ShowCovered,
214511d30742SEllis Hoag                             raw_fd_ostream &OS) {
2146fcd55607SDiego Novillo   auto ReaderOrErr = InstrProfReader::create(Filename);
21471afc1de4SBenjamin Kramer   std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
21481afc1de4SBenjamin Kramer   if (ShowDetailedSummary && Cutoffs.empty()) {
21490ca8ff4dSWenlei He     Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;
2150183ebbe0SEaswaran Raman   }
21511afc1de4SBenjamin Kramer   InstrProfSummaryBuilder Builder(std::move(Cutoffs));
21529152fd17SVedant Kumar   if (Error E = ReaderOrErr.takeError())
21539152fd17SVedant Kumar     exitWithError(std::move(E), Filename);
21549af28ef9SJustin Bogner 
2155fcd55607SDiego Novillo   auto Reader = std::move(ReaderOrErr.get());
215633c76c0cSRong Xu   bool IsIRInstr = Reader->isIRLevelProfile();
2157183ebbe0SEaswaran Raman   size_t ShownFunctions = 0;
215852aa224aSRong Xu   size_t BelowCutoffFunctions = 0;
21590cf1f56aSRong Xu   int NumVPKind = IPVK_Last - IPVK_First + 1;
21600cf1f56aSRong Xu   std::vector<ValueSitesStats> VPStats(NumVPKind);
2161801b5319SXinliang David Li 
2162801b5319SXinliang David Li   auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
2163801b5319SXinliang David Li                    const std::pair<std::string, uint64_t> &v2) {
2164801b5319SXinliang David Li     return v1.second > v2.second;
2165801b5319SXinliang David Li   };
2166801b5319SXinliang David Li 
2167801b5319SXinliang David Li   std::priority_queue<std::pair<std::string, uint64_t>,
2168801b5319SXinliang David Li                       std::vector<std::pair<std::string, uint64_t>>,
2169801b5319SXinliang David Li                       decltype(MinCmp)>
2170801b5319SXinliang David Li       HottestFuncs(MinCmp);
2171801b5319SXinliang David Li 
217252aa224aSRong Xu   if (!TextFormat && OnlyListBelow) {
217352aa224aSRong Xu     OS << "The list of functions with the maximum counter less than "
217452aa224aSRong Xu        << ValueCutoff << ":\n";
217552aa224aSRong Xu   }
217652aa224aSRong Xu 
2177c6ba9ca1SRichard Smith   // Add marker so that IR-level instrumentation round-trips properly.
2178c6ba9ca1SRichard Smith   if (TextFormat && IsIRInstr)
2179c6ba9ca1SRichard Smith     OS << ":ir\n";
2180c6ba9ca1SRichard Smith 
21819af28ef9SJustin Bogner   for (const auto &Func : *Reader) {
2182a6ff69f6SRong Xu     if (Reader->isIRLevelProfile()) {
2183a6ff69f6SRong Xu       bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
2184a6ff69f6SRong Xu       if (FuncIsCS != ShowCS)
2185a6ff69f6SRong Xu         continue;
2186a6ff69f6SRong Xu     }
21874e3eebc6SKazu Hirata     bool Show = ShowAllFunctions ||
21884e3eebc6SKazu Hirata                 (!ShowFunction.empty() && Func.Name.contains(ShowFunction));
21899af28ef9SJustin Bogner 
2190c6ba9ca1SRichard Smith     bool doTextFormatDump = (Show && TextFormat);
21916f7c19a4SXinliang David Li 
21926f7c19a4SXinliang David Li     if (doTextFormatDump) {
2193a716cc5cSXinliang David Li       InstrProfSymtab &Symtab = Reader->getSymtab();
2194cf9d52c6SDavid Blaikie       InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
2195cf9d52c6SDavid Blaikie                                          OS);
21966f7c19a4SXinliang David Li       continue;
21976f7c19a4SXinliang David Li     }
21986f7c19a4SXinliang David Li 
2199b59d7c73SJustin Bogner     assert(Func.Counts.size() > 0 && "function missing entry counter");
2200e5a17e3fSEaswaran Raman     Builder.addRecord(Func);
22016f7c19a4SXinliang David Li 
220211d30742SEllis Hoag     if (ShowCovered) {
2203*9e88cbccSKazu Hirata       if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))
220411d30742SEllis Hoag         OS << Func.Name << "\n";
220511d30742SEllis Hoag       continue;
220611d30742SEllis Hoag     }
220711d30742SEllis Hoag 
22087162e16eSRong Xu     uint64_t FuncMax = 0;
220952aa224aSRong Xu     uint64_t FuncSum = 0;
221052aa224aSRong Xu     for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
2211a23f6234SWei Mi       if (Func.Counts[I] == (uint64_t)-1)
2212a23f6234SWei Mi         continue;
22137162e16eSRong Xu       FuncMax = std::max(FuncMax, Func.Counts[I]);
221452aa224aSRong Xu       FuncSum += Func.Counts[I];
221552aa224aSRong Xu     }
22167162e16eSRong Xu 
221752aa224aSRong Xu     if (FuncMax < ValueCutoff) {
221852aa224aSRong Xu       ++BelowCutoffFunctions;
221952aa224aSRong Xu       if (OnlyListBelow) {
222052aa224aSRong Xu         OS << "  " << Func.Name << ": (Max = " << FuncMax
222152aa224aSRong Xu            << " Sum = " << FuncSum << ")\n";
222252aa224aSRong Xu       }
222352aa224aSRong Xu       continue;
222452aa224aSRong Xu     } else if (OnlyListBelow)
222552aa224aSRong Xu       continue;
222652aa224aSRong Xu 
222752aa224aSRong Xu     if (TopN) {
2228801b5319SXinliang David Li       if (HottestFuncs.size() == TopN) {
2229801b5319SXinliang David Li         if (HottestFuncs.top().second < FuncMax) {
2230801b5319SXinliang David Li           HottestFuncs.pop();
2231801b5319SXinliang David Li           HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
2232801b5319SXinliang David Li         }
2233801b5319SXinliang David Li       } else
2234801b5319SXinliang David Li         HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
2235801b5319SXinliang David Li     }
2236801b5319SXinliang David Li 
22379af28ef9SJustin Bogner     if (Show) {
22389af28ef9SJustin Bogner       if (!ShownFunctions)
22399af28ef9SJustin Bogner         OS << "Counters:\n";
22406f7c19a4SXinliang David Li 
22419af28ef9SJustin Bogner       ++ShownFunctions;
22429af28ef9SJustin Bogner 
22439af28ef9SJustin Bogner       OS << "  " << Func.Name << ":\n"
2244423380f9SJustin Bogner          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
224533c76c0cSRong Xu          << "    Counters: " << Func.Counts.size() << "\n";
224633c76c0cSRong Xu       if (!IsIRInstr)
224733c76c0cSRong Xu         OS << "    Function count: " << Func.Counts[0] << "\n";
22486f7c19a4SXinliang David Li 
22499e9a057aSJustin Bogner       if (ShowIndirectCallTargets)
22502004f003SXinliang David Li         OS << "    Indirect Call Site Count: "
22512004f003SXinliang David Li            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
22529af28ef9SJustin Bogner 
225360faea19SRong Xu       uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
225460faea19SRong Xu       if (ShowMemOPSizes && NumMemOPCalls > 0)
225560faea19SRong Xu         OS << "    Number of Memory Intrinsics Calls: " << NumMemOPCalls
225660faea19SRong Xu            << "\n";
225760faea19SRong Xu 
22586f7c19a4SXinliang David Li       if (ShowCounts) {
22599af28ef9SJustin Bogner         OS << "    Block counts: [";
226033c76c0cSRong Xu         size_t Start = (IsIRInstr ? 0 : 1);
226133c76c0cSRong Xu         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
226233c76c0cSRong Xu           OS << (I == Start ? "" : ", ") << Func.Counts[I];
22639af28ef9SJustin Bogner         }
22649af28ef9SJustin Bogner         OS << "]\n";
22656f7c19a4SXinliang David Li       }
22669e9a057aSJustin Bogner 
22676f7c19a4SXinliang David Li       if (ShowIndirectCallTargets) {
22689e9a057aSJustin Bogner         OS << "    Indirect Target Results:\n";
22690cf1f56aSRong Xu         traverseAllValueSites(Func, IPVK_IndirectCallTarget,
22700cf1f56aSRong Xu                               VPStats[IPVK_IndirectCallTarget], OS,
227160faea19SRong Xu                               &(Reader->getSymtab()));
227260faea19SRong Xu       }
227360faea19SRong Xu 
227460faea19SRong Xu       if (ShowMemOPSizes && NumMemOPCalls > 0) {
2275cd2aa0d2STeresa Johnson         OS << "    Memory Intrinsic Size Results:\n";
227660faea19SRong Xu         traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
227760faea19SRong Xu                               nullptr);
22789e9a057aSJustin Bogner       }
22799af28ef9SJustin Bogner     }
22806f7c19a4SXinliang David Li   }
2281db1225d0SJustin Bogner   if (Reader->hasError())
22829152fd17SVedant Kumar     exitWithError(Reader->getError(), Filename);
22839af28ef9SJustin Bogner 
228411d30742SEllis Hoag   if (TextFormat || ShowCovered)
22856f7c19a4SXinliang David Li     return 0;
22867cefdb81SEaswaran Raman   std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
228750da55a5SRong Xu   bool IsIR = Reader->isIRLevelProfile();
228850da55a5SRong Xu   OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
228950da55a5SRong Xu   if (IsIR)
229050da55a5SRong Xu     OS << "  entry_first = " << Reader->instrEntryBBEnabled();
229150da55a5SRong Xu   OS << "\n";
22929af28ef9SJustin Bogner   if (ShowAllFunctions || !ShowFunction.empty())
22939af28ef9SJustin Bogner     OS << "Functions shown: " << ShownFunctions << "\n";
2294e5a17e3fSEaswaran Raman   OS << "Total functions: " << PS->getNumFunctions() << "\n";
229552aa224aSRong Xu   if (ValueCutoff > 0) {
229652aa224aSRong Xu     OS << "Number of functions with maximum count (< " << ValueCutoff
229752aa224aSRong Xu        << "): " << BelowCutoffFunctions << "\n";
229852aa224aSRong Xu     OS << "Number of functions with maximum count (>= " << ValueCutoff
229952aa224aSRong Xu        << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
230052aa224aSRong Xu   }
2301e5a17e3fSEaswaran Raman   OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
23027cefdb81SEaswaran Raman   OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
230360faea19SRong Xu 
2304801b5319SXinliang David Li   if (TopN) {
2305801b5319SXinliang David Li     std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
2306801b5319SXinliang David Li     while (!HottestFuncs.empty()) {
2307801b5319SXinliang David Li       SortedHottestFuncs.emplace_back(HottestFuncs.top());
2308801b5319SXinliang David Li       HottestFuncs.pop();
2309801b5319SXinliang David Li     }
2310801b5319SXinliang David Li     OS << "Top " << TopN
2311801b5319SXinliang David Li        << " functions with the largest internal block counts: \n";
2312801b5319SXinliang David Li     for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
2313801b5319SXinliang David Li       OS << "  " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
2314801b5319SXinliang David Li   }
2315801b5319SXinliang David Li 
2316872362c4SXinliang David Li   if (ShownFunctions && ShowIndirectCallTargets) {
23170cf1f56aSRong Xu     OS << "Statistics for indirect call sites profile:\n";
23180cf1f56aSRong Xu     showValueSitesStats(OS, IPVK_IndirectCallTarget,
23190cf1f56aSRong Xu                         VPStats[IPVK_IndirectCallTarget]);
2320872362c4SXinliang David Li   }
2321183ebbe0SEaswaran Raman 
232260faea19SRong Xu   if (ShownFunctions && ShowMemOPSizes) {
232360faea19SRong Xu     OS << "Statistics for memory intrinsic calls sizes profile:\n";
232460faea19SRong Xu     showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
232560faea19SRong Xu   }
232660faea19SRong Xu 
2327183ebbe0SEaswaran Raman   if (ShowDetailedSummary) {
23287cefdb81SEaswaran Raman     OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
2329e5a17e3fSEaswaran Raman     OS << "Total count: " << PS->getTotalCount() << "\n";
233017fc6518SWenlei He     PS->printDetailedSummary(OS);
2331183ebbe0SEaswaran Raman   }
2332e50a3884SGulfem Savrun Yeniceri 
2333e50a3884SGulfem Savrun Yeniceri   if (ShowBinaryIds)
2334e50a3884SGulfem Savrun Yeniceri     if (Error E = Reader->printBinaryIds(OS))
2335e50a3884SGulfem Savrun Yeniceri       exitWithError(std::move(E), Filename);
2336e50a3884SGulfem Savrun Yeniceri 
23379af28ef9SJustin Bogner   return 0;
23389af28ef9SJustin Bogner }
23399af28ef9SJustin Bogner 
showSectionInfo(sampleprof::SampleProfileReader * Reader,raw_fd_ostream & OS)2340eee532cdSWei Mi static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
2341eee532cdSWei Mi                             raw_fd_ostream &OS) {
2342eee532cdSWei Mi   if (!Reader->dumpSectionInfo(OS)) {
2343eee532cdSWei Mi     WithColor::warning() << "-show-sec-info-only is only supported for "
2344eee532cdSWei Mi                          << "sample profile in extbinary format and is "
2345eee532cdSWei Mi                          << "ignored for other formats.\n";
2346eee532cdSWei Mi     return;
2347eee532cdSWei Mi   }
2348eee532cdSWei Mi }
2349eee532cdSWei Mi 
2350546be088SFangrui Song namespace {
235153cf5302Sweihe struct HotFuncInfo {
2352b9db7036SHongtao Yu   std::string FuncName;
235353cf5302Sweihe   uint64_t TotalCount;
235453cf5302Sweihe   double TotalCountPercent;
235553cf5302Sweihe   uint64_t MaxCount;
235653cf5302Sweihe   uint64_t EntryCount;
235753cf5302Sweihe 
HotFuncInfo__anon73ab2c7b1111::HotFuncInfo235853cf5302Sweihe   HotFuncInfo()
2359f44473ecSKazu Hirata       : TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), EntryCount(0) {}
236053cf5302Sweihe 
HotFuncInfo__anon73ab2c7b1111::HotFuncInfo236153cf5302Sweihe   HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
2362b9db7036SHongtao Yu       : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
2363b9db7036SHongtao Yu         MaxCount(MS), EntryCount(ES) {}
236453cf5302Sweihe };
2365546be088SFangrui Song } // namespace
236653cf5302Sweihe 
236753cf5302Sweihe // Print out detailed information about hot functions in PrintValues vector.
236853cf5302Sweihe // Users specify titles and offset of every columns through ColumnTitle and
236953cf5302Sweihe // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
237053cf5302Sweihe // and at least 4. Besides, users can optionally give a HotFuncMetric string to
237153cf5302Sweihe // print out or let it be an empty string.
dumpHotFunctionList(const std::vector<std::string> & ColumnTitle,const std::vector<int> & ColumnOffset,const std::vector<HotFuncInfo> & PrintValues,uint64_t HotFuncCount,uint64_t TotalFuncCount,uint64_t HotProfCount,uint64_t TotalProfCount,const std::string & HotFuncMetric,uint32_t TopNFunctions,raw_fd_ostream & OS)237253cf5302Sweihe static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
237353cf5302Sweihe                                 const std::vector<int> &ColumnOffset,
237453cf5302Sweihe                                 const std::vector<HotFuncInfo> &PrintValues,
237553cf5302Sweihe                                 uint64_t HotFuncCount, uint64_t TotalFuncCount,
237653cf5302Sweihe                                 uint64_t HotProfCount, uint64_t TotalProfCount,
237753cf5302Sweihe                                 const std::string &HotFuncMetric,
2378ce6ed64aSmodimo                                 uint32_t TopNFunctions, raw_fd_ostream &OS) {
2379540489deSweihe   assert(ColumnOffset.size() == ColumnTitle.size() &&
2380540489deSweihe          "ColumnOffset and ColumnTitle should have the same size");
2381540489deSweihe   assert(ColumnTitle.size() >= 4 &&
2382540489deSweihe          "ColumnTitle should have at least 4 elements");
2383540489deSweihe   assert(TotalFuncCount > 0 &&
2384540489deSweihe          "There should be at least one function in the profile");
238553cf5302Sweihe   double TotalProfPercent = 0;
238653cf5302Sweihe   if (TotalProfCount > 0)
2387540489deSweihe     TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
238853cf5302Sweihe 
238953cf5302Sweihe   formatted_raw_ostream FOS(OS);
239053cf5302Sweihe   FOS << HotFuncCount << " out of " << TotalFuncCount
239153cf5302Sweihe       << " functions with profile ("
2392540489deSweihe       << format("%.2f%%",
2393540489deSweihe                 (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
239453cf5302Sweihe       << ") are considered hot functions";
239553cf5302Sweihe   if (!HotFuncMetric.empty())
239653cf5302Sweihe     FOS << " (" << HotFuncMetric << ")";
239753cf5302Sweihe   FOS << ".\n";
239853cf5302Sweihe   FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
239953cf5302Sweihe       << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
240053cf5302Sweihe 
240153cf5302Sweihe   for (size_t I = 0; I < ColumnTitle.size(); ++I) {
240253cf5302Sweihe     FOS.PadToColumn(ColumnOffset[I]);
240353cf5302Sweihe     FOS << ColumnTitle[I];
240453cf5302Sweihe   }
240553cf5302Sweihe   FOS << "\n";
240653cf5302Sweihe 
2407ce6ed64aSmodimo   uint32_t Count = 0;
2408ce6ed64aSmodimo   for (const auto &R : PrintValues) {
2409ce6ed64aSmodimo     if (TopNFunctions && (Count++ == TopNFunctions))
2410ce6ed64aSmodimo       break;
241153cf5302Sweihe     FOS.PadToColumn(ColumnOffset[0]);
241253cf5302Sweihe     FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
241353cf5302Sweihe     FOS.PadToColumn(ColumnOffset[1]);
241453cf5302Sweihe     FOS << R.MaxCount;
241553cf5302Sweihe     FOS.PadToColumn(ColumnOffset[2]);
241653cf5302Sweihe     FOS << R.EntryCount;
241753cf5302Sweihe     FOS.PadToColumn(ColumnOffset[3]);
241853cf5302Sweihe     FOS << R.FuncName << "\n";
241953cf5302Sweihe   }
242053cf5302Sweihe }
242153cf5302Sweihe 
showHotFunctionList(const sampleprof::SampleProfileMap & Profiles,ProfileSummary & PS,uint32_t TopN,raw_fd_ostream & OS)2422b9db7036SHongtao Yu static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
2423ce6ed64aSmodimo                                ProfileSummary &PS, uint32_t TopN,
2424ce6ed64aSmodimo                                raw_fd_ostream &OS) {
242553cf5302Sweihe   using namespace sampleprof;
242653cf5302Sweihe 
242753cf5302Sweihe   const uint32_t HotFuncCutoff = 990000;
242853cf5302Sweihe   auto &SummaryVector = PS.getDetailedSummary();
242953cf5302Sweihe   uint64_t MinCountThreshold = 0;
2430546be088SFangrui Song   for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
243153cf5302Sweihe     if (SummaryEntry.Cutoff == HotFuncCutoff) {
243253cf5302Sweihe       MinCountThreshold = SummaryEntry.MinCount;
243353cf5302Sweihe       break;
243453cf5302Sweihe     }
243553cf5302Sweihe   }
243653cf5302Sweihe 
243753cf5302Sweihe   // Traverse all functions in the profile and keep only hot functions.
243853cf5302Sweihe   // The following loop also calculates the sum of total samples of all
243953cf5302Sweihe   // functions.
244053cf5302Sweihe   std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
244153cf5302Sweihe                 std::greater<uint64_t>>
244253cf5302Sweihe       HotFunc;
244353cf5302Sweihe   uint64_t ProfileTotalSample = 0;
244453cf5302Sweihe   uint64_t HotFuncSample = 0;
244553cf5302Sweihe   uint64_t HotFuncCount = 0;
2446540489deSweihe 
244753cf5302Sweihe   for (const auto &I : Profiles) {
2448540489deSweihe     FuncSampleStats FuncStats;
2449546be088SFangrui Song     const FunctionSamples &FuncProf = I.second;
245053cf5302Sweihe     ProfileTotalSample += FuncProf.getTotalSamples();
2451540489deSweihe     getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
245253cf5302Sweihe 
2453540489deSweihe     if (isFunctionHot(FuncStats, MinCountThreshold)) {
245453cf5302Sweihe       HotFunc.emplace(FuncProf.getTotalSamples(),
2455540489deSweihe                       std::make_pair(&(I.second), FuncStats.MaxSample));
245653cf5302Sweihe       HotFuncSample += FuncProf.getTotalSamples();
245753cf5302Sweihe       ++HotFuncCount;
245853cf5302Sweihe     }
245953cf5302Sweihe   }
246053cf5302Sweihe 
246153cf5302Sweihe   std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
246253cf5302Sweihe                                        "Entry sample", "Function name"};
246353cf5302Sweihe   std::vector<int> ColumnOffset{0, 24, 42, 58};
246453cf5302Sweihe   std::string Metric =
246553cf5302Sweihe       std::string("max sample >= ") + std::to_string(MinCountThreshold);
246653cf5302Sweihe   std::vector<HotFuncInfo> PrintValues;
246753cf5302Sweihe   for (const auto &FuncPair : HotFunc) {
2468546be088SFangrui Song     const FunctionSamples &Func = *FuncPair.second.first;
246953cf5302Sweihe     double TotalSamplePercent =
247053cf5302Sweihe         (ProfileTotalSample > 0)
2471546be088SFangrui Song             ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
247253cf5302Sweihe             : 0;
24737b81a81dSMircea Trofin     PrintValues.emplace_back(
24747b81a81dSMircea Trofin         HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),
24757b81a81dSMircea Trofin                     TotalSamplePercent, FuncPair.second.second,
24767b81a81dSMircea Trofin                     Func.getHeadSamplesEstimate()));
247753cf5302Sweihe   }
247853cf5302Sweihe   dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
247953cf5302Sweihe                       Profiles.size(), HotFuncSample, ProfileTotalSample,
2480ce6ed64aSmodimo                       Metric, TopN, OS);
248153cf5302Sweihe 
248253cf5302Sweihe   return 0;
248353cf5302Sweihe }
248453cf5302Sweihe 
showSampleProfile(const std::string & Filename,bool ShowCounts,uint32_t TopN,bool ShowAllFunctions,bool ShowDetailedSummary,const std::string & ShowFunction,bool ShowProfileSymbolList,bool ShowSectionInfoOnly,bool ShowHotFuncList,raw_fd_ostream & OS)24851afc1de4SBenjamin Kramer static int showSampleProfile(const std::string &Filename, bool ShowCounts,
2486ce6ed64aSmodimo                              uint32_t TopN, bool ShowAllFunctions,
2487ce6ed64aSmodimo                              bool ShowDetailedSummary,
24881afc1de4SBenjamin Kramer                              const std::string &ShowFunction,
2489eee532cdSWei Mi                              bool ShowProfileSymbolList,
249053cf5302Sweihe                              bool ShowSectionInfoOnly, bool ShowHotFuncList,
249153cf5302Sweihe                              raw_fd_ostream &OS) {
2492d5336ae2SDiego Novillo   using namespace sampleprof;
249303b42e41SMehdi Amini   LLVMContext Context;
24948d581857SRong Xu   auto ReaderOrErr =
24958d581857SRong Xu       SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption);
2496fcd55607SDiego Novillo   if (std::error_code EC = ReaderOrErr.getError())
24974f823667SNathan Slingerland     exitWithErrorCode(EC, Filename);
2498d5336ae2SDiego Novillo 
2499fcd55607SDiego Novillo   auto Reader = std::move(ReaderOrErr.get());
2500eee532cdSWei Mi   if (ShowSectionInfoOnly) {
2501eee532cdSWei Mi     showSectionInfo(Reader.get(), OS);
2502eee532cdSWei Mi     return 0;
2503eee532cdSWei Mi   }
2504eee532cdSWei Mi 
2505c6d032abSDiego Novillo   if (std::error_code EC = Reader->read())
25064f823667SNathan Slingerland     exitWithErrorCode(EC, Filename);
2507c6d032abSDiego Novillo 
2508d5336ae2SDiego Novillo   if (ShowAllFunctions || ShowFunction.empty())
2509d5336ae2SDiego Novillo     Reader->dump(OS);
2510d5336ae2SDiego Novillo   else
2511b9db7036SHongtao Yu     // TODO: parse context string to support filtering by contexts.
2512b9db7036SHongtao Yu     Reader->dumpFunctionProfile(StringRef(ShowFunction), OS);
2513d5336ae2SDiego Novillo 
2514798e59b8SWei Mi   if (ShowProfileSymbolList) {
2515798e59b8SWei Mi     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
2516798e59b8SWei Mi         Reader->getProfileSymbolList();
2517798e59b8SWei Mi     ReaderList->dump(OS);
2518798e59b8SWei Mi   }
2519798e59b8SWei Mi 
252017fc6518SWenlei He   if (ShowDetailedSummary) {
252117fc6518SWenlei He     auto &PS = Reader->getSummary();
252217fc6518SWenlei He     PS.printSummary(OS);
252317fc6518SWenlei He     PS.printDetailedSummary(OS);
252417fc6518SWenlei He   }
252517fc6518SWenlei He 
2526ce6ed64aSmodimo   if (ShowHotFuncList || TopN)
2527ce6ed64aSmodimo     showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS);
252853cf5302Sweihe 
2529d5336ae2SDiego Novillo   return 0;
2530d5336ae2SDiego Novillo }
2531d5336ae2SDiego Novillo 
showMemProfProfile(const std::string & Filename,const std::string & ProfiledBinary,raw_fd_ostream & OS)2532216575e5SSnehasish Kumar static int showMemProfProfile(const std::string &Filename,
2533216575e5SSnehasish Kumar                               const std::string &ProfiledBinary,
2534216575e5SSnehasish Kumar                               raw_fd_ostream &OS) {
2535ec51971eSSnehasish Kumar   auto ReaderOr = llvm::memprof::RawMemProfReader::create(
2536ec51971eSSnehasish Kumar       Filename, ProfiledBinary, /*KeepNames=*/true);
25377cca33b4SSnehasish Kumar   if (Error E = ReaderOr.takeError())
2538216575e5SSnehasish Kumar     // Since the error can be related to the profile or the binary we do not
2539216575e5SSnehasish Kumar     // pass whence. Instead additional context is provided where necessary in
2540216575e5SSnehasish Kumar     // the error message.
2541216575e5SSnehasish Kumar     exitWithError(std::move(E), /*Whence*/ "");
25427cca33b4SSnehasish Kumar 
25437cca33b4SSnehasish Kumar   std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
25447cca33b4SSnehasish Kumar       ReaderOr.get().release());
254514f4f63aSSnehasish Kumar 
254614f4f63aSSnehasish Kumar   Reader->printYAML(OS);
25477cca33b4SSnehasish Kumar   return 0;
25487cca33b4SSnehasish Kumar }
25497cca33b4SSnehasish Kumar 
showDebugInfoCorrelation(const std::string & Filename,bool ShowDetailedSummary,bool ShowProfileSymbolList,raw_fd_ostream & OS)2550c9baa560SEllis Hoag static int showDebugInfoCorrelation(const std::string &Filename,
2551c9baa560SEllis Hoag                                     bool ShowDetailedSummary,
2552c9baa560SEllis Hoag                                     bool ShowProfileSymbolList,
2553c9baa560SEllis Hoag                                     raw_fd_ostream &OS) {
2554c9baa560SEllis Hoag   std::unique_ptr<InstrProfCorrelator> Correlator;
2555c9baa560SEllis Hoag   if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator))
2556c9baa560SEllis Hoag     exitWithError(std::move(Err), Filename);
2557c9baa560SEllis Hoag   if (auto Err = Correlator->correlateProfileData())
2558c9baa560SEllis Hoag     exitWithError(std::move(Err), Filename);
2559c9baa560SEllis Hoag 
2560c9baa560SEllis Hoag   InstrProfSymtab Symtab;
2561c9baa560SEllis Hoag   if (auto Err = Symtab.create(
2562c9baa560SEllis Hoag           StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
2563c9baa560SEllis Hoag     exitWithError(std::move(Err), Filename);
2564c9baa560SEllis Hoag 
2565c9baa560SEllis Hoag   if (ShowProfileSymbolList)
2566c9baa560SEllis Hoag     Symtab.dumpNames(OS);
2567c9baa560SEllis Hoag   // TODO: Read "Profile Data Type" from debug info to compute and show how many
2568c9baa560SEllis Hoag   // counters the section holds.
2569c9baa560SEllis Hoag   if (ShowDetailedSummary)
2570c9baa560SEllis Hoag     OS << "Counters section size: 0x"
2571c9baa560SEllis Hoag        << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
2572c9baa560SEllis Hoag   OS << "Found " << Correlator->getDataSize() << " functions\n";
2573c9baa560SEllis Hoag 
2574c9baa560SEllis Hoag   return 0;
2575c9baa560SEllis Hoag }
2576c9baa560SEllis Hoag 
show_main(int argc,const char * argv[])2577f044d3f9SBenjamin Kramer static int show_main(int argc, const char *argv[]) {
2578c9baa560SEllis Hoag   cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>"));
2579d5336ae2SDiego Novillo 
2580d5336ae2SDiego Novillo   cl::opt<bool> ShowCounts("counts", cl::init(false),
2581d5336ae2SDiego Novillo                            cl::desc("Show counter values for shown functions"));
25826f7c19a4SXinliang David Li   cl::opt<bool> TextFormat(
25836f7c19a4SXinliang David Li       "text", cl::init(false),
25846f7c19a4SXinliang David Li       cl::desc("Show instr profile data in text dump format"));
25859e9a057aSJustin Bogner   cl::opt<bool> ShowIndirectCallTargets(
25869e9a057aSJustin Bogner       "ic-targets", cl::init(false),
25879e9a057aSJustin Bogner       cl::desc("Show indirect call site target values for shown functions"));
258860faea19SRong Xu   cl::opt<bool> ShowMemOPSizes(
258960faea19SRong Xu       "memop-sizes", cl::init(false),
259060faea19SRong Xu       cl::desc("Show the profiled sizes of the memory intrinsic calls "
259160faea19SRong Xu                "for shown functions"));
2592183ebbe0SEaswaran Raman   cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
2593183ebbe0SEaswaran Raman                                     cl::desc("Show detailed profile summary"));
2594183ebbe0SEaswaran Raman   cl::list<uint32_t> DetailedSummaryCutoffs(
2595183ebbe0SEaswaran Raman       cl::CommaSeparated, "detailed-summary-cutoffs",
2596183ebbe0SEaswaran Raman       cl::desc(
2597183ebbe0SEaswaran Raman           "Cutoff percentages (times 10000) for generating detailed summary"),
2598183ebbe0SEaswaran Raman       cl::value_desc("800000,901000,999999"));
259953cf5302Sweihe   cl::opt<bool> ShowHotFuncList(
260053cf5302Sweihe       "hot-func-list", cl::init(false),
260153cf5302Sweihe       cl::desc("Show profile summary of a list of hot functions"));
2602d5336ae2SDiego Novillo   cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
2603d5336ae2SDiego Novillo                                  cl::desc("Details for every function"));
2604a6ff69f6SRong Xu   cl::opt<bool> ShowCS("showcs", cl::init(false),
2605a6ff69f6SRong Xu                        cl::desc("Show context sensitive counts"));
2606d5336ae2SDiego Novillo   cl::opt<std::string> ShowFunction("function",
2607d5336ae2SDiego Novillo                                     cl::desc("Details for matching functions"));
2608d5336ae2SDiego Novillo 
2609d5336ae2SDiego Novillo   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
2610d5336ae2SDiego Novillo                                       cl::init("-"), cl::desc("Output file"));
2611d5336ae2SDiego Novillo   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
2612d5336ae2SDiego Novillo                             cl::aliasopt(OutputFilename));
2613d5336ae2SDiego Novillo   cl::opt<ProfileKinds> ProfileKind(
2614d5336ae2SDiego Novillo       cl::desc("Profile kind:"), cl::init(instr),
2615d5336ae2SDiego Novillo       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
26167cca33b4SSnehasish Kumar                  clEnumVal(sample, "Sample profile"),
26177cca33b4SSnehasish Kumar                  clEnumVal(memory, "MemProf memory access profile")));
2618801b5319SXinliang David Li   cl::opt<uint32_t> TopNFunctions(
2619801b5319SXinliang David Li       "topn", cl::init(0),
2620801b5319SXinliang David Li       cl::desc("Show the list of functions with the largest internal counts"));
262152aa224aSRong Xu   cl::opt<uint32_t> ValueCutoff(
262252aa224aSRong Xu       "value-cutoff", cl::init(0),
262352aa224aSRong Xu       cl::desc("Set the count value cutoff. Functions with the maximum count "
262452aa224aSRong Xu                "less than this value will not be printed out. (Default is 0)"));
262552aa224aSRong Xu   cl::opt<bool> OnlyListBelow(
262652aa224aSRong Xu       "list-below-cutoff", cl::init(false),
262752aa224aSRong Xu       cl::desc("Only output names of functions whose max count values are "
262852aa224aSRong Xu                "below the cutoff value"));
2629798e59b8SWei Mi   cl::opt<bool> ShowProfileSymbolList(
2630798e59b8SWei Mi       "show-prof-sym-list", cl::init(false),
2631798e59b8SWei Mi       cl::desc("Show profile symbol list if it exists in the profile. "));
2632eee532cdSWei Mi   cl::opt<bool> ShowSectionInfoOnly(
2633eee532cdSWei Mi       "show-sec-info-only", cl::init(false),
2634eee532cdSWei Mi       cl::desc("Show the information of each section in the sample profile. "
2635eee532cdSWei Mi                "The flag is only usable when the sample profile is in "
2636eee532cdSWei Mi                "extbinary format"));
2637e50a3884SGulfem Savrun Yeniceri   cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
2638e50a3884SGulfem Savrun Yeniceri                               cl::desc("Show binary ids in the profile. "));
2639c9baa560SEllis Hoag   cl::opt<std::string> DebugInfoFilename(
2640c9baa560SEllis Hoag       "debug-info", cl::init(""),
2641c9baa560SEllis Hoag       cl::desc("Read and extract profile metadata from debug info and show "
2642c9baa560SEllis Hoag                "the functions it found."));
264311d30742SEllis Hoag   cl::opt<bool> ShowCovered(
264411d30742SEllis Hoag       "covered", cl::init(false),
264511d30742SEllis Hoag       cl::desc("Show only the functions that have been executed."));
2646216575e5SSnehasish Kumar   cl::opt<std::string> ProfiledBinary(
2647216575e5SSnehasish Kumar       "profiled-binary", cl::init(""),
2648216575e5SSnehasish Kumar       cl::desc("Path to binary from which the profile was collected."));
2649798e59b8SWei Mi 
2650d5336ae2SDiego Novillo   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
2651d5336ae2SDiego Novillo 
2652c9baa560SEllis Hoag   if (Filename.empty() && DebugInfoFilename.empty())
2653c9baa560SEllis Hoag     exitWithError(
2654c9baa560SEllis Hoag         "the positional argument '<profdata-file>' is required unless '--" +
2655c9baa560SEllis Hoag         DebugInfoFilename.ArgStr + "' is provided");
2656c9baa560SEllis Hoag 
2657e5b4dbabSKazu Hirata   if (Filename == OutputFilename) {
2658f0d3dcecSRong Xu     errs() << sys::path::filename(argv[0])
2659f0d3dcecSRong Xu            << ": Input file name cannot be the same as the output file name!\n";
2660f0d3dcecSRong Xu     return 1;
2661f0d3dcecSRong Xu   }
2662f0d3dcecSRong Xu 
2663d5336ae2SDiego Novillo   std::error_code EC;
266482b3e28eSAbhina Sreeskantharajan   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
2665d5336ae2SDiego Novillo   if (EC)
26664f823667SNathan Slingerland     exitWithErrorCode(EC, OutputFilename);
2667d5336ae2SDiego Novillo 
2668d5336ae2SDiego Novillo   if (ShowAllFunctions && !ShowFunction.empty())
2669e46b7565SJonas Devlieghere     WithColor::warning() << "-function argument ignored: showing all functions\n";
2670d5336ae2SDiego Novillo 
2671c9baa560SEllis Hoag   if (!DebugInfoFilename.empty())
2672c9baa560SEllis Hoag     return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary,
2673c9baa560SEllis Hoag                                     ShowProfileSymbolList, OS);
2674c9baa560SEllis Hoag 
2675d5336ae2SDiego Novillo   if (ProfileKind == instr)
2676e50a3884SGulfem Savrun Yeniceri     return showInstrProfile(
2677e50a3884SGulfem Savrun Yeniceri         Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets,
2678e50a3884SGulfem Savrun Yeniceri         ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs,
2679e50a3884SGulfem Savrun Yeniceri         ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction,
268011d30742SEllis Hoag         TextFormat, ShowBinaryIds, ShowCovered, OS);
26817cca33b4SSnehasish Kumar   if (ProfileKind == sample)
2682ce6ed64aSmodimo     return showSampleProfile(Filename, ShowCounts, TopNFunctions,
2683ce6ed64aSmodimo                              ShowAllFunctions, ShowDetailedSummary,
2684ce6ed64aSmodimo                              ShowFunction, ShowProfileSymbolList,
2685ce6ed64aSmodimo                              ShowSectionInfoOnly, ShowHotFuncList, OS);
2686216575e5SSnehasish Kumar   return showMemProfProfile(Filename, ProfiledBinary, OS);
2687d5336ae2SDiego Novillo }
2688d5336ae2SDiego Novillo 
main(int argc,const char * argv[])2689618bcea7SJustin Bogner int main(int argc, const char *argv[]) {
2690197194b6SRui Ueyama   InitLLVM X(argc, argv);
2691618bcea7SJustin Bogner 
2692618bcea7SJustin Bogner   StringRef ProgName(sys::path::filename(argv[0]));
2693618bcea7SJustin Bogner   if (argc > 1) {
2694e6cb63e4SCraig Topper     int (*func)(int, const char *[]) = nullptr;
2695618bcea7SJustin Bogner 
2696618bcea7SJustin Bogner     if (strcmp(argv[1], "merge") == 0)
2697618bcea7SJustin Bogner       func = merge_main;
26989af28ef9SJustin Bogner     else if (strcmp(argv[1], "show") == 0)
26999af28ef9SJustin Bogner       func = show_main;
2700998b97f6SRong Xu     else if (strcmp(argv[1], "overlap") == 0)
2701998b97f6SRong Xu       func = overlap_main;
2702618bcea7SJustin Bogner 
2703618bcea7SJustin Bogner     if (func) {
2704618bcea7SJustin Bogner       std::string Invocation(ProgName.str() + " " + argv[1]);
2705618bcea7SJustin Bogner       argv[1] = Invocation.c_str();
2706618bcea7SJustin Bogner       return func(argc - 1, argv + 1);
2707618bcea7SJustin Bogner     }
2708618bcea7SJustin Bogner 
2709d3babdbcSDiego Novillo     if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
2710618bcea7SJustin Bogner         strcmp(argv[1], "--help") == 0) {
2711618bcea7SJustin Bogner 
2712618bcea7SJustin Bogner       errs() << "OVERVIEW: LLVM profile data tools\n\n"
2713618bcea7SJustin Bogner              << "USAGE: " << ProgName << " <command> [args...]\n"
2714618bcea7SJustin Bogner              << "USAGE: " << ProgName << " <command> -help\n\n"
2715253eb17bSJustin Bogner              << "See each individual command --help for more details.\n"
2716998b97f6SRong Xu              << "Available commands: merge, show, overlap\n";
2717618bcea7SJustin Bogner       return 0;
2718618bcea7SJustin Bogner     }
2719618bcea7SJustin Bogner   }
2720618bcea7SJustin Bogner 
2721618bcea7SJustin Bogner   if (argc < 2)
2722618bcea7SJustin Bogner     errs() << ProgName << ": No command specified!\n";
2723618bcea7SJustin Bogner   else
2724618bcea7SJustin Bogner     errs() << ProgName << ": Unknown command!\n";
2725618bcea7SJustin Bogner 
2726998b97f6SRong Xu   errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n";
2727618bcea7SJustin Bogner   return 1;
2728618bcea7SJustin Bogner }
2729