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"
16d9903888SChandler Carruth #include "llvm/IR/LLVMContext.h"
17f8d79198SJustin Bogner #include "llvm/ProfileData/InstrProfReader.h"
18b9bd7f85SJustin Bogner #include "llvm/ProfileData/InstrProfWriter.h"
19d68aae24SEaswaran Raman #include "llvm/ProfileData/ProfileCommon.h"
20d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfReader.h"
21d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfWriter.h"
22846a627fSDuncan P. N. Exon Smith #include "llvm/Support/CommandLine.h"
237f5b47ddSNathan Slingerland #include "llvm/Support/Errc.h"
24d59664f4SBenjamin Kramer #include "llvm/Support/FileSystem.h"
25423380f9SJustin Bogner #include "llvm/Support/Format.h"
2653cf5302Sweihe #include "llvm/Support/FormattedStream.h"
27197194b6SRui Ueyama #include "llvm/Support/InitLLVM.h"
28846a627fSDuncan P. N. Exon Smith #include "llvm/Support/MemoryBuffer.h"
2916132e6fSBenjamin Kramer #include "llvm/Support/Path.h"
305342dd6bSBruno Ricci #include "llvm/Support/ThreadPool.h"
3153cf5302Sweihe #include "llvm/Support/Threading.h"
32ef598759SFangrui Song #include "llvm/Support/WithColor.h"
33846a627fSDuncan P. N. Exon Smith #include "llvm/Support/raw_ostream.h"
347f5b47ddSNathan Slingerland #include <algorithm>
35846a627fSDuncan P. N. Exon Smith 
36846a627fSDuncan P. N. Exon Smith using namespace llvm;
37846a627fSDuncan P. N. Exon Smith 
38a0c0857eSWei Mi enum ProfileFormat {
39a0c0857eSWei Mi   PF_None = 0,
40a0c0857eSWei Mi   PF_Text,
41a0c0857eSWei Mi   PF_Compact_Binary,
42be907324SWei Mi   PF_Ext_Binary,
43a0c0857eSWei Mi   PF_GCC,
44d9be2c7eSWei Mi   PF_Binary
45a0c0857eSWei Mi };
466f7c19a4SXinliang David Li 
47e46b7565SJonas Devlieghere static void warn(Twine Message, std::string Whence = "",
48faaa42adSVedant Kumar                  std::string Hint = "") {
49e46b7565SJonas Devlieghere   WithColor::warning();
50f8d79198SJustin Bogner   if (!Whence.empty())
51f8d79198SJustin Bogner     errs() << Whence << ": ";
52f8d79198SJustin Bogner   errs() << Message << "\n";
534f823667SNathan Slingerland   if (!Hint.empty())
54e46b7565SJonas Devlieghere     WithColor::note() << Hint << "\n";
55188efda5SVedant Kumar }
56188efda5SVedant Kumar 
57188efda5SVedant Kumar static void exitWithError(Twine Message, std::string Whence = "",
58188efda5SVedant Kumar                           std::string Hint = "") {
59e46b7565SJonas Devlieghere   WithColor::error();
60e46b7565SJonas Devlieghere   if (!Whence.empty())
61e46b7565SJonas Devlieghere     errs() << Whence << ": ";
62e46b7565SJonas Devlieghere   errs() << Message << "\n";
63e46b7565SJonas Devlieghere   if (!Hint.empty())
64e46b7565SJonas Devlieghere     WithColor::note() << Hint << "\n";
65846a627fSDuncan P. N. Exon Smith   ::exit(1);
66846a627fSDuncan P. N. Exon Smith }
67846a627fSDuncan P. N. Exon Smith 
689152fd17SVedant Kumar static void exitWithError(Error E, StringRef Whence = "") {
699152fd17SVedant Kumar   if (E.isA<InstrProfError>()) {
709152fd17SVedant Kumar     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
719152fd17SVedant Kumar       instrprof_error instrError = IPE.get();
729152fd17SVedant Kumar       StringRef Hint = "";
734f823667SNathan Slingerland       if (instrError == instrprof_error::unrecognized_format) {
7401bfb366SYi Kong         // Hint for common error of forgetting --sample for sample profiles.
7501bfb366SYi Kong         Hint = "Perhaps you forgot to use the --sample option?";
764f823667SNathan Slingerland       }
77adcd0268SBenjamin Kramer       exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
789152fd17SVedant Kumar     });
794f823667SNathan Slingerland   }
809152fd17SVedant Kumar 
81adcd0268SBenjamin Kramer   exitWithError(toString(std::move(E)), std::string(Whence));
829152fd17SVedant Kumar }
839152fd17SVedant Kumar 
849152fd17SVedant Kumar static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
85adcd0268SBenjamin Kramer   exitWithError(EC.message(), std::string(Whence));
864f823667SNathan Slingerland }
874f823667SNathan Slingerland 
8802b6fa90SDuncan P. N. Exon Smith namespace {
89d5336ae2SDiego Novillo enum ProfileKinds { instr, sample };
900fcfe897SVedant Kumar enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid };
910fcfe897SVedant Kumar }
920fcfe897SVedant Kumar 
930fcfe897SVedant Kumar static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
940fcfe897SVedant Kumar                                  StringRef Whence = "") {
950fcfe897SVedant Kumar   if (FailMode == failIfAnyAreInvalid)
960fcfe897SVedant Kumar     exitWithErrorCode(EC, Whence);
970fcfe897SVedant Kumar   else
98adcd0268SBenjamin Kramer     warn(EC.message(), std::string(Whence));
9902b6fa90SDuncan P. N. Exon Smith }
100618bcea7SJustin Bogner 
1019152fd17SVedant Kumar static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
102e6e30d5eSNathan Slingerland                                    StringRef WhenceFunction = "",
103d3babdbcSDiego Novillo                                    bool ShowHint = true) {
104e6e30d5eSNathan Slingerland   if (!WhenceFile.empty())
105e6e30d5eSNathan Slingerland     errs() << WhenceFile << ": ";
106e6e30d5eSNathan Slingerland   if (!WhenceFunction.empty())
107e6e30d5eSNathan Slingerland     errs() << WhenceFunction << ": ";
1089152fd17SVedant Kumar 
1099152fd17SVedant Kumar   auto IPE = instrprof_error::success;
1109152fd17SVedant Kumar   E = handleErrors(std::move(E),
1119152fd17SVedant Kumar                    [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
1129152fd17SVedant Kumar                      IPE = E->get();
1139152fd17SVedant Kumar                      return Error(std::move(E));
1149152fd17SVedant Kumar                    });
1159152fd17SVedant Kumar   errs() << toString(std::move(E)) << "\n";
116e6e30d5eSNathan Slingerland 
117e6e30d5eSNathan Slingerland   if (ShowHint) {
118e6e30d5eSNathan Slingerland     StringRef Hint = "";
1199152fd17SVedant Kumar     if (IPE != instrprof_error::success) {
1209152fd17SVedant Kumar       switch (IPE) {
12111c938d1SNathan Slingerland       case instrprof_error::hash_mismatch:
12211c938d1SNathan Slingerland       case instrprof_error::count_mismatch:
12311c938d1SNathan Slingerland       case instrprof_error::value_site_count_mismatch:
124d3babdbcSDiego Novillo         Hint = "Make sure that all profile data to be merged is generated "
125e6e30d5eSNathan Slingerland                "from the same binary.";
12611c938d1SNathan Slingerland         break;
127b2d95f0dSNathan Slingerland       default:
128b2d95f0dSNathan Slingerland         break;
129e6e30d5eSNathan Slingerland       }
130e6e30d5eSNathan Slingerland     }
131e6e30d5eSNathan Slingerland 
132e6e30d5eSNathan Slingerland     if (!Hint.empty())
133e6e30d5eSNathan Slingerland       errs() << Hint << "\n";
134e6e30d5eSNathan Slingerland   }
135e6e30d5eSNathan Slingerland }
136e6e30d5eSNathan Slingerland 
1373164fcfdSRichard Smith namespace {
1383164fcfdSRichard Smith /// A remapper from original symbol names to new symbol names based on a file
1393164fcfdSRichard Smith /// containing a list of mappings from old name to new name.
1403164fcfdSRichard Smith class SymbolRemapper {
1413164fcfdSRichard Smith   std::unique_ptr<MemoryBuffer> File;
1423164fcfdSRichard Smith   DenseMap<StringRef, StringRef> RemappingTable;
1433164fcfdSRichard Smith 
1443164fcfdSRichard Smith public:
1453164fcfdSRichard Smith   /// Build a SymbolRemapper from a file containing a list of old/new symbols.
1463164fcfdSRichard Smith   static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
1473164fcfdSRichard Smith     auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
1483164fcfdSRichard Smith     if (!BufOrError)
1493164fcfdSRichard Smith       exitWithErrorCode(BufOrError.getError(), InputFile);
1503164fcfdSRichard Smith 
1510eaee545SJonas Devlieghere     auto Remapper = std::make_unique<SymbolRemapper>();
1523164fcfdSRichard Smith     Remapper->File = std::move(BufOrError.get());
1533164fcfdSRichard Smith 
1543164fcfdSRichard Smith     for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
1553164fcfdSRichard Smith          !LineIt.is_at_eof(); ++LineIt) {
1563164fcfdSRichard Smith       std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
1573164fcfdSRichard Smith       if (Parts.first.empty() || Parts.second.empty() ||
1583164fcfdSRichard Smith           Parts.second.count(' ')) {
1593164fcfdSRichard Smith         exitWithError("unexpected line in remapping file",
1603164fcfdSRichard Smith                       (InputFile + ":" + Twine(LineIt.line_number())).str(),
1613164fcfdSRichard Smith                       "expected 'old_symbol new_symbol'");
1623164fcfdSRichard Smith       }
1633164fcfdSRichard Smith       Remapper->RemappingTable.insert(Parts);
1643164fcfdSRichard Smith     }
1653164fcfdSRichard Smith     return Remapper;
1663164fcfdSRichard Smith   }
1673164fcfdSRichard Smith 
1683164fcfdSRichard Smith   /// Attempt to map the given old symbol into a new symbol.
1693164fcfdSRichard Smith   ///
1703164fcfdSRichard Smith   /// \return The new symbol, or \p Name if no such symbol was found.
1713164fcfdSRichard Smith   StringRef operator()(StringRef Name) {
1723164fcfdSRichard Smith     StringRef New = RemappingTable.lookup(Name);
1733164fcfdSRichard Smith     return New.empty() ? Name : New;
1743164fcfdSRichard Smith   }
1753164fcfdSRichard Smith };
1763164fcfdSRichard Smith }
1773164fcfdSRichard Smith 
1787f5b47ddSNathan Slingerland struct WeightedFile {
1799a1bfcfaSXinliang David Li   std::string Filename;
1807f5b47ddSNathan Slingerland   uint64_t Weight;
1817f5b47ddSNathan Slingerland };
1827f5b47ddSNathan Slingerland typedef SmallVector<WeightedFile, 5> WeightedFileVector;
1837f5b47ddSNathan Slingerland 
184e3a0bf50SVedant Kumar /// Keep track of merged data and reported errors.
185e3a0bf50SVedant Kumar struct WriterContext {
186e3a0bf50SVedant Kumar   std::mutex Lock;
187e3a0bf50SVedant Kumar   InstrProfWriter Writer;
1880fcfe897SVedant Kumar   std::vector<std::pair<Error, std::string>> Errors;
189e3a0bf50SVedant Kumar   std::mutex &ErrLock;
190e3a0bf50SVedant Kumar   SmallSet<instrprof_error, 4> &WriterErrorCodes;
191e3a0bf50SVedant Kumar 
192e3a0bf50SVedant Kumar   WriterContext(bool IsSparse, std::mutex &ErrLock,
193e3a0bf50SVedant Kumar                 SmallSet<instrprof_error, 4> &WriterErrorCodes)
1940fcfe897SVedant Kumar       : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock),
1950fcfe897SVedant Kumar         WriterErrorCodes(WriterErrorCodes) {}
196e3a0bf50SVedant Kumar };
197e3a0bf50SVedant Kumar 
198998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and TestFileName,
199998b97f6SRong Xu /// and store the program level result to Overlap.
200998b97f6SRong Xu static void overlapInput(const std::string &BaseFilename,
201998b97f6SRong Xu                          const std::string &TestFilename, WriterContext *WC,
202998b97f6SRong Xu                          OverlapStats &Overlap,
203998b97f6SRong Xu                          const OverlapFuncFilters &FuncFilter,
204998b97f6SRong Xu                          raw_fd_ostream &OS, bool IsCS) {
205998b97f6SRong Xu   auto ReaderOrErr = InstrProfReader::create(TestFilename);
206998b97f6SRong Xu   if (Error E = ReaderOrErr.takeError()) {
207998b97f6SRong Xu     // Skip the empty profiles by returning sliently.
208998b97f6SRong Xu     instrprof_error IPE = InstrProfError::take(std::move(E));
209998b97f6SRong Xu     if (IPE != instrprof_error::empty_raw_profile)
2100fcfe897SVedant Kumar       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename);
211998b97f6SRong Xu     return;
212998b97f6SRong Xu   }
213998b97f6SRong Xu 
214998b97f6SRong Xu   auto Reader = std::move(ReaderOrErr.get());
215998b97f6SRong Xu   for (auto &I : *Reader) {
216998b97f6SRong Xu     OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
217998b97f6SRong Xu     FuncOverlap.setFuncInfo(I.Name, I.Hash);
218998b97f6SRong Xu 
219998b97f6SRong Xu     WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
220998b97f6SRong Xu     FuncOverlap.dump(OS);
221998b97f6SRong Xu   }
222998b97f6SRong Xu }
223998b97f6SRong Xu 
224e3a0bf50SVedant Kumar /// Load an input into a writer context.
2253164fcfdSRichard Smith static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
2263164fcfdSRichard Smith                       WriterContext *WC) {
227e3a0bf50SVedant Kumar   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
228e3a0bf50SVedant Kumar 
229faaa42adSVedant Kumar   // Copy the filename, because llvm::ThreadPool copied the input "const
230faaa42adSVedant Kumar   // WeightedFile &" by value, making a reference to the filename within it
231faaa42adSVedant Kumar   // invalid outside of this packaged task.
2320fcfe897SVedant Kumar   std::string Filename = Input.Filename;
233e3a0bf50SVedant Kumar 
234e3a0bf50SVedant Kumar   auto ReaderOrErr = InstrProfReader::create(Input.Filename);
2352c684cfdSRong Xu   if (Error E = ReaderOrErr.takeError()) {
2362c684cfdSRong Xu     // Skip the empty profiles by returning sliently.
2372c684cfdSRong Xu     instrprof_error IPE = InstrProfError::take(std::move(E));
2382c684cfdSRong Xu     if (IPE != instrprof_error::empty_raw_profile)
2390fcfe897SVedant Kumar       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
240e3a0bf50SVedant Kumar     return;
2412c684cfdSRong Xu   }
242e3a0bf50SVedant Kumar 
243e3a0bf50SVedant Kumar   auto Reader = std::move(ReaderOrErr.get());
244e3a0bf50SVedant Kumar   bool IsIRProfile = Reader->isIRLevelProfile();
245a6ff69f6SRong Xu   bool HasCSIRProfile = Reader->hasCSIRLevelProfile();
246a6ff69f6SRong Xu   if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) {
2470fcfe897SVedant Kumar     WC->Errors.emplace_back(
2480fcfe897SVedant Kumar         make_error<StringError>(
249e3a0bf50SVedant Kumar             "Merge IR generated profile with Clang generated profile.",
2500fcfe897SVedant Kumar             std::error_code()),
2510fcfe897SVedant Kumar         Filename);
252e3a0bf50SVedant Kumar     return;
253e3a0bf50SVedant Kumar   }
25450da55a5SRong Xu   WC->Writer.setInstrEntryBBEnabled(Reader->instrEntryBBEnabled());
255e3a0bf50SVedant Kumar 
256e3a0bf50SVedant Kumar   for (auto &I : *Reader) {
2573164fcfdSRichard Smith     if (Remapper)
2583164fcfdSRichard Smith       I.Name = (*Remapper)(I.Name);
259fe90d86cSRong Xu     const StringRef FuncName = I.Name;
26098cce003SDavid Blaikie     bool Reported = false;
26198cce003SDavid Blaikie     WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
26298cce003SDavid Blaikie       if (Reported) {
26398cce003SDavid Blaikie         consumeError(std::move(E));
26498cce003SDavid Blaikie         return;
26598cce003SDavid Blaikie       }
26698cce003SDavid Blaikie       Reported = true;
267e3a0bf50SVedant Kumar       // Only show hint the first time an error occurs.
268e3a0bf50SVedant Kumar       instrprof_error IPE = InstrProfError::take(std::move(E));
269e3a0bf50SVedant Kumar       std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
270e3a0bf50SVedant Kumar       bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
271e3a0bf50SVedant Kumar       handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
272fe90d86cSRong Xu                              FuncName, firstTime);
27398cce003SDavid Blaikie     });
274e3a0bf50SVedant Kumar   }
2750fcfe897SVedant Kumar   if (Reader->hasError())
2760fcfe897SVedant Kumar     if (Error E = Reader->getError())
2770fcfe897SVedant Kumar       WC->Errors.emplace_back(std::move(E), Filename);
278e3a0bf50SVedant Kumar }
279e3a0bf50SVedant Kumar 
280e3a0bf50SVedant Kumar /// Merge the \p Src writer context into \p Dst.
281e3a0bf50SVedant Kumar static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
2820fcfe897SVedant Kumar   for (auto &ErrorPair : Src->Errors)
2830fcfe897SVedant Kumar     Dst->Errors.push_back(std::move(ErrorPair));
2840fcfe897SVedant Kumar   Src->Errors.clear();
285faaa42adSVedant Kumar 
28698cce003SDavid Blaikie   Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
2870fcfe897SVedant Kumar     instrprof_error IPE = InstrProfError::take(std::move(E));
2880fcfe897SVedant Kumar     std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
2890fcfe897SVedant Kumar     bool firstTime = Dst->WriterErrorCodes.insert(IPE).second;
2900fcfe897SVedant Kumar     if (firstTime)
2910fcfe897SVedant Kumar       warn(toString(make_error<InstrProfError>(IPE)));
29298cce003SDavid Blaikie   });
293e3a0bf50SVedant Kumar }
294e3a0bf50SVedant Kumar 
29578fe6a3eSWei Mi static void writeInstrProfile(StringRef OutputFilename,
29678fe6a3eSWei Mi                               ProfileFormat OutputFormat,
29778fe6a3eSWei Mi                               InstrProfWriter &Writer) {
29878fe6a3eSWei Mi   std::error_code EC;
29978fe6a3eSWei Mi   raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None);
30078fe6a3eSWei Mi   if (EC)
30178fe6a3eSWei Mi     exitWithErrorCode(EC, OutputFilename);
30278fe6a3eSWei Mi 
30378fe6a3eSWei Mi   if (OutputFormat == PF_Text) {
30478fe6a3eSWei Mi     if (Error E = Writer.writeText(Output))
30578fe6a3eSWei Mi       exitWithError(std::move(E));
30678fe6a3eSWei Mi   } else {
30778fe6a3eSWei Mi     Writer.write(Output);
30878fe6a3eSWei Mi   }
30978fe6a3eSWei Mi }
31078fe6a3eSWei Mi 
3117f5b47ddSNathan Slingerland static void mergeInstrProfile(const WeightedFileVector &Inputs,
3123164fcfdSRichard Smith                               SymbolRemapper *Remapper,
3136f7c19a4SXinliang David Li                               StringRef OutputFilename,
314e3a0bf50SVedant Kumar                               ProfileFormat OutputFormat, bool OutputSparse,
3150fcfe897SVedant Kumar                               unsigned NumThreads, FailureMode FailMode) {
316b7aa2630SJustin Bogner   if (OutputFilename.compare("-") == 0)
317b7aa2630SJustin Bogner     exitWithError("Cannot write indexed profdata format to stdout.");
318ec49f982SJustin Bogner 
319d9be2c7eSWei Mi   if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
320be907324SWei Mi       OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text)
3216f7c19a4SXinliang David Li     exitWithError("Unknown format is specified.");
3226f7c19a4SXinliang David Li 
323e3a0bf50SVedant Kumar   std::mutex ErrorLock;
3249152fd17SVedant Kumar   SmallSet<instrprof_error, 4> WriterErrorCodes;
325e3a0bf50SVedant Kumar 
326e3a0bf50SVedant Kumar   // If NumThreads is not specified, auto-detect a good default.
327e3a0bf50SVedant Kumar   if (NumThreads == 0)
3288404aeb5SAlexandre Ganea     NumThreads = std::min(hardware_concurrency().compute_thread_count(),
3298404aeb5SAlexandre Ganea                           unsigned((Inputs.size() + 1) / 2));
3308404aeb5SAlexandre Ganea   // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails
3318404aeb5SAlexandre Ganea   // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't
3328404aeb5SAlexandre Ganea   // merged, thus the emitted file ends up with a PF_Unknown kind.
333e3a0bf50SVedant Kumar 
334e3a0bf50SVedant Kumar   // Initialize the writer contexts.
335e3a0bf50SVedant Kumar   SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
336e3a0bf50SVedant Kumar   for (unsigned I = 0; I < NumThreads; ++I)
3370eaee545SJonas Devlieghere     Contexts.emplace_back(std::make_unique<WriterContext>(
338e3a0bf50SVedant Kumar         OutputSparse, ErrorLock, WriterErrorCodes));
339e3a0bf50SVedant Kumar 
340e3a0bf50SVedant Kumar   if (NumThreads == 1) {
341e3a0bf50SVedant Kumar     for (const auto &Input : Inputs)
3423164fcfdSRichard Smith       loadInput(Input, Remapper, Contexts[0].get());
343e3a0bf50SVedant Kumar   } else {
3448404aeb5SAlexandre Ganea     ThreadPool Pool(hardware_concurrency(NumThreads));
345e3a0bf50SVedant Kumar 
346e3a0bf50SVedant Kumar     // Load the inputs in parallel (N/NumThreads serial steps).
347e3a0bf50SVedant Kumar     unsigned Ctx = 0;
3487f5b47ddSNathan Slingerland     for (const auto &Input : Inputs) {
3493164fcfdSRichard Smith       Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get());
350e3a0bf50SVedant Kumar       Ctx = (Ctx + 1) % NumThreads;
351e3a0bf50SVedant Kumar     }
352e3a0bf50SVedant Kumar     Pool.wait();
35321ab20e0SVedant Kumar 
354e3a0bf50SVedant Kumar     // Merge the writer contexts together (~ lg(NumThreads) serial steps).
355e3a0bf50SVedant Kumar     unsigned Mid = Contexts.size() / 2;
356e3a0bf50SVedant Kumar     unsigned End = Contexts.size();
357e3a0bf50SVedant Kumar     assert(Mid > 0 && "Expected more than one context");
358e3a0bf50SVedant Kumar     do {
359e3a0bf50SVedant Kumar       for (unsigned I = 0; I < Mid; ++I)
360e3a0bf50SVedant Kumar         Pool.async(mergeWriterContexts, Contexts[I].get(),
361e3a0bf50SVedant Kumar                    Contexts[I + Mid].get());
362e3a0bf50SVedant Kumar       Pool.wait();
363e3a0bf50SVedant Kumar       if (End & 1) {
364e3a0bf50SVedant Kumar         Pool.async(mergeWriterContexts, Contexts[0].get(),
365e3a0bf50SVedant Kumar                    Contexts[End - 1].get());
366e3a0bf50SVedant Kumar         Pool.wait();
367e3a0bf50SVedant Kumar       }
368e3a0bf50SVedant Kumar       End = Mid;
369e3a0bf50SVedant Kumar       Mid /= 2;
370e3a0bf50SVedant Kumar     } while (Mid > 0);
371e3a0bf50SVedant Kumar   }
37221ab20e0SVedant Kumar 
3730fcfe897SVedant Kumar   // Handle deferred errors encountered during merging. If the number of errors
3740fcfe897SVedant Kumar   // is equal to the number of inputs the merge failed.
3750fcfe897SVedant Kumar   unsigned NumErrors = 0;
376188efda5SVedant Kumar   for (std::unique_ptr<WriterContext> &WC : Contexts) {
3770fcfe897SVedant Kumar     for (auto &ErrorPair : WC->Errors) {
3780fcfe897SVedant Kumar       ++NumErrors;
3790fcfe897SVedant Kumar       warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
380188efda5SVedant Kumar     }
3810fcfe897SVedant Kumar   }
3820fcfe897SVedant Kumar   if (NumErrors == Inputs.size() ||
3830fcfe897SVedant Kumar       (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
3840fcfe897SVedant Kumar     exitWithError("No profiles could be merged.");
385188efda5SVedant Kumar 
38678fe6a3eSWei Mi   writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
387b5794ca9SVedant Kumar }
388d5336ae2SDiego Novillo 
389a23f6234SWei Mi /// The profile entry for a function in instrumentation profile.
390a23f6234SWei Mi struct InstrProfileEntry {
391a23f6234SWei Mi   uint64_t MaxCount = 0;
392a23f6234SWei Mi   float ZeroCounterRatio = 0.0;
393a23f6234SWei Mi   InstrProfRecord *ProfRecord;
394a23f6234SWei Mi   InstrProfileEntry(InstrProfRecord *Record);
395a23f6234SWei Mi   InstrProfileEntry() = default;
396a23f6234SWei Mi };
397a23f6234SWei Mi 
398a23f6234SWei Mi InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
399a23f6234SWei Mi   ProfRecord = Record;
400a23f6234SWei Mi   uint64_t CntNum = Record->Counts.size();
401a23f6234SWei Mi   uint64_t ZeroCntNum = 0;
402a23f6234SWei Mi   for (size_t I = 0; I < CntNum; ++I) {
403a23f6234SWei Mi     MaxCount = std::max(MaxCount, Record->Counts[I]);
404a23f6234SWei Mi     ZeroCntNum += !Record->Counts[I];
405a23f6234SWei Mi   }
406a23f6234SWei Mi   ZeroCounterRatio = (float)ZeroCntNum / CntNum;
407a23f6234SWei Mi }
408a23f6234SWei Mi 
409a23f6234SWei Mi /// Either set all the counters in the instr profile entry \p IFE to -1
410a23f6234SWei Mi /// in order to drop the profile or scale up the counters in \p IFP to
411a23f6234SWei Mi /// be above hot threshold. We use the ratio of zero counters in the
412a23f6234SWei Mi /// profile of a function to decide the profile is helpful or harmful
413a23f6234SWei Mi /// for performance, and to choose whether to scale up or drop it.
414a23f6234SWei Mi static void updateInstrProfileEntry(InstrProfileEntry &IFE,
415a23f6234SWei Mi                                     uint64_t HotInstrThreshold,
416a23f6234SWei Mi                                     float ZeroCounterThreshold) {
417a23f6234SWei Mi   InstrProfRecord *ProfRecord = IFE.ProfRecord;
418a23f6234SWei Mi   if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
419a23f6234SWei Mi     // If all or most of the counters of the function are zero, the
420a23f6234SWei Mi     // profile is unaccountable and shuld be dropped. Reset all the
421a23f6234SWei Mi     // counters to be -1 and PGO profile-use will drop the profile.
422a23f6234SWei Mi     // All counters being -1 also implies that the function is hot so
423a23f6234SWei Mi     // PGO profile-use will also set the entry count metadata to be
424a23f6234SWei Mi     // above hot threshold.
425a23f6234SWei Mi     for (size_t I = 0; I < ProfRecord->Counts.size(); ++I)
426a23f6234SWei Mi       ProfRecord->Counts[I] = -1;
427a23f6234SWei Mi     return;
428a23f6234SWei Mi   }
429a23f6234SWei Mi 
430a23f6234SWei Mi   // Scale up the MaxCount to be multiple times above hot threshold.
431a23f6234SWei Mi   const unsigned MultiplyFactor = 3;
432a23f6234SWei Mi   uint64_t Numerator = HotInstrThreshold * MultiplyFactor;
433a23f6234SWei Mi   uint64_t Denominator = IFE.MaxCount;
434a23f6234SWei Mi   ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
435a23f6234SWei Mi     warn(toString(make_error<InstrProfError>(E)));
436a23f6234SWei Mi   });
437a23f6234SWei Mi }
438a23f6234SWei Mi 
439a23f6234SWei Mi const uint64_t ColdPercentileIdx = 15;
440a23f6234SWei Mi const uint64_t HotPercentileIdx = 11;
441a23f6234SWei Mi 
442a23f6234SWei Mi /// Adjust the instr profile in \p WC based on the sample profile in
443a23f6234SWei Mi /// \p Reader.
444a23f6234SWei Mi static void
445a23f6234SWei Mi adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
446a23f6234SWei Mi                    std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
447a23f6234SWei Mi                    unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
448a23f6234SWei Mi                    unsigned InstrProfColdThreshold) {
449a23f6234SWei Mi   // Function to its entry in instr profile.
450a23f6234SWei Mi   StringMap<InstrProfileEntry> InstrProfileMap;
451a23f6234SWei Mi   InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
452a23f6234SWei Mi   for (auto &PD : WC->Writer.getProfileData()) {
453a23f6234SWei Mi     // Populate IPBuilder.
454a23f6234SWei Mi     for (const auto &PDV : PD.getValue()) {
455a23f6234SWei Mi       InstrProfRecord Record = PDV.second;
456a23f6234SWei Mi       IPBuilder.addRecord(Record);
457a23f6234SWei Mi     }
458a23f6234SWei Mi 
459a23f6234SWei Mi     // If a function has multiple entries in instr profile, skip it.
460a23f6234SWei Mi     if (PD.getValue().size() != 1)
461a23f6234SWei Mi       continue;
462a23f6234SWei Mi 
463a23f6234SWei Mi     // Initialize InstrProfileMap.
464a23f6234SWei Mi     InstrProfRecord *R = &PD.getValue().begin()->second;
465a23f6234SWei Mi     InstrProfileMap[PD.getKey()] = InstrProfileEntry(R);
466a23f6234SWei Mi   }
467a23f6234SWei Mi 
468a23f6234SWei Mi   ProfileSummary InstrPS = *IPBuilder.getSummary();
469a23f6234SWei Mi   ProfileSummary SamplePS = Reader->getSummary();
470a23f6234SWei Mi 
471a23f6234SWei Mi   // Compute cold thresholds for instr profile and sample profile.
472a23f6234SWei Mi   uint64_t ColdSampleThreshold =
473a23f6234SWei Mi       ProfileSummaryBuilder::getEntryForPercentile(
474a23f6234SWei Mi           SamplePS.getDetailedSummary(),
475a23f6234SWei Mi           ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
476a23f6234SWei Mi           .MinCount;
477a23f6234SWei Mi   uint64_t HotInstrThreshold =
478a23f6234SWei Mi       ProfileSummaryBuilder::getEntryForPercentile(
479a23f6234SWei Mi           InstrPS.getDetailedSummary(),
480a23f6234SWei Mi           ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
481a23f6234SWei Mi           .MinCount;
482a23f6234SWei Mi   uint64_t ColdInstrThreshold =
483a23f6234SWei Mi       InstrProfColdThreshold
484a23f6234SWei Mi           ? InstrProfColdThreshold
485a23f6234SWei Mi           : ProfileSummaryBuilder::getEntryForPercentile(
486a23f6234SWei Mi                 InstrPS.getDetailedSummary(),
487a23f6234SWei Mi                 ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
488a23f6234SWei Mi                 .MinCount;
489a23f6234SWei Mi 
490a23f6234SWei Mi   // Find hot/warm functions in sample profile which is cold in instr profile
491a23f6234SWei Mi   // and adjust the profiles of those functions in the instr profile.
492a23f6234SWei Mi   for (const auto &PD : Reader->getProfiles()) {
493a23f6234SWei Mi     StringRef FName = PD.getKey();
494a23f6234SWei Mi     const sampleprof::FunctionSamples &FS = PD.getValue();
495a23f6234SWei Mi     auto It = InstrProfileMap.find(FName);
496a23f6234SWei Mi     if (FS.getHeadSamples() > ColdSampleThreshold &&
497a23f6234SWei Mi         It != InstrProfileMap.end() &&
498a23f6234SWei Mi         It->second.MaxCount <= ColdInstrThreshold &&
499a23f6234SWei Mi         FS.getBodySamples().size() >= SupplMinSizeThreshold) {
500a23f6234SWei Mi       updateInstrProfileEntry(It->second, HotInstrThreshold,
501a23f6234SWei Mi                               ZeroCounterThreshold);
502a23f6234SWei Mi     }
503a23f6234SWei Mi   }
504a23f6234SWei Mi }
505a23f6234SWei Mi 
506a23f6234SWei Mi /// The main function to supplement instr profile with sample profile.
507a23f6234SWei Mi /// \Inputs contains the instr profile. \p SampleFilename specifies the
508a23f6234SWei Mi /// sample profile. \p OutputFilename specifies the output profile name.
509a23f6234SWei Mi /// \p OutputFormat specifies the output profile format. \p OutputSparse
510a23f6234SWei Mi /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
511a23f6234SWei Mi /// specifies the minimal size for the functions whose profile will be
512a23f6234SWei Mi /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
513a23f6234SWei Mi /// a function contains too many zero counters and whether its profile
514a23f6234SWei Mi /// should be dropped. \p InstrProfColdThreshold is the user specified
515a23f6234SWei Mi /// cold threshold which will override the cold threshold got from the
516a23f6234SWei Mi /// instr profile summary.
517a23f6234SWei Mi static void supplementInstrProfile(
518a23f6234SWei Mi     const WeightedFileVector &Inputs, StringRef SampleFilename,
519a23f6234SWei Mi     StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse,
520a23f6234SWei Mi     unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
521a23f6234SWei Mi     unsigned InstrProfColdThreshold) {
522a23f6234SWei Mi   if (OutputFilename.compare("-") == 0)
523a23f6234SWei Mi     exitWithError("Cannot write indexed profdata format to stdout.");
524a23f6234SWei Mi   if (Inputs.size() != 1)
525a23f6234SWei Mi     exitWithError("Expect one input to be an instr profile.");
526a23f6234SWei Mi   if (Inputs[0].Weight != 1)
527a23f6234SWei Mi     exitWithError("Expect instr profile doesn't have weight.");
528a23f6234SWei Mi 
529a23f6234SWei Mi   StringRef InstrFilename = Inputs[0].Filename;
530a23f6234SWei Mi 
531a23f6234SWei Mi   // Read sample profile.
532a23f6234SWei Mi   LLVMContext Context;
533a23f6234SWei Mi   auto ReaderOrErr =
534a23f6234SWei Mi       sampleprof::SampleProfileReader::create(SampleFilename.str(), Context);
535a23f6234SWei Mi   if (std::error_code EC = ReaderOrErr.getError())
536a23f6234SWei Mi     exitWithErrorCode(EC, SampleFilename);
537a23f6234SWei Mi   auto Reader = std::move(ReaderOrErr.get());
538a23f6234SWei Mi   if (std::error_code EC = Reader->read())
539a23f6234SWei Mi     exitWithErrorCode(EC, SampleFilename);
540a23f6234SWei Mi 
541a23f6234SWei Mi   // Read instr profile.
542a23f6234SWei Mi   std::mutex ErrorLock;
543a23f6234SWei Mi   SmallSet<instrprof_error, 4> WriterErrorCodes;
544a23f6234SWei Mi   auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
545a23f6234SWei Mi                                             WriterErrorCodes);
546a23f6234SWei Mi   loadInput(Inputs[0], nullptr, WC.get());
547a23f6234SWei Mi   if (WC->Errors.size() > 0)
548a23f6234SWei Mi     exitWithError(std::move(WC->Errors[0].first), InstrFilename);
549a23f6234SWei Mi 
550a23f6234SWei Mi   adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
551a23f6234SWei Mi                      InstrProfColdThreshold);
552a23f6234SWei Mi   writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
553a23f6234SWei Mi }
554a23f6234SWei Mi 
5553164fcfdSRichard Smith /// Make a copy of the given function samples with all symbol names remapped
5563164fcfdSRichard Smith /// by the provided symbol remapper.
5573164fcfdSRichard Smith static sampleprof::FunctionSamples
5583164fcfdSRichard Smith remapSamples(const sampleprof::FunctionSamples &Samples,
5593164fcfdSRichard Smith              SymbolRemapper &Remapper, sampleprof_error &Error) {
5603164fcfdSRichard Smith   sampleprof::FunctionSamples Result;
5613164fcfdSRichard Smith   Result.setName(Remapper(Samples.getName()));
5623164fcfdSRichard Smith   Result.addTotalSamples(Samples.getTotalSamples());
5633164fcfdSRichard Smith   Result.addHeadSamples(Samples.getHeadSamples());
5643164fcfdSRichard Smith   for (const auto &BodySample : Samples.getBodySamples()) {
5653164fcfdSRichard Smith     Result.addBodySamples(BodySample.first.LineOffset,
5663164fcfdSRichard Smith                           BodySample.first.Discriminator,
5673164fcfdSRichard Smith                           BodySample.second.getSamples());
5683164fcfdSRichard Smith     for (const auto &Target : BodySample.second.getCallTargets()) {
5693164fcfdSRichard Smith       Result.addCalledTargetSamples(BodySample.first.LineOffset,
5703164fcfdSRichard Smith                                     BodySample.first.Discriminator,
5713164fcfdSRichard Smith                                     Remapper(Target.first()), Target.second);
5723164fcfdSRichard Smith     }
5733164fcfdSRichard Smith   }
5743164fcfdSRichard Smith   for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
5753164fcfdSRichard Smith     sampleprof::FunctionSamplesMap &Target =
5763164fcfdSRichard Smith         Result.functionSamplesAt(CallsiteSamples.first);
5773164fcfdSRichard Smith     for (const auto &Callsite : CallsiteSamples.second) {
5783164fcfdSRichard Smith       sampleprof::FunctionSamples Remapped =
5793164fcfdSRichard Smith           remapSamples(Callsite.second, Remapper, Error);
580adcd0268SBenjamin Kramer       MergeResult(Error,
581adcd0268SBenjamin Kramer                   Target[std::string(Remapped.getName())].merge(Remapped));
5823164fcfdSRichard Smith     }
5833164fcfdSRichard Smith   }
5843164fcfdSRichard Smith   return Result;
5853164fcfdSRichard Smith }
5863164fcfdSRichard Smith 
5876f7c19a4SXinliang David Li static sampleprof::SampleProfileFormat FormatMap[] = {
588be907324SWei Mi     sampleprof::SPF_None,
589be907324SWei Mi     sampleprof::SPF_Text,
590be907324SWei Mi     sampleprof::SPF_Compact_Binary,
591be907324SWei Mi     sampleprof::SPF_Ext_Binary,
592be907324SWei Mi     sampleprof::SPF_GCC,
593be907324SWei Mi     sampleprof::SPF_Binary};
5946f7c19a4SXinliang David Li 
595798e59b8SWei Mi static std::unique_ptr<MemoryBuffer>
596798e59b8SWei Mi getInputFileBuf(const StringRef &InputFile) {
597798e59b8SWei Mi   if (InputFile == "")
598798e59b8SWei Mi     return {};
599798e59b8SWei Mi 
600798e59b8SWei Mi   auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
601798e59b8SWei Mi   if (!BufOrError)
602798e59b8SWei Mi     exitWithErrorCode(BufOrError.getError(), InputFile);
603798e59b8SWei Mi 
604798e59b8SWei Mi   return std::move(*BufOrError);
605798e59b8SWei Mi }
606798e59b8SWei Mi 
607798e59b8SWei Mi static void populateProfileSymbolList(MemoryBuffer *Buffer,
608798e59b8SWei Mi                                       sampleprof::ProfileSymbolList &PSL) {
609798e59b8SWei Mi   if (!Buffer)
610798e59b8SWei Mi     return;
611798e59b8SWei Mi 
612798e59b8SWei Mi   SmallVector<StringRef, 32> SymbolVec;
613798e59b8SWei Mi   StringRef Data = Buffer->getBuffer();
614798e59b8SWei Mi   Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
615798e59b8SWei Mi 
616798e59b8SWei Mi   for (StringRef symbol : SymbolVec)
617798e59b8SWei Mi     PSL.add(symbol);
618798e59b8SWei Mi }
619798e59b8SWei Mi 
620b523790aSWei Mi static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
621b523790aSWei Mi                                   ProfileFormat OutputFormat,
622b523790aSWei Mi                                   MemoryBuffer *Buffer,
623b523790aSWei Mi                                   sampleprof::ProfileSymbolList &WriterList,
624b49eac71SWei Mi                                   bool CompressAllSections, bool UseMD5,
62556926ae0SWei Mi                                   bool GenPartialProfile) {
626b523790aSWei Mi   populateProfileSymbolList(Buffer, WriterList);
627b523790aSWei Mi   if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
628b523790aSWei Mi     warn("Profile Symbol list is not empty but the output format is not "
629b523790aSWei Mi          "ExtBinary format. The list will be lost in the output. ");
630b523790aSWei Mi 
631b523790aSWei Mi   Writer.setProfileSymbolList(&WriterList);
632b523790aSWei Mi 
633b523790aSWei Mi   if (CompressAllSections) {
634ebad6788SWei Mi     if (OutputFormat != PF_Ext_Binary)
635b523790aSWei Mi       warn("-compress-all-section is ignored. Specify -extbinary to enable it");
636ebad6788SWei Mi     else
637ebad6788SWei Mi       Writer.setToCompressAllSections();
638b523790aSWei Mi   }
639ebad6788SWei Mi   if (UseMD5) {
640ebad6788SWei Mi     if (OutputFormat != PF_Ext_Binary)
641ebad6788SWei Mi       warn("-use-md5 is ignored. Specify -extbinary to enable it");
642ebad6788SWei Mi     else
643ebad6788SWei Mi       Writer.setUseMD5();
644b523790aSWei Mi   }
64556926ae0SWei Mi   if (GenPartialProfile) {
646b49eac71SWei Mi     if (OutputFormat != PF_Ext_Binary)
64756926ae0SWei Mi       warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
648b49eac71SWei Mi     else
649b49eac71SWei Mi       Writer.setPartialProfile();
650b49eac71SWei Mi   }
651b523790aSWei Mi }
652b523790aSWei Mi 
653ebad6788SWei Mi static void
654ebad6788SWei Mi mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
655ebad6788SWei Mi                    StringRef OutputFilename, ProfileFormat OutputFormat,
656ebad6788SWei Mi                    StringRef ProfileSymbolListFile, bool CompressAllSections,
65756926ae0SWei Mi                    bool UseMD5, bool GenPartialProfile, FailureMode FailMode) {
658d5336ae2SDiego Novillo   using namespace sampleprof;
659d5336ae2SDiego Novillo   StringMap<FunctionSamples> ProfileMap;
660aae1ed8eSDiego Novillo   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
66103b42e41SMehdi Amini   LLVMContext Context;
662798e59b8SWei Mi   sampleprof::ProfileSymbolList WriterList;
6637f5b47ddSNathan Slingerland   for (const auto &Input : Inputs) {
66403b42e41SMehdi Amini     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
6650fcfe897SVedant Kumar     if (std::error_code EC = ReaderOrErr.getError()) {
6660fcfe897SVedant Kumar       warnOrExitGivenError(FailMode, EC, Input.Filename);
6670fcfe897SVedant Kumar       continue;
6680fcfe897SVedant Kumar     }
669d5336ae2SDiego Novillo 
670aae1ed8eSDiego Novillo     // We need to keep the readers around until after all the files are
671aae1ed8eSDiego Novillo     // read so that we do not lose the function names stored in each
672aae1ed8eSDiego Novillo     // reader's memory. The function names are needed to write out the
673aae1ed8eSDiego Novillo     // merged profile map.
674aae1ed8eSDiego Novillo     Readers.push_back(std::move(ReaderOrErr.get()));
675aae1ed8eSDiego Novillo     const auto Reader = Readers.back().get();
6760fcfe897SVedant Kumar     if (std::error_code EC = Reader->read()) {
6770fcfe897SVedant Kumar       warnOrExitGivenError(FailMode, EC, Input.Filename);
6780fcfe897SVedant Kumar       Readers.pop_back();
6790fcfe897SVedant Kumar       continue;
6800fcfe897SVedant Kumar     }
681d5336ae2SDiego Novillo 
682d5336ae2SDiego Novillo     StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
683d5336ae2SDiego Novillo     for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
684d5336ae2SDiego Novillo                                               E = Profiles.end();
685d5336ae2SDiego Novillo          I != E; ++I) {
6863164fcfdSRichard Smith       sampleprof_error Result = sampleprof_error::success;
6873164fcfdSRichard Smith       FunctionSamples Remapped =
6883164fcfdSRichard Smith           Remapper ? remapSamples(I->second, *Remapper, Result)
6893164fcfdSRichard Smith                    : FunctionSamples();
6903164fcfdSRichard Smith       FunctionSamples &Samples = Remapper ? Remapped : I->second;
6913164fcfdSRichard Smith       StringRef FName = Samples.getName();
6923164fcfdSRichard Smith       MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight));
69348dd080cSNathan Slingerland       if (Result != sampleprof_error::success) {
69448dd080cSNathan Slingerland         std::error_code EC = make_error_code(Result);
6959152fd17SVedant Kumar         handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
69648dd080cSNathan Slingerland       }
697d5336ae2SDiego Novillo     }
698798e59b8SWei Mi 
699798e59b8SWei Mi     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
700798e59b8SWei Mi         Reader->getProfileSymbolList();
701798e59b8SWei Mi     if (ReaderList)
702798e59b8SWei Mi       WriterList.merge(*ReaderList);
703d5336ae2SDiego Novillo   }
704f0d3dcecSRong Xu   auto WriterOrErr =
705f0d3dcecSRong Xu       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
706f0d3dcecSRong Xu   if (std::error_code EC = WriterOrErr.getError())
707f0d3dcecSRong Xu     exitWithErrorCode(EC, OutputFilename);
708f0d3dcecSRong Xu 
709b523790aSWei Mi   auto Writer = std::move(WriterOrErr.get());
710798e59b8SWei Mi   // WriterList will have StringRef refering to string in Buffer.
711798e59b8SWei Mi   // Make sure Buffer lives as long as WriterList.
712798e59b8SWei Mi   auto Buffer = getInputFileBuf(ProfileSymbolListFile);
713b523790aSWei Mi   handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
71456926ae0SWei Mi                         CompressAllSections, UseMD5, GenPartialProfile);
715d5336ae2SDiego Novillo   Writer->write(ProfileMap);
716d5336ae2SDiego Novillo }
717d5336ae2SDiego Novillo 
7187f5b47ddSNathan Slingerland static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
7198d0e861eSVedant Kumar   StringRef WeightStr, FileName;
7208d0e861eSVedant Kumar   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
721d5336ae2SDiego Novillo 
7227f5b47ddSNathan Slingerland   uint64_t Weight;
7237f5b47ddSNathan Slingerland   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
7247f5b47ddSNathan Slingerland     exitWithError("Input weight must be a positive integer.");
7257f5b47ddSNathan Slingerland 
726adcd0268SBenjamin Kramer   return {std::string(FileName), Weight};
7277f5b47ddSNathan Slingerland }
7287f5b47ddSNathan Slingerland 
7299a1bfcfaSXinliang David Li static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
7309a1bfcfaSXinliang David Li   StringRef Filename = WF.Filename;
7319a1bfcfaSXinliang David Li   uint64_t Weight = WF.Weight;
732a81f4728SBenjamin Kramer 
733a81f4728SBenjamin Kramer   // If it's STDIN just pass it on.
734a81f4728SBenjamin Kramer   if (Filename == "-") {
735adcd0268SBenjamin Kramer     WNI.push_back({std::string(Filename), Weight});
736a81f4728SBenjamin Kramer     return;
737a81f4728SBenjamin Kramer   }
738a81f4728SBenjamin Kramer 
7399a1bfcfaSXinliang David Li   llvm::sys::fs::file_status Status;
7409a1bfcfaSXinliang David Li   llvm::sys::fs::status(Filename, Status);
7419a1bfcfaSXinliang David Li   if (!llvm::sys::fs::exists(Status))
7429a1bfcfaSXinliang David Li     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
7439a1bfcfaSXinliang David Li                       Filename);
7449a1bfcfaSXinliang David Li   // If it's a source file, collect it.
7459a1bfcfaSXinliang David Li   if (llvm::sys::fs::is_regular_file(Status)) {
746adcd0268SBenjamin Kramer     WNI.push_back({std::string(Filename), Weight});
7479a1bfcfaSXinliang David Li     return;
7489a1bfcfaSXinliang David Li   }
7499a1bfcfaSXinliang David Li 
7509a1bfcfaSXinliang David Li   if (llvm::sys::fs::is_directory(Status)) {
7519a1bfcfaSXinliang David Li     std::error_code EC;
7529a1bfcfaSXinliang David Li     for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
7539a1bfcfaSXinliang David Li          F != E && !EC; F.increment(EC)) {
7549a1bfcfaSXinliang David Li       if (llvm::sys::fs::is_regular_file(F->path())) {
7559a1bfcfaSXinliang David Li         addWeightedInput(WNI, {F->path(), Weight});
7569a1bfcfaSXinliang David Li       }
7579a1bfcfaSXinliang David Li     }
7589a1bfcfaSXinliang David Li     if (EC)
7599a1bfcfaSXinliang David Li       exitWithErrorCode(EC, Filename);
7609a1bfcfaSXinliang David Li   }
7619a1bfcfaSXinliang David Li }
7629a1bfcfaSXinliang David Li 
763cef4360aSVedant Kumar static void parseInputFilenamesFile(MemoryBuffer *Buffer,
764cef4360aSVedant Kumar                                     WeightedFileVector &WFV) {
765cef4360aSVedant Kumar   if (!Buffer)
766cef4360aSVedant Kumar     return;
767cef4360aSVedant Kumar 
768cef4360aSVedant Kumar   SmallVector<StringRef, 8> Entries;
769cef4360aSVedant Kumar   StringRef Data = Buffer->getBuffer();
770cef4360aSVedant Kumar   Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
771cef4360aSVedant Kumar   for (const StringRef &FileWeightEntry : Entries) {
772cef4360aSVedant Kumar     StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
773cef4360aSVedant Kumar     // Skip comments.
774cef4360aSVedant Kumar     if (SanitizedEntry.startswith("#"))
775cef4360aSVedant Kumar       continue;
776cef4360aSVedant Kumar     // If there's no comma, it's an unweighted profile.
777cef4360aSVedant Kumar     else if (SanitizedEntry.find(',') == StringRef::npos)
778adcd0268SBenjamin Kramer       addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
779cef4360aSVedant Kumar     else
7809a1bfcfaSXinliang David Li       addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
781cef4360aSVedant Kumar   }
782cef4360aSVedant Kumar }
783cef4360aSVedant Kumar 
7847f5b47ddSNathan Slingerland static int merge_main(int argc, const char *argv[]) {
7857f5b47ddSNathan Slingerland   cl::list<std::string> InputFilenames(cl::Positional,
7867f5b47ddSNathan Slingerland                                        cl::desc("<filename...>"));
7877f5b47ddSNathan Slingerland   cl::list<std::string> WeightedInputFilenames("weighted-input",
7887f5b47ddSNathan Slingerland                                                cl::desc("<weight>,<filename>"));
789cef4360aSVedant Kumar   cl::opt<std::string> InputFilenamesFile(
790cef4360aSVedant Kumar       "input-files", cl::init(""),
791cef4360aSVedant Kumar       cl::desc("Path to file containing newline-separated "
792cef4360aSVedant Kumar                "[<weight>,]<filename> entries"));
793cef4360aSVedant Kumar   cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
794cef4360aSVedant Kumar                                 cl::aliasopt(InputFilenamesFile));
795cef4360aSVedant Kumar   cl::opt<bool> DumpInputFileList(
796cef4360aSVedant Kumar       "dump-input-file-list", cl::init(false), cl::Hidden,
797cef4360aSVedant Kumar       cl::desc("Dump the list of input files and their weights, then exit"));
7983164fcfdSRichard Smith   cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
7993164fcfdSRichard Smith                                      cl::desc("Symbol remapping file"));
8003164fcfdSRichard Smith   cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
8013164fcfdSRichard Smith                            cl::aliasopt(RemappingFile));
802d5336ae2SDiego Novillo   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
803d5336ae2SDiego Novillo                                       cl::init("-"), cl::Required,
804d5336ae2SDiego Novillo                                       cl::desc("Output file"));
805d5336ae2SDiego Novillo   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
806d5336ae2SDiego Novillo                             cl::aliasopt(OutputFilename));
807d5336ae2SDiego Novillo   cl::opt<ProfileKinds> ProfileKind(
808d5336ae2SDiego Novillo       cl::desc("Profile kind:"), cl::init(instr),
809d5336ae2SDiego Novillo       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
810732afdd0SMehdi Amini                  clEnumVal(sample, "Sample profile")));
8116f7c19a4SXinliang David Li   cl::opt<ProfileFormat> OutputFormat(
812d9be2c7eSWei Mi       cl::desc("Format of output profile"), cl::init(PF_Binary),
813be907324SWei Mi       cl::values(
814be907324SWei Mi           clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
815a0c0857eSWei Mi           clEnumValN(PF_Compact_Binary, "compbinary",
816432db3b4SWei Mi                      "Compact binary encoding"),
817be907324SWei Mi           clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"),
8186f7c19a4SXinliang David Li           clEnumValN(PF_Text, "text", "Text encoding"),
8196f7c19a4SXinliang David Li           clEnumValN(PF_GCC, "gcc",
820732afdd0SMehdi Amini                      "GCC encoding (only meaningful for -sample)")));
8210fcfe897SVedant Kumar   cl::opt<FailureMode> FailureMode(
8220fcfe897SVedant Kumar       "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"),
8230fcfe897SVedant Kumar       cl::values(clEnumValN(failIfAnyAreInvalid, "any",
8240fcfe897SVedant Kumar                             "Fail if any profile is invalid."),
8250fcfe897SVedant Kumar                  clEnumValN(failIfAllAreInvalid, "all",
8260fcfe897SVedant Kumar                             "Fail only if all profiles are invalid.")));
82700dab228SVedant Kumar   cl::opt<bool> OutputSparse("sparse", cl::init(false),
82800dab228SVedant Kumar       cl::desc("Generate a sparse profile (only meaningful for -instr)"));
829e3a0bf50SVedant Kumar   cl::opt<unsigned> NumThreads(
830e3a0bf50SVedant Kumar       "num-threads", cl::init(0),
831e3a0bf50SVedant Kumar       cl::desc("Number of merge threads to use (default: autodetect)"));
832e3a0bf50SVedant Kumar   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
833e3a0bf50SVedant Kumar                         cl::aliasopt(NumThreads));
834798e59b8SWei Mi   cl::opt<std::string> ProfileSymbolListFile(
835798e59b8SWei Mi       "prof-sym-list", cl::init(""),
836798e59b8SWei Mi       cl::desc("Path to file containing the list of function symbols "
837798e59b8SWei Mi                "used to populate profile symbol list"));
838b523790aSWei Mi   cl::opt<bool> CompressAllSections(
839b523790aSWei Mi       "compress-all-sections", cl::init(false), cl::Hidden,
840b523790aSWei Mi       cl::desc("Compress all sections when writing the profile (only "
841b523790aSWei Mi                "meaningful for -extbinary)"));
842ebad6788SWei Mi   cl::opt<bool> UseMD5(
843ebad6788SWei Mi       "use-md5", cl::init(false), cl::Hidden,
844ebad6788SWei Mi       cl::desc("Choose to use MD5 to represent string in name table (only "
845ebad6788SWei Mi                "meaningful for -extbinary)"));
84656926ae0SWei Mi   cl::opt<bool> GenPartialProfile(
84756926ae0SWei Mi       "gen-partial-profile", cl::init(false), cl::Hidden,
84856926ae0SWei Mi       cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
849a23f6234SWei Mi   cl::opt<std::string> SupplInstrWithSample(
850a23f6234SWei Mi       "supplement-instr-with-sample", cl::init(""), cl::Hidden,
851a23f6234SWei Mi       cl::desc("Supplement an instr profile with sample profile, to correct "
852a23f6234SWei Mi                "the profile unrepresentativeness issue. The sample "
853a23f6234SWei Mi                "profile is the input of the flag. Output will be in instr "
854a23f6234SWei Mi                "format (The flag only works with -instr)"));
855a23f6234SWei Mi   cl::opt<float> ZeroCounterThreshold(
856a23f6234SWei Mi       "zero-counter-threshold", cl::init(0.7), cl::Hidden,
857a23f6234SWei Mi       cl::desc("For the function which is cold in instr profile but hot in "
858a23f6234SWei Mi                "sample profile, if the ratio of the number of zero counters "
859a23f6234SWei Mi                "divided by the the total number of counters is above the "
860a23f6234SWei Mi                "threshold, the profile of the function will be regarded as "
861a23f6234SWei Mi                "being harmful for performance and will be dropped. "));
862a23f6234SWei Mi   cl::opt<unsigned> SupplMinSizeThreshold(
863a23f6234SWei Mi       "suppl-min-size-threshold", cl::init(10), cl::Hidden,
864a23f6234SWei Mi       cl::desc("If the size of a function is smaller than the threshold, "
865a23f6234SWei Mi                "assume it can be inlined by PGO early inliner and it won't "
866a23f6234SWei Mi                "be adjusted based on sample profile. "));
867a23f6234SWei Mi   cl::opt<unsigned> InstrProfColdThreshold(
868a23f6234SWei Mi       "instr-prof-cold-threshold", cl::init(0), cl::Hidden,
869a23f6234SWei Mi       cl::desc("User specified cold threshold for instr profile which will "
870a23f6234SWei Mi                "override the cold threshold got from profile summary. "));
87100dab228SVedant Kumar 
872d5336ae2SDiego Novillo   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
873d5336ae2SDiego Novillo 
874cef4360aSVedant Kumar   WeightedFileVector WeightedInputs;
875cef4360aSVedant Kumar   for (StringRef Filename : InputFilenames)
876adcd0268SBenjamin Kramer     addWeightedInput(WeightedInputs, {std::string(Filename), 1});
877cef4360aSVedant Kumar   for (StringRef WeightedFilename : WeightedInputFilenames)
8789a1bfcfaSXinliang David Li     addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
879cef4360aSVedant Kumar 
880cef4360aSVedant Kumar   // Make sure that the file buffer stays alive for the duration of the
881cef4360aSVedant Kumar   // weighted input vector's lifetime.
882798e59b8SWei Mi   auto Buffer = getInputFileBuf(InputFilenamesFile);
883cef4360aSVedant Kumar   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
884cef4360aSVedant Kumar 
885cef4360aSVedant Kumar   if (WeightedInputs.empty())
8860c30f89cSChandler Carruth     exitWithError("No input files specified. See " +
8870c30f89cSChandler Carruth                   sys::path::filename(argv[0]) + " -help");
8880c30f89cSChandler Carruth 
889cef4360aSVedant Kumar   if (DumpInputFileList) {
890cef4360aSVedant Kumar     for (auto &WF : WeightedInputs)
891cef4360aSVedant Kumar       outs() << WF.Weight << "," << WF.Filename << "\n";
892cef4360aSVedant Kumar     return 0;
893cef4360aSVedant Kumar   }
894f771a050SVedant Kumar 
8953164fcfdSRichard Smith   std::unique_ptr<SymbolRemapper> Remapper;
8963164fcfdSRichard Smith   if (!RemappingFile.empty())
8973164fcfdSRichard Smith     Remapper = SymbolRemapper::create(RemappingFile);
8983164fcfdSRichard Smith 
899a23f6234SWei Mi   if (!SupplInstrWithSample.empty()) {
900a23f6234SWei Mi     if (ProfileKind != instr)
901a23f6234SWei Mi       exitWithError(
902a23f6234SWei Mi           "-supplement-instr-with-sample can only work with -instr. ");
903a23f6234SWei Mi 
904a23f6234SWei Mi     supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename,
905a23f6234SWei Mi                            OutputFormat, OutputSparse, SupplMinSizeThreshold,
906a23f6234SWei Mi                            ZeroCounterThreshold, InstrProfColdThreshold);
907a23f6234SWei Mi     return 0;
908a23f6234SWei Mi   }
909a23f6234SWei Mi 
910d5336ae2SDiego Novillo   if (ProfileKind == instr)
9113164fcfdSRichard Smith     mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename,
9120fcfe897SVedant Kumar                       OutputFormat, OutputSparse, NumThreads, FailureMode);
913d5336ae2SDiego Novillo   else
9143164fcfdSRichard Smith     mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
915b523790aSWei Mi                        OutputFormat, ProfileSymbolListFile, CompressAllSections,
91656926ae0SWei Mi                        UseMD5, GenPartialProfile, FailureMode);
917ec49f982SJustin Bogner 
918bfee8d49SJustin Bogner   return 0;
919bfee8d49SJustin Bogner }
920618bcea7SJustin Bogner 
921998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and profile TestFilename.
922998b97f6SRong Xu static void overlapInstrProfile(const std::string &BaseFilename,
923998b97f6SRong Xu                                 const std::string &TestFilename,
924998b97f6SRong Xu                                 const OverlapFuncFilters &FuncFilter,
925998b97f6SRong Xu                                 raw_fd_ostream &OS, bool IsCS) {
926998b97f6SRong Xu   std::mutex ErrorLock;
927998b97f6SRong Xu   SmallSet<instrprof_error, 4> WriterErrorCodes;
928998b97f6SRong Xu   WriterContext Context(false, ErrorLock, WriterErrorCodes);
929998b97f6SRong Xu   WeightedFile WeightedInput{BaseFilename, 1};
930998b97f6SRong Xu   OverlapStats Overlap;
931e0fa2689SRong Xu   Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
932998b97f6SRong Xu   if (E)
933998b97f6SRong Xu     exitWithError(std::move(E), "Error in getting profile count sums");
934998b97f6SRong Xu   if (Overlap.Base.CountSum < 1.0f) {
935998b97f6SRong Xu     OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
936998b97f6SRong Xu     exit(0);
937998b97f6SRong Xu   }
938998b97f6SRong Xu   if (Overlap.Test.CountSum < 1.0f) {
939998b97f6SRong Xu     OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
940998b97f6SRong Xu     exit(0);
941998b97f6SRong Xu   }
942998b97f6SRong Xu   loadInput(WeightedInput, nullptr, &Context);
943998b97f6SRong Xu   overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
944998b97f6SRong Xu                IsCS);
945998b97f6SRong Xu   Overlap.dump(OS);
946998b97f6SRong Xu }
947998b97f6SRong Xu 
948*540489deSweihe namespace {
949*540489deSweihe struct SampleOverlapStats {
950*540489deSweihe   StringRef BaseName;
951*540489deSweihe   StringRef TestName;
952*540489deSweihe   // Number of overlap units
953*540489deSweihe   uint64_t OverlapCount;
954*540489deSweihe   // Total samples of overlap units
955*540489deSweihe   uint64_t OverlapSample;
956*540489deSweihe   // Number of and total samples of units that only present in base or test
957*540489deSweihe   // profile
958*540489deSweihe   uint64_t BaseUniqueCount;
959*540489deSweihe   uint64_t BaseUniqueSample;
960*540489deSweihe   uint64_t TestUniqueCount;
961*540489deSweihe   uint64_t TestUniqueSample;
962*540489deSweihe   // Number of units and total samples in base or test profile
963*540489deSweihe   uint64_t BaseCount;
964*540489deSweihe   uint64_t BaseSample;
965*540489deSweihe   uint64_t TestCount;
966*540489deSweihe   uint64_t TestSample;
967*540489deSweihe   // Number of and total samples of units that present in at least one profile
968*540489deSweihe   uint64_t UnionCount;
969*540489deSweihe   uint64_t UnionSample;
970*540489deSweihe   // Weighted similarity
971*540489deSweihe   double Similarity;
972*540489deSweihe   // For SampleOverlapStats instances representing functions, weights of the
973*540489deSweihe   // function in base and test profiles
974*540489deSweihe   double BaseWeight;
975*540489deSweihe   double TestWeight;
976*540489deSweihe 
977*540489deSweihe   SampleOverlapStats()
978*540489deSweihe       : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0),
979*540489deSweihe         BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0),
980*540489deSweihe         BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0),
981*540489deSweihe         UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {}
982*540489deSweihe };
983*540489deSweihe } // end anonymous namespace
984*540489deSweihe 
985*540489deSweihe namespace {
986*540489deSweihe struct FuncSampleStats {
987*540489deSweihe   uint64_t SampleSum;
988*540489deSweihe   uint64_t MaxSample;
989*540489deSweihe   uint64_t HotBlockCount;
990*540489deSweihe   FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {}
991*540489deSweihe   FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
992*540489deSweihe                   uint64_t HotBlockCount)
993*540489deSweihe       : SampleSum(SampleSum), MaxSample(MaxSample),
994*540489deSweihe         HotBlockCount(HotBlockCount) {}
995*540489deSweihe };
996*540489deSweihe } // end anonymous namespace
997*540489deSweihe 
998*540489deSweihe namespace {
999*540489deSweihe enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1000*540489deSweihe 
1001*540489deSweihe // Class for updating merging steps for two sorted maps. The class should be
1002*540489deSweihe // instantiated with a map iterator type.
1003*540489deSweihe template <class T> class MatchStep {
1004*540489deSweihe public:
1005*540489deSweihe   MatchStep() = delete;
1006*540489deSweihe 
1007*540489deSweihe   MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1008*540489deSweihe       : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1009*540489deSweihe         SecondEnd(SecondEnd), Status(MS_None) {}
1010*540489deSweihe 
1011*540489deSweihe   bool areBothFinished() const {
1012*540489deSweihe     return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1013*540489deSweihe   }
1014*540489deSweihe 
1015*540489deSweihe   bool isFirstFinished() const { return FirstIter == FirstEnd; }
1016*540489deSweihe 
1017*540489deSweihe   bool isSecondFinished() const { return SecondIter == SecondEnd; }
1018*540489deSweihe 
1019*540489deSweihe   /// Advance one step based on the previous match status unless the previous
1020*540489deSweihe   /// status is MS_None. Then update Status based on the comparison between two
1021*540489deSweihe   /// container iterators at the current step. If the previous status is
1022*540489deSweihe   /// MS_None, it means two iterators are at the beginning and no comparison has
1023*540489deSweihe   /// been made, so we simply update Status without advancing the iterators.
1024*540489deSweihe   void updateOneStep();
1025*540489deSweihe 
1026*540489deSweihe   T getFirstIter() const { return FirstIter; }
1027*540489deSweihe 
1028*540489deSweihe   T getSecondIter() const { return SecondIter; }
1029*540489deSweihe 
1030*540489deSweihe   MatchStatus getMatchStatus() const { return Status; }
1031*540489deSweihe 
1032*540489deSweihe private:
1033*540489deSweihe   // Current iterator and end iterator of the first container.
1034*540489deSweihe   T FirstIter;
1035*540489deSweihe   T FirstEnd;
1036*540489deSweihe   // Current iterator and end iterator of the second container.
1037*540489deSweihe   T SecondIter;
1038*540489deSweihe   T SecondEnd;
1039*540489deSweihe   // Match status of the current step.
1040*540489deSweihe   MatchStatus Status;
1041*540489deSweihe };
1042*540489deSweihe } // end anonymous namespace
1043*540489deSweihe 
1044*540489deSweihe template <class T> void MatchStep<T>::updateOneStep() {
1045*540489deSweihe   switch (Status) {
1046*540489deSweihe   case MS_Match:
1047*540489deSweihe     ++FirstIter;
1048*540489deSweihe     ++SecondIter;
1049*540489deSweihe     break;
1050*540489deSweihe   case MS_FirstUnique:
1051*540489deSweihe     ++FirstIter;
1052*540489deSweihe     break;
1053*540489deSweihe   case MS_SecondUnique:
1054*540489deSweihe     ++SecondIter;
1055*540489deSweihe     break;
1056*540489deSweihe   case MS_None:
1057*540489deSweihe     break;
1058*540489deSweihe   }
1059*540489deSweihe 
1060*540489deSweihe   // Update Status according to iterators at the current step.
1061*540489deSweihe   if (areBothFinished())
1062*540489deSweihe     return;
1063*540489deSweihe   if (FirstIter != FirstEnd &&
1064*540489deSweihe       (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1065*540489deSweihe     Status = MS_FirstUnique;
1066*540489deSweihe   else if (SecondIter != SecondEnd &&
1067*540489deSweihe            (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1068*540489deSweihe     Status = MS_SecondUnique;
1069*540489deSweihe   else
1070*540489deSweihe     Status = MS_Match;
1071*540489deSweihe }
1072*540489deSweihe 
1073*540489deSweihe // Return the sum of line/block samples, the max line/block sample, and the
1074*540489deSweihe // number of line/block samples above the given threshold in a function
1075*540489deSweihe // including its inlinees.
1076*540489deSweihe static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1077*540489deSweihe                                FuncSampleStats &FuncStats,
1078*540489deSweihe                                uint64_t HotThreshold) {
1079*540489deSweihe   for (const auto &L : Func.getBodySamples()) {
1080*540489deSweihe     uint64_t Sample = L.second.getSamples();
1081*540489deSweihe     FuncStats.SampleSum += Sample;
1082*540489deSweihe     FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1083*540489deSweihe     if (Sample >= HotThreshold)
1084*540489deSweihe       ++FuncStats.HotBlockCount;
1085*540489deSweihe   }
1086*540489deSweihe 
1087*540489deSweihe   for (const auto &C : Func.getCallsiteSamples()) {
1088*540489deSweihe     for (const auto &F : C.second)
1089*540489deSweihe       getFuncSampleStats(F.second, FuncStats, HotThreshold);
1090*540489deSweihe   }
1091*540489deSweihe }
1092*540489deSweihe 
1093*540489deSweihe /// Predicate that determines if a function is hot with a given threshold. We
1094*540489deSweihe /// keep it separate from its callsites for possible extension in the future.
1095*540489deSweihe static bool isFunctionHot(const FuncSampleStats &FuncStats,
1096*540489deSweihe                           uint64_t HotThreshold) {
1097*540489deSweihe   // We intentionally compare the maximum sample count in a function with the
1098*540489deSweihe   // HotThreshold to get an approximate determination on hot functions.
1099*540489deSweihe   return (FuncStats.MaxSample >= HotThreshold);
1100*540489deSweihe }
1101*540489deSweihe 
1102*540489deSweihe namespace {
1103*540489deSweihe class SampleOverlapAggregator {
1104*540489deSweihe public:
1105*540489deSweihe   SampleOverlapAggregator(const std::string &BaseFilename,
1106*540489deSweihe                           const std::string &TestFilename,
1107*540489deSweihe                           double LowSimilarityThreshold, double Epsilon,
1108*540489deSweihe                           const OverlapFuncFilters &FuncFilter)
1109*540489deSweihe       : BaseFilename(BaseFilename), TestFilename(TestFilename),
1110*540489deSweihe         LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1111*540489deSweihe         FuncFilter(FuncFilter) {}
1112*540489deSweihe 
1113*540489deSweihe   /// Detect 0-sample input profile and report to output stream. This interface
1114*540489deSweihe   /// should be called after loadProfiles().
1115*540489deSweihe   bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1116*540489deSweihe 
1117*540489deSweihe   /// Write out function-level similarity statistics for functions specified by
1118*540489deSweihe   /// options --function, --value-cutoff, and --similarity-cutoff.
1119*540489deSweihe   void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1120*540489deSweihe 
1121*540489deSweihe   /// Write out program-level similarity and overlap statistics.
1122*540489deSweihe   void dumpProgramSummary(raw_fd_ostream &OS) const;
1123*540489deSweihe 
1124*540489deSweihe   /// Write out hot-function and hot-block statistics for base_profile,
1125*540489deSweihe   /// test_profile, and their overlap. For both cases, the overlap HO is
1126*540489deSweihe   /// calculated as follows:
1127*540489deSweihe   ///    Given the number of functions (or blocks) that are hot in both profiles
1128*540489deSweihe   ///    HCommon and the number of functions (or blocks) that are hot in at
1129*540489deSweihe   ///    least one profile HUnion, HO = HCommon / HUnion.
1130*540489deSweihe   void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1131*540489deSweihe 
1132*540489deSweihe   /// This function tries matching functions in base and test profiles. For each
1133*540489deSweihe   /// pair of matched functions, it aggregates the function-level
1134*540489deSweihe   /// similarity into a profile-level similarity. It also dump function-level
1135*540489deSweihe   /// similarity information of functions specified by --function,
1136*540489deSweihe   /// --value-cutoff, and --similarity-cutoff options. The program-level
1137*540489deSweihe   /// similarity PS is computed as follows:
1138*540489deSweihe   ///     Given function-level similarity FS(A) for all function A, the
1139*540489deSweihe   ///     weight of function A in base profile WB(A), and the weight of function
1140*540489deSweihe   ///     A in test profile WT(A), compute PS(base_profile, test_profile) =
1141*540489deSweihe   ///     sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1142*540489deSweihe   ///     meaning no-overlap.
1143*540489deSweihe   void computeSampleProfileOverlap(raw_fd_ostream &OS);
1144*540489deSweihe 
1145*540489deSweihe   /// Initialize ProfOverlap with the sum of samples in base and test
1146*540489deSweihe   /// profiles. This function also computes and keeps the sum of samples and
1147*540489deSweihe   /// max sample counts of each function in BaseStats and TestStats for later
1148*540489deSweihe   /// use to avoid re-computations.
1149*540489deSweihe   void initializeSampleProfileOverlap();
1150*540489deSweihe 
1151*540489deSweihe   /// Load profiles specified by BaseFilename and TestFilename.
1152*540489deSweihe   std::error_code loadProfiles();
1153*540489deSweihe 
1154*540489deSweihe private:
1155*540489deSweihe   SampleOverlapStats ProfOverlap;
1156*540489deSweihe   SampleOverlapStats HotFuncOverlap;
1157*540489deSweihe   SampleOverlapStats HotBlockOverlap;
1158*540489deSweihe   std::string BaseFilename;
1159*540489deSweihe   std::string TestFilename;
1160*540489deSweihe   std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1161*540489deSweihe   std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1162*540489deSweihe   // BaseStats and TestStats hold FuncSampleStats for each function, with
1163*540489deSweihe   // function name as the key.
1164*540489deSweihe   StringMap<FuncSampleStats> BaseStats;
1165*540489deSweihe   StringMap<FuncSampleStats> TestStats;
1166*540489deSweihe   // Low similarity threshold in floating point number
1167*540489deSweihe   double LowSimilarityThreshold;
1168*540489deSweihe   // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1169*540489deSweihe   // for tracking hot blocks.
1170*540489deSweihe   uint64_t BaseHotThreshold;
1171*540489deSweihe   uint64_t TestHotThreshold;
1172*540489deSweihe   // A small threshold used to round the results of floating point accumulations
1173*540489deSweihe   // to resolve imprecision.
1174*540489deSweihe   const double Epsilon;
1175*540489deSweihe   std::multimap<double, SampleOverlapStats, std::greater<double>>
1176*540489deSweihe       FuncSimilarityDump;
1177*540489deSweihe   // FuncFilter carries specifications in options --value-cutoff and
1178*540489deSweihe   // --function.
1179*540489deSweihe   OverlapFuncFilters FuncFilter;
1180*540489deSweihe   // Column offsets for printing the function-level details table.
1181*540489deSweihe   static const unsigned int TestWeightCol = 15;
1182*540489deSweihe   static const unsigned int SimilarityCol = 30;
1183*540489deSweihe   static const unsigned int OverlapCol = 43;
1184*540489deSweihe   static const unsigned int BaseUniqueCol = 53;
1185*540489deSweihe   static const unsigned int TestUniqueCol = 67;
1186*540489deSweihe   static const unsigned int BaseSampleCol = 81;
1187*540489deSweihe   static const unsigned int TestSampleCol = 96;
1188*540489deSweihe   static const unsigned int FuncNameCol = 111;
1189*540489deSweihe 
1190*540489deSweihe   /// Return a similarity of two line/block sample counters in the same
1191*540489deSweihe   /// function in base and test profiles. The line/block-similarity BS(i) is
1192*540489deSweihe   /// computed as follows:
1193*540489deSweihe   ///    For an offsets i, given the sample count at i in base profile BB(i),
1194*540489deSweihe   ///    the sample count at i in test profile BT(i), the sum of sample counts
1195*540489deSweihe   ///    in this function in base profile SB, and the sum of sample counts in
1196*540489deSweihe   ///    this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1197*540489deSweihe   ///    BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1198*540489deSweihe   double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1199*540489deSweihe                                 const SampleOverlapStats &FuncOverlap) const;
1200*540489deSweihe 
1201*540489deSweihe   void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1202*540489deSweihe                              uint64_t HotBlockCount);
1203*540489deSweihe 
1204*540489deSweihe   void getHotFunctions(const StringMap<FuncSampleStats> &ProfStats,
1205*540489deSweihe                        StringMap<FuncSampleStats> &HotFunc,
1206*540489deSweihe                        uint64_t HotThreshold) const;
1207*540489deSweihe 
1208*540489deSweihe   void computeHotFuncOverlap();
1209*540489deSweihe 
1210*540489deSweihe   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1211*540489deSweihe   /// Difference for two sample units in a matched function according to the
1212*540489deSweihe   /// given match status.
1213*540489deSweihe   void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
1214*540489deSweihe                                      uint64_t HotBlockCount,
1215*540489deSweihe                                      SampleOverlapStats &FuncOverlap,
1216*540489deSweihe                                      double &Difference, MatchStatus Status);
1217*540489deSweihe 
1218*540489deSweihe   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1219*540489deSweihe   /// Difference for unmatched callees that only present in one profile in a
1220*540489deSweihe   /// matched caller function.
1221*540489deSweihe   void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
1222*540489deSweihe                                 SampleOverlapStats &FuncOverlap,
1223*540489deSweihe                                 double &Difference, MatchStatus Status);
1224*540489deSweihe 
1225*540489deSweihe   /// This function updates sample overlap statistics of an overlap function in
1226*540489deSweihe   /// base and test profile. It also calculates a function-internal similarity
1227*540489deSweihe   /// FIS as follows:
1228*540489deSweihe   ///    For offsets i that have samples in at least one profile in this
1229*540489deSweihe   ///    function A, given BS(i) returned by computeBlockSimilarity(), compute
1230*540489deSweihe   ///    FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
1231*540489deSweihe   ///    0.0 meaning no overlap.
1232*540489deSweihe   double computeSampleFunctionInternalOverlap(
1233*540489deSweihe       const sampleprof::FunctionSamples &BaseFunc,
1234*540489deSweihe       const sampleprof::FunctionSamples &TestFunc,
1235*540489deSweihe       SampleOverlapStats &FuncOverlap);
1236*540489deSweihe 
1237*540489deSweihe   /// Function-level similarity (FS) is a weighted value over function internal
1238*540489deSweihe   /// similarity (FIS). This function computes a function's FS from its FIS by
1239*540489deSweihe   /// applying the weight.
1240*540489deSweihe   double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
1241*540489deSweihe                                  uint64_t TestFuncSample) const;
1242*540489deSweihe 
1243*540489deSweihe   /// The function-level similarity FS(A) for a function A is computed as
1244*540489deSweihe   /// follows:
1245*540489deSweihe   ///     Compute a function-internal similarity FIS(A) by
1246*540489deSweihe   ///     computeSampleFunctionInternalOverlap(). Then, with the weight of
1247*540489deSweihe   ///     function A in base profile WB(A), and the weight of function A in test
1248*540489deSweihe   ///     profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
1249*540489deSweihe   ///     ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
1250*540489deSweihe   double
1251*540489deSweihe   computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
1252*540489deSweihe                                const sampleprof::FunctionSamples *TestFunc,
1253*540489deSweihe                                SampleOverlapStats *FuncOverlap,
1254*540489deSweihe                                uint64_t BaseFuncSample,
1255*540489deSweihe                                uint64_t TestFuncSample);
1256*540489deSweihe 
1257*540489deSweihe   /// Profile-level similarity (PS) is a weighted aggregate over function-level
1258*540489deSweihe   /// similarities (FS). This method weights the FS value by the function
1259*540489deSweihe   /// weights in the base and test profiles for the aggregation.
1260*540489deSweihe   double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
1261*540489deSweihe                             uint64_t TestFuncSample) const;
1262*540489deSweihe };
1263*540489deSweihe } // end anonymous namespace
1264*540489deSweihe 
1265*540489deSweihe bool SampleOverlapAggregator::detectZeroSampleProfile(
1266*540489deSweihe     raw_fd_ostream &OS) const {
1267*540489deSweihe   bool HaveZeroSample = false;
1268*540489deSweihe   if (ProfOverlap.BaseSample == 0) {
1269*540489deSweihe     OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
1270*540489deSweihe     HaveZeroSample = true;
1271*540489deSweihe   }
1272*540489deSweihe   if (ProfOverlap.TestSample == 0) {
1273*540489deSweihe     OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
1274*540489deSweihe     HaveZeroSample = true;
1275*540489deSweihe   }
1276*540489deSweihe   return HaveZeroSample;
1277*540489deSweihe }
1278*540489deSweihe 
1279*540489deSweihe double SampleOverlapAggregator::computeBlockSimilarity(
1280*540489deSweihe     uint64_t BaseSample, uint64_t TestSample,
1281*540489deSweihe     const SampleOverlapStats &FuncOverlap) const {
1282*540489deSweihe   double BaseFrac = 0.0;
1283*540489deSweihe   double TestFrac = 0.0;
1284*540489deSweihe   if (FuncOverlap.BaseSample > 0)
1285*540489deSweihe     BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
1286*540489deSweihe   if (FuncOverlap.TestSample > 0)
1287*540489deSweihe     TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
1288*540489deSweihe   return 1.0 - std::fabs(BaseFrac - TestFrac);
1289*540489deSweihe }
1290*540489deSweihe 
1291*540489deSweihe void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
1292*540489deSweihe                                                     uint64_t TestSample,
1293*540489deSweihe                                                     uint64_t HotBlockCount) {
1294*540489deSweihe   bool IsBaseHot = (BaseSample >= BaseHotThreshold);
1295*540489deSweihe   bool IsTestHot = (TestSample >= TestHotThreshold);
1296*540489deSweihe   if (!IsBaseHot && !IsTestHot)
1297*540489deSweihe     return;
1298*540489deSweihe 
1299*540489deSweihe   HotBlockOverlap.UnionCount += HotBlockCount;
1300*540489deSweihe   if (IsBaseHot)
1301*540489deSweihe     HotBlockOverlap.BaseCount += HotBlockCount;
1302*540489deSweihe   if (IsTestHot)
1303*540489deSweihe     HotBlockOverlap.TestCount += HotBlockCount;
1304*540489deSweihe   if (IsBaseHot && IsTestHot)
1305*540489deSweihe     HotBlockOverlap.OverlapCount += HotBlockCount;
1306*540489deSweihe }
1307*540489deSweihe 
1308*540489deSweihe void SampleOverlapAggregator::getHotFunctions(
1309*540489deSweihe     const StringMap<FuncSampleStats> &ProfStats,
1310*540489deSweihe     StringMap<FuncSampleStats> &HotFunc, uint64_t HotThreshold) const {
1311*540489deSweihe   for (const auto &F : ProfStats) {
1312*540489deSweihe     if (isFunctionHot(F.second, HotThreshold))
1313*540489deSweihe       HotFunc.try_emplace(F.first(), F.second);
1314*540489deSweihe   }
1315*540489deSweihe }
1316*540489deSweihe 
1317*540489deSweihe void SampleOverlapAggregator::computeHotFuncOverlap() {
1318*540489deSweihe   StringMap<FuncSampleStats> BaseHotFunc;
1319*540489deSweihe   getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
1320*540489deSweihe   HotFuncOverlap.BaseCount = BaseHotFunc.size();
1321*540489deSweihe 
1322*540489deSweihe   StringMap<FuncSampleStats> TestHotFunc;
1323*540489deSweihe   getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
1324*540489deSweihe   HotFuncOverlap.TestCount = TestHotFunc.size();
1325*540489deSweihe   HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
1326*540489deSweihe 
1327*540489deSweihe   for (const auto &F : BaseHotFunc) {
1328*540489deSweihe     if (TestHotFunc.count(F.first()))
1329*540489deSweihe       ++HotFuncOverlap.OverlapCount;
1330*540489deSweihe     else
1331*540489deSweihe       ++HotFuncOverlap.UnionCount;
1332*540489deSweihe   }
1333*540489deSweihe }
1334*540489deSweihe 
1335*540489deSweihe void SampleOverlapAggregator::updateOverlapStatsForFunction(
1336*540489deSweihe     uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
1337*540489deSweihe     SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
1338*540489deSweihe   assert(Status != MS_None &&
1339*540489deSweihe          "Match status should be updated before updating overlap statistics");
1340*540489deSweihe   if (Status == MS_FirstUnique) {
1341*540489deSweihe     TestSample = 0;
1342*540489deSweihe     FuncOverlap.BaseUniqueSample += BaseSample;
1343*540489deSweihe   } else if (Status == MS_SecondUnique) {
1344*540489deSweihe     BaseSample = 0;
1345*540489deSweihe     FuncOverlap.TestUniqueSample += TestSample;
1346*540489deSweihe   } else {
1347*540489deSweihe     ++FuncOverlap.OverlapCount;
1348*540489deSweihe   }
1349*540489deSweihe 
1350*540489deSweihe   FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
1351*540489deSweihe   FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
1352*540489deSweihe   Difference +=
1353*540489deSweihe       1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
1354*540489deSweihe   updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
1355*540489deSweihe }
1356*540489deSweihe 
1357*540489deSweihe void SampleOverlapAggregator::updateForUnmatchedCallee(
1358*540489deSweihe     const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
1359*540489deSweihe     double &Difference, MatchStatus Status) {
1360*540489deSweihe   assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
1361*540489deSweihe          "Status must be either of the two unmatched cases");
1362*540489deSweihe   FuncSampleStats FuncStats;
1363*540489deSweihe   if (Status == MS_FirstUnique) {
1364*540489deSweihe     getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
1365*540489deSweihe     updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
1366*540489deSweihe                                   FuncStats.HotBlockCount, FuncOverlap,
1367*540489deSweihe                                   Difference, Status);
1368*540489deSweihe   } else {
1369*540489deSweihe     getFuncSampleStats(Func, FuncStats, TestHotThreshold);
1370*540489deSweihe     updateOverlapStatsForFunction(0, FuncStats.SampleSum,
1371*540489deSweihe                                   FuncStats.HotBlockCount, FuncOverlap,
1372*540489deSweihe                                   Difference, Status);
1373*540489deSweihe   }
1374*540489deSweihe }
1375*540489deSweihe 
1376*540489deSweihe double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
1377*540489deSweihe     const sampleprof::FunctionSamples &BaseFunc,
1378*540489deSweihe     const sampleprof::FunctionSamples &TestFunc,
1379*540489deSweihe     SampleOverlapStats &FuncOverlap) {
1380*540489deSweihe 
1381*540489deSweihe   using namespace sampleprof;
1382*540489deSweihe 
1383*540489deSweihe   double Difference = 0;
1384*540489deSweihe 
1385*540489deSweihe   // Accumulate Difference for regular line/block samples in the function.
1386*540489deSweihe   // We match them through sort-merge join algorithm because
1387*540489deSweihe   // FunctionSamples::getBodySamples() returns a map of sample counters ordered
1388*540489deSweihe   // by their offsets.
1389*540489deSweihe   MatchStep<BodySampleMap::const_iterator> BlockIterStep(
1390*540489deSweihe       BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
1391*540489deSweihe       TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
1392*540489deSweihe   BlockIterStep.updateOneStep();
1393*540489deSweihe   while (!BlockIterStep.areBothFinished()) {
1394*540489deSweihe     uint64_t BaseSample =
1395*540489deSweihe         BlockIterStep.isFirstFinished()
1396*540489deSweihe             ? 0
1397*540489deSweihe             : BlockIterStep.getFirstIter()->second.getSamples();
1398*540489deSweihe     uint64_t TestSample =
1399*540489deSweihe         BlockIterStep.isSecondFinished()
1400*540489deSweihe             ? 0
1401*540489deSweihe             : BlockIterStep.getSecondIter()->second.getSamples();
1402*540489deSweihe     updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
1403*540489deSweihe                                   Difference, BlockIterStep.getMatchStatus());
1404*540489deSweihe 
1405*540489deSweihe     BlockIterStep.updateOneStep();
1406*540489deSweihe   }
1407*540489deSweihe 
1408*540489deSweihe   // Accumulate Difference for callsite lines in the function. We match
1409*540489deSweihe   // them through sort-merge algorithm because
1410*540489deSweihe   // FunctionSamples::getCallsiteSamples() returns a map of callsite records
1411*540489deSweihe   // ordered by their offsets.
1412*540489deSweihe   MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
1413*540489deSweihe       BaseFunc.getCallsiteSamples().cbegin(),
1414*540489deSweihe       BaseFunc.getCallsiteSamples().cend(),
1415*540489deSweihe       TestFunc.getCallsiteSamples().cbegin(),
1416*540489deSweihe       TestFunc.getCallsiteSamples().cend());
1417*540489deSweihe   CallsiteIterStep.updateOneStep();
1418*540489deSweihe   while (!CallsiteIterStep.areBothFinished()) {
1419*540489deSweihe     MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
1420*540489deSweihe     assert(CallsiteStepStatus != MS_None &&
1421*540489deSweihe            "Match status should be updated before entering loop body");
1422*540489deSweihe 
1423*540489deSweihe     if (CallsiteStepStatus != MS_Match) {
1424*540489deSweihe       auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
1425*540489deSweihe                           ? CallsiteIterStep.getFirstIter()
1426*540489deSweihe                           : CallsiteIterStep.getSecondIter();
1427*540489deSweihe       for (const auto &F : Callsite->second)
1428*540489deSweihe         updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
1429*540489deSweihe                                  CallsiteStepStatus);
1430*540489deSweihe     } else {
1431*540489deSweihe       // There may be multiple inlinees at the same offset, so we need to try
1432*540489deSweihe       // matching all of them. This match is implemented through sort-merge
1433*540489deSweihe       // algorithm because callsite records at the same offset are ordered by
1434*540489deSweihe       // function names.
1435*540489deSweihe       MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
1436*540489deSweihe           CallsiteIterStep.getFirstIter()->second.cbegin(),
1437*540489deSweihe           CallsiteIterStep.getFirstIter()->second.cend(),
1438*540489deSweihe           CallsiteIterStep.getSecondIter()->second.cbegin(),
1439*540489deSweihe           CallsiteIterStep.getSecondIter()->second.cend());
1440*540489deSweihe       CalleeIterStep.updateOneStep();
1441*540489deSweihe       while (!CalleeIterStep.areBothFinished()) {
1442*540489deSweihe         MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
1443*540489deSweihe         if (CalleeStepStatus != MS_Match) {
1444*540489deSweihe           auto Callee = (CalleeStepStatus == MS_FirstUnique)
1445*540489deSweihe                             ? CalleeIterStep.getFirstIter()
1446*540489deSweihe                             : CalleeIterStep.getSecondIter();
1447*540489deSweihe           updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
1448*540489deSweihe                                    CalleeStepStatus);
1449*540489deSweihe         } else {
1450*540489deSweihe           // An inlined function can contain other inlinees inside, so compute
1451*540489deSweihe           // the Difference recursively.
1452*540489deSweihe           Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
1453*540489deSweihe                                       CalleeIterStep.getFirstIter()->second,
1454*540489deSweihe                                       CalleeIterStep.getSecondIter()->second,
1455*540489deSweihe                                       FuncOverlap);
1456*540489deSweihe         }
1457*540489deSweihe         CalleeIterStep.updateOneStep();
1458*540489deSweihe       }
1459*540489deSweihe     }
1460*540489deSweihe     CallsiteIterStep.updateOneStep();
1461*540489deSweihe   }
1462*540489deSweihe 
1463*540489deSweihe   // Difference reflects the total differences of line/block samples in this
1464*540489deSweihe   // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
1465*540489deSweihe   // reflect the similarity between function profiles in [0.0f to 1.0f].
1466*540489deSweihe   return (2.0 - Difference) / 2;
1467*540489deSweihe }
1468*540489deSweihe 
1469*540489deSweihe double SampleOverlapAggregator::weightForFuncSimilarity(
1470*540489deSweihe     double FuncInternalSimilarity, uint64_t BaseFuncSample,
1471*540489deSweihe     uint64_t TestFuncSample) const {
1472*540489deSweihe   // Compute the weight as the distance between the function weights in two
1473*540489deSweihe   // profiles.
1474*540489deSweihe   double BaseFrac = 0.0;
1475*540489deSweihe   double TestFrac = 0.0;
1476*540489deSweihe   assert(ProfOverlap.BaseSample > 0 &&
1477*540489deSweihe          "Total samples in base profile should be greater than 0");
1478*540489deSweihe   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
1479*540489deSweihe   assert(ProfOverlap.TestSample > 0 &&
1480*540489deSweihe          "Total samples in test profile should be greater than 0");
1481*540489deSweihe   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
1482*540489deSweihe   double WeightDistance = std::fabs(BaseFrac - TestFrac);
1483*540489deSweihe 
1484*540489deSweihe   // Take WeightDistance into the similarity.
1485*540489deSweihe   return FuncInternalSimilarity * (1 - WeightDistance);
1486*540489deSweihe }
1487*540489deSweihe 
1488*540489deSweihe double
1489*540489deSweihe SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
1490*540489deSweihe                                             uint64_t BaseFuncSample,
1491*540489deSweihe                                             uint64_t TestFuncSample) const {
1492*540489deSweihe 
1493*540489deSweihe   double BaseFrac = 0.0;
1494*540489deSweihe   double TestFrac = 0.0;
1495*540489deSweihe   assert(ProfOverlap.BaseSample > 0 &&
1496*540489deSweihe          "Total samples in base profile should be greater than 0");
1497*540489deSweihe   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
1498*540489deSweihe   assert(ProfOverlap.TestSample > 0 &&
1499*540489deSweihe          "Total samples in test profile should be greater than 0");
1500*540489deSweihe   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
1501*540489deSweihe   return FuncSimilarity * (BaseFrac + TestFrac);
1502*540489deSweihe }
1503*540489deSweihe 
1504*540489deSweihe double SampleOverlapAggregator::computeSampleFunctionOverlap(
1505*540489deSweihe     const sampleprof::FunctionSamples *BaseFunc,
1506*540489deSweihe     const sampleprof::FunctionSamples *TestFunc,
1507*540489deSweihe     SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
1508*540489deSweihe     uint64_t TestFuncSample) {
1509*540489deSweihe   // Default function internal similarity before weighted, meaning two functions
1510*540489deSweihe   // has no overlap.
1511*540489deSweihe   const double DefaultFuncInternalSimilarity = 0;
1512*540489deSweihe   double FuncSimilarity;
1513*540489deSweihe   double FuncInternalSimilarity;
1514*540489deSweihe 
1515*540489deSweihe   // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
1516*540489deSweihe   // In this case, we use DefaultFuncInternalSimilarity as the function internal
1517*540489deSweihe   // similarity.
1518*540489deSweihe   if (!BaseFunc || !TestFunc) {
1519*540489deSweihe     FuncInternalSimilarity = DefaultFuncInternalSimilarity;
1520*540489deSweihe   } else {
1521*540489deSweihe     assert(FuncOverlap != nullptr &&
1522*540489deSweihe            "FuncOverlap should be provided in this case");
1523*540489deSweihe     FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
1524*540489deSweihe         *BaseFunc, *TestFunc, *FuncOverlap);
1525*540489deSweihe     // Now, FuncInternalSimilarity may be a little less than 0 due to
1526*540489deSweihe     // imprecision of floating point accumulations. Make it zero if the
1527*540489deSweihe     // difference is below Epsilon.
1528*540489deSweihe     FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
1529*540489deSweihe                                  ? 0
1530*540489deSweihe                                  : FuncInternalSimilarity;
1531*540489deSweihe   }
1532*540489deSweihe   FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
1533*540489deSweihe                                            BaseFuncSample, TestFuncSample);
1534*540489deSweihe   return FuncSimilarity;
1535*540489deSweihe }
1536*540489deSweihe 
1537*540489deSweihe void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
1538*540489deSweihe   using namespace sampleprof;
1539*540489deSweihe 
1540*540489deSweihe   StringMap<const FunctionSamples *> BaseFuncProf;
1541*540489deSweihe   const auto &BaseProfiles = BaseReader->getProfiles();
1542*540489deSweihe   for (const auto &BaseFunc : BaseProfiles) {
1543*540489deSweihe     BaseFuncProf.try_emplace(BaseFunc.second.getFuncName(), &(BaseFunc.second));
1544*540489deSweihe   }
1545*540489deSweihe   ProfOverlap.UnionCount = BaseFuncProf.size();
1546*540489deSweihe 
1547*540489deSweihe   const auto &TestProfiles = TestReader->getProfiles();
1548*540489deSweihe   for (const auto &TestFunc : TestProfiles) {
1549*540489deSweihe     SampleOverlapStats FuncOverlap;
1550*540489deSweihe     FuncOverlap.TestName = TestFunc.second.getFuncName();
1551*540489deSweihe     assert(TestStats.count(FuncOverlap.TestName) &&
1552*540489deSweihe            "TestStats should have records for all functions in test profile "
1553*540489deSweihe            "except inlinees");
1554*540489deSweihe     FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
1555*540489deSweihe 
1556*540489deSweihe     const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
1557*540489deSweihe     if (Match == BaseFuncProf.end()) {
1558*540489deSweihe       const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
1559*540489deSweihe       ++ProfOverlap.TestUniqueCount;
1560*540489deSweihe       ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
1561*540489deSweihe       FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
1562*540489deSweihe 
1563*540489deSweihe       updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
1564*540489deSweihe 
1565*540489deSweihe       double FuncSimilarity = computeSampleFunctionOverlap(
1566*540489deSweihe           nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
1567*540489deSweihe       ProfOverlap.Similarity +=
1568*540489deSweihe           weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
1569*540489deSweihe 
1570*540489deSweihe       ++ProfOverlap.UnionCount;
1571*540489deSweihe       ProfOverlap.UnionSample += FuncStats.SampleSum;
1572*540489deSweihe     } else {
1573*540489deSweihe       ++ProfOverlap.OverlapCount;
1574*540489deSweihe 
1575*540489deSweihe       // Two functions match with each other. Compute function-level overlap and
1576*540489deSweihe       // aggregate them into profile-level overlap.
1577*540489deSweihe       FuncOverlap.BaseName = Match->second->getFuncName();
1578*540489deSweihe       assert(BaseStats.count(FuncOverlap.BaseName) &&
1579*540489deSweihe              "BaseStats should have records for all functions in base profile "
1580*540489deSweihe              "except inlinees");
1581*540489deSweihe       FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
1582*540489deSweihe 
1583*540489deSweihe       FuncOverlap.Similarity = computeSampleFunctionOverlap(
1584*540489deSweihe           Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
1585*540489deSweihe           FuncOverlap.TestSample);
1586*540489deSweihe       ProfOverlap.Similarity +=
1587*540489deSweihe           weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
1588*540489deSweihe                              FuncOverlap.TestSample);
1589*540489deSweihe       ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
1590*540489deSweihe       ProfOverlap.UnionSample += FuncOverlap.UnionSample;
1591*540489deSweihe 
1592*540489deSweihe       // Accumulate the percentage of base unique and test unique samples into
1593*540489deSweihe       // ProfOverlap.
1594*540489deSweihe       ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
1595*540489deSweihe       ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
1596*540489deSweihe 
1597*540489deSweihe       // Remove matched base functions for later reporting functions not found
1598*540489deSweihe       // in test profile.
1599*540489deSweihe       BaseFuncProf.erase(Match);
1600*540489deSweihe     }
1601*540489deSweihe 
1602*540489deSweihe     // Print function-level similarity information if specified by options.
1603*540489deSweihe     assert(TestStats.count(FuncOverlap.TestName) &&
1604*540489deSweihe            "TestStats should have records for all functions in test profile "
1605*540489deSweihe            "except inlinees");
1606*540489deSweihe     if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
1607*540489deSweihe         (Match != BaseFuncProf.end() &&
1608*540489deSweihe          FuncOverlap.Similarity < LowSimilarityThreshold) ||
1609*540489deSweihe         (Match != BaseFuncProf.end() && !FuncFilter.NameFilter.empty() &&
1610*540489deSweihe          FuncOverlap.BaseName.find(FuncFilter.NameFilter) !=
1611*540489deSweihe              FuncOverlap.BaseName.npos)) {
1612*540489deSweihe       assert(ProfOverlap.BaseSample > 0 &&
1613*540489deSweihe              "Total samples in base profile should be greater than 0");
1614*540489deSweihe       FuncOverlap.BaseWeight =
1615*540489deSweihe           static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
1616*540489deSweihe       assert(ProfOverlap.TestSample > 0 &&
1617*540489deSweihe              "Total samples in test profile should be greater than 0");
1618*540489deSweihe       FuncOverlap.TestWeight =
1619*540489deSweihe           static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
1620*540489deSweihe       FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
1621*540489deSweihe     }
1622*540489deSweihe   }
1623*540489deSweihe 
1624*540489deSweihe   // Traverse through functions in base profile but not in test profile.
1625*540489deSweihe   for (const auto &F : BaseFuncProf) {
1626*540489deSweihe     assert(BaseStats.count(F.second->getFuncName()) &&
1627*540489deSweihe            "BaseStats should have records for all functions in base profile "
1628*540489deSweihe            "except inlinees");
1629*540489deSweihe     const FuncSampleStats &FuncStats = BaseStats[F.second->getFuncName()];
1630*540489deSweihe     ++ProfOverlap.BaseUniqueCount;
1631*540489deSweihe     ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
1632*540489deSweihe 
1633*540489deSweihe     updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
1634*540489deSweihe 
1635*540489deSweihe     double FuncSimilarity = computeSampleFunctionOverlap(
1636*540489deSweihe         nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
1637*540489deSweihe     ProfOverlap.Similarity +=
1638*540489deSweihe         weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
1639*540489deSweihe 
1640*540489deSweihe     ProfOverlap.UnionSample += FuncStats.SampleSum;
1641*540489deSweihe   }
1642*540489deSweihe 
1643*540489deSweihe   // Now, ProfSimilarity may be a little greater than 1 due to imprecision
1644*540489deSweihe   // of floating point accumulations. Make it 1.0 if the difference is below
1645*540489deSweihe   // Epsilon.
1646*540489deSweihe   ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
1647*540489deSweihe                                ? 1
1648*540489deSweihe                                : ProfOverlap.Similarity;
1649*540489deSweihe 
1650*540489deSweihe   computeHotFuncOverlap();
1651*540489deSweihe }
1652*540489deSweihe 
1653*540489deSweihe void SampleOverlapAggregator::initializeSampleProfileOverlap() {
1654*540489deSweihe   const auto &BaseProf = BaseReader->getProfiles();
1655*540489deSweihe   for (const auto &I : BaseProf) {
1656*540489deSweihe     ++ProfOverlap.BaseCount;
1657*540489deSweihe     FuncSampleStats FuncStats;
1658*540489deSweihe     getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
1659*540489deSweihe     ProfOverlap.BaseSample += FuncStats.SampleSum;
1660*540489deSweihe     BaseStats.try_emplace(I.second.getFuncName(), FuncStats);
1661*540489deSweihe   }
1662*540489deSweihe 
1663*540489deSweihe   const auto &TestProf = TestReader->getProfiles();
1664*540489deSweihe   for (const auto &I : TestProf) {
1665*540489deSweihe     ++ProfOverlap.TestCount;
1666*540489deSweihe     FuncSampleStats FuncStats;
1667*540489deSweihe     getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
1668*540489deSweihe     ProfOverlap.TestSample += FuncStats.SampleSum;
1669*540489deSweihe     TestStats.try_emplace(I.second.getFuncName(), FuncStats);
1670*540489deSweihe   }
1671*540489deSweihe 
1672*540489deSweihe   ProfOverlap.BaseName = StringRef(BaseFilename);
1673*540489deSweihe   ProfOverlap.TestName = StringRef(TestFilename);
1674*540489deSweihe }
1675*540489deSweihe 
1676*540489deSweihe void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
1677*540489deSweihe   using namespace sampleprof;
1678*540489deSweihe 
1679*540489deSweihe   if (FuncSimilarityDump.empty())
1680*540489deSweihe     return;
1681*540489deSweihe 
1682*540489deSweihe   formatted_raw_ostream FOS(OS);
1683*540489deSweihe   FOS << "Function-level details:\n";
1684*540489deSweihe   FOS << "Base weight";
1685*540489deSweihe   FOS.PadToColumn(TestWeightCol);
1686*540489deSweihe   FOS << "Test weight";
1687*540489deSweihe   FOS.PadToColumn(SimilarityCol);
1688*540489deSweihe   FOS << "Similarity";
1689*540489deSweihe   FOS.PadToColumn(OverlapCol);
1690*540489deSweihe   FOS << "Overlap";
1691*540489deSweihe   FOS.PadToColumn(BaseUniqueCol);
1692*540489deSweihe   FOS << "Base unique";
1693*540489deSweihe   FOS.PadToColumn(TestUniqueCol);
1694*540489deSweihe   FOS << "Test unique";
1695*540489deSweihe   FOS.PadToColumn(BaseSampleCol);
1696*540489deSweihe   FOS << "Base samples";
1697*540489deSweihe   FOS.PadToColumn(TestSampleCol);
1698*540489deSweihe   FOS << "Test samples";
1699*540489deSweihe   FOS.PadToColumn(FuncNameCol);
1700*540489deSweihe   FOS << "Function name\n";
1701*540489deSweihe   for (const auto &F : FuncSimilarityDump) {
1702*540489deSweihe     double OverlapPercent =
1703*540489deSweihe         F.second.UnionSample > 0
1704*540489deSweihe             ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
1705*540489deSweihe             : 0;
1706*540489deSweihe     double BaseUniquePercent =
1707*540489deSweihe         F.second.BaseSample > 0
1708*540489deSweihe             ? static_cast<double>(F.second.BaseUniqueSample) /
1709*540489deSweihe                   F.second.BaseSample
1710*540489deSweihe             : 0;
1711*540489deSweihe     double TestUniquePercent =
1712*540489deSweihe         F.second.TestSample > 0
1713*540489deSweihe             ? static_cast<double>(F.second.TestUniqueSample) /
1714*540489deSweihe                   F.second.TestSample
1715*540489deSweihe             : 0;
1716*540489deSweihe 
1717*540489deSweihe     FOS << format("%.2f%%", F.second.BaseWeight * 100);
1718*540489deSweihe     FOS.PadToColumn(TestWeightCol);
1719*540489deSweihe     FOS << format("%.2f%%", F.second.TestWeight * 100);
1720*540489deSweihe     FOS.PadToColumn(SimilarityCol);
1721*540489deSweihe     FOS << format("%.2f%%", F.second.Similarity * 100);
1722*540489deSweihe     FOS.PadToColumn(OverlapCol);
1723*540489deSweihe     FOS << format("%.2f%%", OverlapPercent * 100);
1724*540489deSweihe     FOS.PadToColumn(BaseUniqueCol);
1725*540489deSweihe     FOS << format("%.2f%%", BaseUniquePercent * 100);
1726*540489deSweihe     FOS.PadToColumn(TestUniqueCol);
1727*540489deSweihe     FOS << format("%.2f%%", TestUniquePercent * 100);
1728*540489deSweihe     FOS.PadToColumn(BaseSampleCol);
1729*540489deSweihe     FOS << F.second.BaseSample;
1730*540489deSweihe     FOS.PadToColumn(TestSampleCol);
1731*540489deSweihe     FOS << F.second.TestSample;
1732*540489deSweihe     FOS.PadToColumn(FuncNameCol);
1733*540489deSweihe     FOS << F.second.TestName << "\n";
1734*540489deSweihe   }
1735*540489deSweihe }
1736*540489deSweihe 
1737*540489deSweihe void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
1738*540489deSweihe   OS << "Profile overlap infomation for base_profile: " << ProfOverlap.BaseName
1739*540489deSweihe      << " and test_profile: " << ProfOverlap.TestName << "\nProgram level:\n";
1740*540489deSweihe 
1741*540489deSweihe   OS << "  Whole program profile similarity: "
1742*540489deSweihe      << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
1743*540489deSweihe 
1744*540489deSweihe   assert(ProfOverlap.UnionSample > 0 &&
1745*540489deSweihe          "Total samples in two profile should be greater than 0");
1746*540489deSweihe   double OverlapPercent =
1747*540489deSweihe       static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
1748*540489deSweihe   assert(ProfOverlap.BaseSample > 0 &&
1749*540489deSweihe          "Total samples in base profile should be greater than 0");
1750*540489deSweihe   double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
1751*540489deSweihe                              ProfOverlap.BaseSample;
1752*540489deSweihe   assert(ProfOverlap.TestSample > 0 &&
1753*540489deSweihe          "Total samples in test profile should be greater than 0");
1754*540489deSweihe   double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
1755*540489deSweihe                              ProfOverlap.TestSample;
1756*540489deSweihe 
1757*540489deSweihe   OS << "  Whole program sample overlap: "
1758*540489deSweihe      << format("%.3f%%", OverlapPercent * 100) << "\n";
1759*540489deSweihe   OS << "    percentage of samples unique in base profile: "
1760*540489deSweihe      << format("%.3f%%", BaseUniquePercent * 100) << "\n";
1761*540489deSweihe   OS << "    percentage of samples unique in test profile: "
1762*540489deSweihe      << format("%.3f%%", TestUniquePercent * 100) << "\n";
1763*540489deSweihe   OS << "    total samples in base profile: " << ProfOverlap.BaseSample << "\n"
1764*540489deSweihe      << "    total samples in test profile: " << ProfOverlap.TestSample << "\n";
1765*540489deSweihe 
1766*540489deSweihe   assert(ProfOverlap.UnionCount > 0 &&
1767*540489deSweihe          "There should be at least one function in two input profiles");
1768*540489deSweihe   double FuncOverlapPercent =
1769*540489deSweihe       static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
1770*540489deSweihe   OS << "  Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
1771*540489deSweihe      << "\n";
1772*540489deSweihe   OS << "    overlap functions: " << ProfOverlap.OverlapCount << "\n";
1773*540489deSweihe   OS << "    functions unique in base profile: " << ProfOverlap.BaseUniqueCount
1774*540489deSweihe      << "\n";
1775*540489deSweihe   OS << "    functions unique in test profile: " << ProfOverlap.TestUniqueCount
1776*540489deSweihe      << "\n";
1777*540489deSweihe }
1778*540489deSweihe 
1779*540489deSweihe void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
1780*540489deSweihe     raw_fd_ostream &OS) const {
1781*540489deSweihe   assert(HotFuncOverlap.UnionCount > 0 &&
1782*540489deSweihe          "There should be at least one hot function in two input profiles");
1783*540489deSweihe   OS << "  Hot-function overlap: "
1784*540489deSweihe      << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
1785*540489deSweihe                              HotFuncOverlap.UnionCount * 100)
1786*540489deSweihe      << "\n";
1787*540489deSweihe   OS << "    overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
1788*540489deSweihe   OS << "    hot functions unique in base profile: "
1789*540489deSweihe      << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
1790*540489deSweihe   OS << "    hot functions unique in test profile: "
1791*540489deSweihe      << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
1792*540489deSweihe 
1793*540489deSweihe   assert(HotBlockOverlap.UnionCount > 0 &&
1794*540489deSweihe          "There should be at least one hot block in two input profiles");
1795*540489deSweihe   OS << "  Hot-block overlap: "
1796*540489deSweihe      << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
1797*540489deSweihe                              HotBlockOverlap.UnionCount * 100)
1798*540489deSweihe      << "\n";
1799*540489deSweihe   OS << "    overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
1800*540489deSweihe   OS << "    hot blocks unique in base profile: "
1801*540489deSweihe      << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
1802*540489deSweihe   OS << "    hot blocks unique in test profile: "
1803*540489deSweihe      << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
1804*540489deSweihe }
1805*540489deSweihe 
1806*540489deSweihe std::error_code SampleOverlapAggregator::loadProfiles() {
1807*540489deSweihe   using namespace sampleprof;
1808*540489deSweihe 
1809*540489deSweihe   LLVMContext Context;
1810*540489deSweihe   auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context);
1811*540489deSweihe   if (std::error_code EC = BaseReaderOrErr.getError())
1812*540489deSweihe     exitWithErrorCode(EC, BaseFilename);
1813*540489deSweihe 
1814*540489deSweihe   auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context);
1815*540489deSweihe   if (std::error_code EC = TestReaderOrErr.getError())
1816*540489deSweihe     exitWithErrorCode(EC, TestFilename);
1817*540489deSweihe 
1818*540489deSweihe   BaseReader = std::move(BaseReaderOrErr.get());
1819*540489deSweihe   TestReader = std::move(TestReaderOrErr.get());
1820*540489deSweihe 
1821*540489deSweihe   if (std::error_code EC = BaseReader->read())
1822*540489deSweihe     exitWithErrorCode(EC, BaseFilename);
1823*540489deSweihe   if (std::error_code EC = TestReader->read())
1824*540489deSweihe     exitWithErrorCode(EC, TestFilename);
1825*540489deSweihe 
1826*540489deSweihe   // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
1827*540489deSweihe   // profile summary.
1828*540489deSweihe   const uint64_t HotCutoff = 990000;
1829*540489deSweihe   ProfileSummary &BasePS = BaseReader->getSummary();
1830*540489deSweihe   for (const auto &SummaryEntry : BasePS.getDetailedSummary()) {
1831*540489deSweihe     if (SummaryEntry.Cutoff == HotCutoff) {
1832*540489deSweihe       BaseHotThreshold = SummaryEntry.MinCount;
1833*540489deSweihe       break;
1834*540489deSweihe     }
1835*540489deSweihe   }
1836*540489deSweihe 
1837*540489deSweihe   ProfileSummary &TestPS = TestReader->getSummary();
1838*540489deSweihe   for (const auto &SummaryEntry : TestPS.getDetailedSummary()) {
1839*540489deSweihe     if (SummaryEntry.Cutoff == HotCutoff) {
1840*540489deSweihe       TestHotThreshold = SummaryEntry.MinCount;
1841*540489deSweihe       break;
1842*540489deSweihe     }
1843*540489deSweihe   }
1844*540489deSweihe   return std::error_code();
1845*540489deSweihe }
1846*540489deSweihe 
1847*540489deSweihe void overlapSampleProfile(const std::string &BaseFilename,
1848*540489deSweihe                           const std::string &TestFilename,
1849*540489deSweihe                           const OverlapFuncFilters &FuncFilter,
1850*540489deSweihe                           uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
1851*540489deSweihe   using namespace sampleprof;
1852*540489deSweihe 
1853*540489deSweihe   // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
1854*540489deSweihe   // report 2--3 places after decimal point in percentage numbers.
1855*540489deSweihe   SampleOverlapAggregator OverlapAggr(
1856*540489deSweihe       BaseFilename, TestFilename,
1857*540489deSweihe       static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
1858*540489deSweihe   if (std::error_code EC = OverlapAggr.loadProfiles())
1859*540489deSweihe     exitWithErrorCode(EC);
1860*540489deSweihe 
1861*540489deSweihe   OverlapAggr.initializeSampleProfileOverlap();
1862*540489deSweihe   if (OverlapAggr.detectZeroSampleProfile(OS))
1863*540489deSweihe     return;
1864*540489deSweihe 
1865*540489deSweihe   OverlapAggr.computeSampleProfileOverlap(OS);
1866*540489deSweihe 
1867*540489deSweihe   OverlapAggr.dumpProgramSummary(OS);
1868*540489deSweihe   OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
1869*540489deSweihe   OverlapAggr.dumpFuncSimilarity(OS);
1870*540489deSweihe }
1871*540489deSweihe 
1872998b97f6SRong Xu static int overlap_main(int argc, const char *argv[]) {
1873998b97f6SRong Xu   cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
1874998b97f6SRong Xu                                     cl::desc("<base profile file>"));
1875998b97f6SRong Xu   cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
1876998b97f6SRong Xu                                     cl::desc("<test profile file>"));
1877998b97f6SRong Xu   cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"),
1878998b97f6SRong Xu                               cl::desc("Output file"));
1879998b97f6SRong Xu   cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output));
1880998b97f6SRong Xu   cl::opt<bool> IsCS("cs", cl::init(false),
1881998b97f6SRong Xu                      cl::desc("For context sensitive counts"));
1882998b97f6SRong Xu   cl::opt<unsigned long long> ValueCutoff(
1883998b97f6SRong Xu       "value-cutoff", cl::init(-1),
1884998b97f6SRong Xu       cl::desc(
1885998b97f6SRong Xu           "Function level overlap information for every function in test "
1886998b97f6SRong Xu           "profile with max count value greater then the parameter value"));
1887998b97f6SRong Xu   cl::opt<std::string> FuncNameFilter(
1888998b97f6SRong Xu       "function",
1889998b97f6SRong Xu       cl::desc("Function level overlap information for matching functions"));
1890*540489deSweihe   cl::opt<unsigned long long> SimilarityCutoff(
1891*540489deSweihe       "similarity-cutoff", cl::init(0),
1892*540489deSweihe       cl::desc(
1893*540489deSweihe           "For sample profiles, list function names for overlapped functions "
1894*540489deSweihe           "with similarities below the cutoff (percentage times 10000)."));
1895*540489deSweihe   cl::opt<ProfileKinds> ProfileKind(
1896*540489deSweihe       cl::desc("Profile kind:"), cl::init(instr),
1897*540489deSweihe       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
1898*540489deSweihe                  clEnumVal(sample, "Sample profile")));
1899998b97f6SRong Xu   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n");
1900998b97f6SRong Xu 
1901998b97f6SRong Xu   std::error_code EC;
1902d9b948b6SFangrui Song   raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text);
1903998b97f6SRong Xu   if (EC)
1904998b97f6SRong Xu     exitWithErrorCode(EC, Output);
1905998b97f6SRong Xu 
1906*540489deSweihe   if (ProfileKind == instr)
1907998b97f6SRong Xu     overlapInstrProfile(BaseFilename, TestFilename,
1908998b97f6SRong Xu                         OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS,
1909998b97f6SRong Xu                         IsCS);
1910*540489deSweihe   else
1911*540489deSweihe     overlapSampleProfile(BaseFilename, TestFilename,
1912*540489deSweihe                          OverlapFuncFilters{ValueCutoff, FuncNameFilter},
1913*540489deSweihe                          SimilarityCutoff, OS);
1914998b97f6SRong Xu 
1915998b97f6SRong Xu   return 0;
1916998b97f6SRong Xu }
1917998b97f6SRong Xu 
19180cf1f56aSRong Xu typedef struct ValueSitesStats {
19190cf1f56aSRong Xu   ValueSitesStats()
19200cf1f56aSRong Xu       : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
19210cf1f56aSRong Xu         TotalNumValues(0) {}
19220cf1f56aSRong Xu   uint64_t TotalNumValueSites;
19230cf1f56aSRong Xu   uint64_t TotalNumValueSitesWithValueProfile;
19240cf1f56aSRong Xu   uint64_t TotalNumValues;
19250cf1f56aSRong Xu   std::vector<unsigned> ValueSitesHistogram;
19260cf1f56aSRong Xu } ValueSitesStats;
19270cf1f56aSRong Xu 
19280cf1f56aSRong Xu static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
19290cf1f56aSRong Xu                                   ValueSitesStats &Stats, raw_fd_ostream &OS,
193060faea19SRong Xu                                   InstrProfSymtab *Symtab) {
19310cf1f56aSRong Xu   uint32_t NS = Func.getNumValueSites(VK);
19320cf1f56aSRong Xu   Stats.TotalNumValueSites += NS;
19330cf1f56aSRong Xu   for (size_t I = 0; I < NS; ++I) {
19340cf1f56aSRong Xu     uint32_t NV = Func.getNumValueDataForSite(VK, I);
19350cf1f56aSRong Xu     std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
19360cf1f56aSRong Xu     Stats.TotalNumValues += NV;
19370cf1f56aSRong Xu     if (NV) {
19380cf1f56aSRong Xu       Stats.TotalNumValueSitesWithValueProfile++;
19390cf1f56aSRong Xu       if (NV > Stats.ValueSitesHistogram.size())
19400cf1f56aSRong Xu         Stats.ValueSitesHistogram.resize(NV, 0);
19410cf1f56aSRong Xu       Stats.ValueSitesHistogram[NV - 1]++;
19420cf1f56aSRong Xu     }
194352aa224aSRong Xu 
194452aa224aSRong Xu     uint64_t SiteSum = 0;
194552aa224aSRong Xu     for (uint32_t V = 0; V < NV; V++)
194652aa224aSRong Xu       SiteSum += VD[V].Count;
194752aa224aSRong Xu     if (SiteSum == 0)
194852aa224aSRong Xu       SiteSum = 1;
194952aa224aSRong Xu 
19500cf1f56aSRong Xu     for (uint32_t V = 0; V < NV; V++) {
195152aa224aSRong Xu       OS << "\t[ " << format("%2u", I) << ", ";
195260faea19SRong Xu       if (Symtab == nullptr)
195340a7f63cSPetar Jovanovic         OS << format("%4" PRIu64, VD[V].Value);
195460faea19SRong Xu       else
195560faea19SRong Xu         OS << Symtab->getFuncName(VD[V].Value);
195652aa224aSRong Xu       OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
195752aa224aSRong Xu          << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
19580cf1f56aSRong Xu     }
19590cf1f56aSRong Xu   }
19600cf1f56aSRong Xu }
19610cf1f56aSRong Xu 
19620cf1f56aSRong Xu static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
19630cf1f56aSRong Xu                                 ValueSitesStats &Stats) {
19640cf1f56aSRong Xu   OS << "  Total number of sites: " << Stats.TotalNumValueSites << "\n";
19650cf1f56aSRong Xu   OS << "  Total number of sites with values: "
19660cf1f56aSRong Xu      << Stats.TotalNumValueSitesWithValueProfile << "\n";
19670cf1f56aSRong Xu   OS << "  Total number of profiled values: " << Stats.TotalNumValues << "\n";
19680cf1f56aSRong Xu 
19690cf1f56aSRong Xu   OS << "  Value sites histogram:\n\tNumTargets, SiteCount\n";
19700cf1f56aSRong Xu   for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
19710cf1f56aSRong Xu     if (Stats.ValueSitesHistogram[I] > 0)
19720cf1f56aSRong Xu       OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
19730cf1f56aSRong Xu   }
19740cf1f56aSRong Xu }
19750cf1f56aSRong Xu 
19761afc1de4SBenjamin Kramer static int showInstrProfile(const std::string &Filename, bool ShowCounts,
1977801b5319SXinliang David Li                             uint32_t TopN, bool ShowIndirectCallTargets,
1978801b5319SXinliang David Li                             bool ShowMemOPSizes, bool ShowDetailedSummary,
1979183ebbe0SEaswaran Raman                             std::vector<uint32_t> DetailedSummaryCutoffs,
1980a6ff69f6SRong Xu                             bool ShowAllFunctions, bool ShowCS,
1981a6ff69f6SRong Xu                             uint64_t ValueCutoff, bool OnlyListBelow,
1982a6ff69f6SRong Xu                             const std::string &ShowFunction, bool TextFormat,
1983a6ff69f6SRong Xu                             raw_fd_ostream &OS) {
1984fcd55607SDiego Novillo   auto ReaderOrErr = InstrProfReader::create(Filename);
19851afc1de4SBenjamin Kramer   std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
19861afc1de4SBenjamin Kramer   if (ShowDetailedSummary && Cutoffs.empty()) {
1987183ebbe0SEaswaran Raman     Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
1988183ebbe0SEaswaran Raman   }
19891afc1de4SBenjamin Kramer   InstrProfSummaryBuilder Builder(std::move(Cutoffs));
19909152fd17SVedant Kumar   if (Error E = ReaderOrErr.takeError())
19919152fd17SVedant Kumar     exitWithError(std::move(E), Filename);
19929af28ef9SJustin Bogner 
1993fcd55607SDiego Novillo   auto Reader = std::move(ReaderOrErr.get());
199433c76c0cSRong Xu   bool IsIRInstr = Reader->isIRLevelProfile();
1995183ebbe0SEaswaran Raman   size_t ShownFunctions = 0;
199652aa224aSRong Xu   size_t BelowCutoffFunctions = 0;
19970cf1f56aSRong Xu   int NumVPKind = IPVK_Last - IPVK_First + 1;
19980cf1f56aSRong Xu   std::vector<ValueSitesStats> VPStats(NumVPKind);
1999801b5319SXinliang David Li 
2000801b5319SXinliang David Li   auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
2001801b5319SXinliang David Li                    const std::pair<std::string, uint64_t> &v2) {
2002801b5319SXinliang David Li     return v1.second > v2.second;
2003801b5319SXinliang David Li   };
2004801b5319SXinliang David Li 
2005801b5319SXinliang David Li   std::priority_queue<std::pair<std::string, uint64_t>,
2006801b5319SXinliang David Li                       std::vector<std::pair<std::string, uint64_t>>,
2007801b5319SXinliang David Li                       decltype(MinCmp)>
2008801b5319SXinliang David Li       HottestFuncs(MinCmp);
2009801b5319SXinliang David Li 
201052aa224aSRong Xu   if (!TextFormat && OnlyListBelow) {
201152aa224aSRong Xu     OS << "The list of functions with the maximum counter less than "
201252aa224aSRong Xu        << ValueCutoff << ":\n";
201352aa224aSRong Xu   }
201452aa224aSRong Xu 
2015c6ba9ca1SRichard Smith   // Add marker so that IR-level instrumentation round-trips properly.
2016c6ba9ca1SRichard Smith   if (TextFormat && IsIRInstr)
2017c6ba9ca1SRichard Smith     OS << ":ir\n";
2018c6ba9ca1SRichard Smith 
20199af28ef9SJustin Bogner   for (const auto &Func : *Reader) {
2020a6ff69f6SRong Xu     if (Reader->isIRLevelProfile()) {
2021a6ff69f6SRong Xu       bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
2022a6ff69f6SRong Xu       if (FuncIsCS != ShowCS)
2023a6ff69f6SRong Xu         continue;
2024a6ff69f6SRong Xu     }
2025d5336ae2SDiego Novillo     bool Show =
2026d5336ae2SDiego Novillo         ShowAllFunctions || (!ShowFunction.empty() &&
20279af28ef9SJustin Bogner                              Func.Name.find(ShowFunction) != Func.Name.npos);
20289af28ef9SJustin Bogner 
2029c6ba9ca1SRichard Smith     bool doTextFormatDump = (Show && TextFormat);
20306f7c19a4SXinliang David Li 
20316f7c19a4SXinliang David Li     if (doTextFormatDump) {
2032a716cc5cSXinliang David Li       InstrProfSymtab &Symtab = Reader->getSymtab();
2033cf9d52c6SDavid Blaikie       InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
2034cf9d52c6SDavid Blaikie                                          OS);
20356f7c19a4SXinliang David Li       continue;
20366f7c19a4SXinliang David Li     }
20376f7c19a4SXinliang David Li 
2038b59d7c73SJustin Bogner     assert(Func.Counts.size() > 0 && "function missing entry counter");
2039e5a17e3fSEaswaran Raman     Builder.addRecord(Func);
20406f7c19a4SXinliang David Li 
20417162e16eSRong Xu     uint64_t FuncMax = 0;
204252aa224aSRong Xu     uint64_t FuncSum = 0;
204352aa224aSRong Xu     for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
2044a23f6234SWei Mi       if (Func.Counts[I] == (uint64_t)-1)
2045a23f6234SWei Mi         continue;
20467162e16eSRong Xu       FuncMax = std::max(FuncMax, Func.Counts[I]);
204752aa224aSRong Xu       FuncSum += Func.Counts[I];
204852aa224aSRong Xu     }
20497162e16eSRong Xu 
205052aa224aSRong Xu     if (FuncMax < ValueCutoff) {
205152aa224aSRong Xu       ++BelowCutoffFunctions;
205252aa224aSRong Xu       if (OnlyListBelow) {
205352aa224aSRong Xu         OS << "  " << Func.Name << ": (Max = " << FuncMax
205452aa224aSRong Xu            << " Sum = " << FuncSum << ")\n";
205552aa224aSRong Xu       }
205652aa224aSRong Xu       continue;
205752aa224aSRong Xu     } else if (OnlyListBelow)
205852aa224aSRong Xu       continue;
205952aa224aSRong Xu 
206052aa224aSRong Xu     if (TopN) {
2061801b5319SXinliang David Li       if (HottestFuncs.size() == TopN) {
2062801b5319SXinliang David Li         if (HottestFuncs.top().second < FuncMax) {
2063801b5319SXinliang David Li           HottestFuncs.pop();
2064801b5319SXinliang David Li           HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
2065801b5319SXinliang David Li         }
2066801b5319SXinliang David Li       } else
2067801b5319SXinliang David Li         HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
2068801b5319SXinliang David Li     }
2069801b5319SXinliang David Li 
20709af28ef9SJustin Bogner     if (Show) {
20719af28ef9SJustin Bogner       if (!ShownFunctions)
20729af28ef9SJustin Bogner         OS << "Counters:\n";
20736f7c19a4SXinliang David Li 
20749af28ef9SJustin Bogner       ++ShownFunctions;
20759af28ef9SJustin Bogner 
20769af28ef9SJustin Bogner       OS << "  " << Func.Name << ":\n"
2077423380f9SJustin Bogner          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
207833c76c0cSRong Xu          << "    Counters: " << Func.Counts.size() << "\n";
207933c76c0cSRong Xu       if (!IsIRInstr)
208033c76c0cSRong Xu         OS << "    Function count: " << Func.Counts[0] << "\n";
20816f7c19a4SXinliang David Li 
20829e9a057aSJustin Bogner       if (ShowIndirectCallTargets)
20832004f003SXinliang David Li         OS << "    Indirect Call Site Count: "
20842004f003SXinliang David Li            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
20859af28ef9SJustin Bogner 
208660faea19SRong Xu       uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
208760faea19SRong Xu       if (ShowMemOPSizes && NumMemOPCalls > 0)
208860faea19SRong Xu         OS << "    Number of Memory Intrinsics Calls: " << NumMemOPCalls
208960faea19SRong Xu            << "\n";
209060faea19SRong Xu 
20916f7c19a4SXinliang David Li       if (ShowCounts) {
20929af28ef9SJustin Bogner         OS << "    Block counts: [";
209333c76c0cSRong Xu         size_t Start = (IsIRInstr ? 0 : 1);
209433c76c0cSRong Xu         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
209533c76c0cSRong Xu           OS << (I == Start ? "" : ", ") << Func.Counts[I];
20969af28ef9SJustin Bogner         }
20979af28ef9SJustin Bogner         OS << "]\n";
20986f7c19a4SXinliang David Li       }
20999e9a057aSJustin Bogner 
21006f7c19a4SXinliang David Li       if (ShowIndirectCallTargets) {
21019e9a057aSJustin Bogner         OS << "    Indirect Target Results:\n";
21020cf1f56aSRong Xu         traverseAllValueSites(Func, IPVK_IndirectCallTarget,
21030cf1f56aSRong Xu                               VPStats[IPVK_IndirectCallTarget], OS,
210460faea19SRong Xu                               &(Reader->getSymtab()));
210560faea19SRong Xu       }
210660faea19SRong Xu 
210760faea19SRong Xu       if (ShowMemOPSizes && NumMemOPCalls > 0) {
2108cd2aa0d2STeresa Johnson         OS << "    Memory Intrinsic Size Results:\n";
210960faea19SRong Xu         traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
211060faea19SRong Xu                               nullptr);
21119e9a057aSJustin Bogner       }
21129af28ef9SJustin Bogner     }
21136f7c19a4SXinliang David Li   }
2114db1225d0SJustin Bogner   if (Reader->hasError())
21159152fd17SVedant Kumar     exitWithError(Reader->getError(), Filename);
21169af28ef9SJustin Bogner 
2117c6ba9ca1SRichard Smith   if (TextFormat)
21186f7c19a4SXinliang David Li     return 0;
21197cefdb81SEaswaran Raman   std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
212050da55a5SRong Xu   bool IsIR = Reader->isIRLevelProfile();
212150da55a5SRong Xu   OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
212250da55a5SRong Xu   if (IsIR)
212350da55a5SRong Xu     OS << "  entry_first = " << Reader->instrEntryBBEnabled();
212450da55a5SRong Xu   OS << "\n";
21259af28ef9SJustin Bogner   if (ShowAllFunctions || !ShowFunction.empty())
21269af28ef9SJustin Bogner     OS << "Functions shown: " << ShownFunctions << "\n";
2127e5a17e3fSEaswaran Raman   OS << "Total functions: " << PS->getNumFunctions() << "\n";
212852aa224aSRong Xu   if (ValueCutoff > 0) {
212952aa224aSRong Xu     OS << "Number of functions with maximum count (< " << ValueCutoff
213052aa224aSRong Xu        << "): " << BelowCutoffFunctions << "\n";
213152aa224aSRong Xu     OS << "Number of functions with maximum count (>= " << ValueCutoff
213252aa224aSRong Xu        << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
213352aa224aSRong Xu   }
2134e5a17e3fSEaswaran Raman   OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
21357cefdb81SEaswaran Raman   OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
213660faea19SRong Xu 
2137801b5319SXinliang David Li   if (TopN) {
2138801b5319SXinliang David Li     std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
2139801b5319SXinliang David Li     while (!HottestFuncs.empty()) {
2140801b5319SXinliang David Li       SortedHottestFuncs.emplace_back(HottestFuncs.top());
2141801b5319SXinliang David Li       HottestFuncs.pop();
2142801b5319SXinliang David Li     }
2143801b5319SXinliang David Li     OS << "Top " << TopN
2144801b5319SXinliang David Li        << " functions with the largest internal block counts: \n";
2145801b5319SXinliang David Li     for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
2146801b5319SXinliang David Li       OS << "  " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
2147801b5319SXinliang David Li   }
2148801b5319SXinliang David Li 
2149872362c4SXinliang David Li   if (ShownFunctions && ShowIndirectCallTargets) {
21500cf1f56aSRong Xu     OS << "Statistics for indirect call sites profile:\n";
21510cf1f56aSRong Xu     showValueSitesStats(OS, IPVK_IndirectCallTarget,
21520cf1f56aSRong Xu                         VPStats[IPVK_IndirectCallTarget]);
2153872362c4SXinliang David Li   }
2154183ebbe0SEaswaran Raman 
215560faea19SRong Xu   if (ShownFunctions && ShowMemOPSizes) {
215660faea19SRong Xu     OS << "Statistics for memory intrinsic calls sizes profile:\n";
215760faea19SRong Xu     showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
215860faea19SRong Xu   }
215960faea19SRong Xu 
2160183ebbe0SEaswaran Raman   if (ShowDetailedSummary) {
21617cefdb81SEaswaran Raman     OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
2162e5a17e3fSEaswaran Raman     OS << "Total count: " << PS->getTotalCount() << "\n";
216317fc6518SWenlei He     PS->printDetailedSummary(OS);
2164183ebbe0SEaswaran Raman   }
21659af28ef9SJustin Bogner   return 0;
21669af28ef9SJustin Bogner }
21679af28ef9SJustin Bogner 
2168eee532cdSWei Mi static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
2169eee532cdSWei Mi                             raw_fd_ostream &OS) {
2170eee532cdSWei Mi   if (!Reader->dumpSectionInfo(OS)) {
2171eee532cdSWei Mi     WithColor::warning() << "-show-sec-info-only is only supported for "
2172eee532cdSWei Mi                          << "sample profile in extbinary format and is "
2173eee532cdSWei Mi                          << "ignored for other formats.\n";
2174eee532cdSWei Mi     return;
2175eee532cdSWei Mi   }
2176eee532cdSWei Mi }
2177eee532cdSWei Mi 
2178546be088SFangrui Song namespace {
217953cf5302Sweihe struct HotFuncInfo {
218053cf5302Sweihe   StringRef FuncName;
218153cf5302Sweihe   uint64_t TotalCount;
218253cf5302Sweihe   double TotalCountPercent;
218353cf5302Sweihe   uint64_t MaxCount;
218453cf5302Sweihe   uint64_t EntryCount;
218553cf5302Sweihe 
218653cf5302Sweihe   HotFuncInfo()
218753cf5302Sweihe       : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0),
218853cf5302Sweihe         EntryCount(0) {}
218953cf5302Sweihe 
219053cf5302Sweihe   HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
219153cf5302Sweihe       : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS),
219253cf5302Sweihe         EntryCount(ES) {}
219353cf5302Sweihe };
2194546be088SFangrui Song } // namespace
219553cf5302Sweihe 
219653cf5302Sweihe // Print out detailed information about hot functions in PrintValues vector.
219753cf5302Sweihe // Users specify titles and offset of every columns through ColumnTitle and
219853cf5302Sweihe // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
219953cf5302Sweihe // and at least 4. Besides, users can optionally give a HotFuncMetric string to
220053cf5302Sweihe // print out or let it be an empty string.
220153cf5302Sweihe static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
220253cf5302Sweihe                                 const std::vector<int> &ColumnOffset,
220353cf5302Sweihe                                 const std::vector<HotFuncInfo> &PrintValues,
220453cf5302Sweihe                                 uint64_t HotFuncCount, uint64_t TotalFuncCount,
220553cf5302Sweihe                                 uint64_t HotProfCount, uint64_t TotalProfCount,
220653cf5302Sweihe                                 const std::string &HotFuncMetric,
220753cf5302Sweihe                                 raw_fd_ostream &OS) {
2208*540489deSweihe   assert(ColumnOffset.size() == ColumnTitle.size() &&
2209*540489deSweihe          "ColumnOffset and ColumnTitle should have the same size");
2210*540489deSweihe   assert(ColumnTitle.size() >= 4 &&
2211*540489deSweihe          "ColumnTitle should have at least 4 elements");
2212*540489deSweihe   assert(TotalFuncCount > 0 &&
2213*540489deSweihe          "There should be at least one function in the profile");
221453cf5302Sweihe   double TotalProfPercent = 0;
221553cf5302Sweihe   if (TotalProfCount > 0)
2216*540489deSweihe     TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
221753cf5302Sweihe 
221853cf5302Sweihe   formatted_raw_ostream FOS(OS);
221953cf5302Sweihe   FOS << HotFuncCount << " out of " << TotalFuncCount
222053cf5302Sweihe       << " functions with profile ("
2221*540489deSweihe       << format("%.2f%%",
2222*540489deSweihe                 (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
222353cf5302Sweihe       << ") are considered hot functions";
222453cf5302Sweihe   if (!HotFuncMetric.empty())
222553cf5302Sweihe     FOS << " (" << HotFuncMetric << ")";
222653cf5302Sweihe   FOS << ".\n";
222753cf5302Sweihe   FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
222853cf5302Sweihe       << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
222953cf5302Sweihe 
223053cf5302Sweihe   for (size_t I = 0; I < ColumnTitle.size(); ++I) {
223153cf5302Sweihe     FOS.PadToColumn(ColumnOffset[I]);
223253cf5302Sweihe     FOS << ColumnTitle[I];
223353cf5302Sweihe   }
223453cf5302Sweihe   FOS << "\n";
223553cf5302Sweihe 
2236546be088SFangrui Song   for (const HotFuncInfo &R : PrintValues) {
223753cf5302Sweihe     FOS.PadToColumn(ColumnOffset[0]);
223853cf5302Sweihe     FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
223953cf5302Sweihe     FOS.PadToColumn(ColumnOffset[1]);
224053cf5302Sweihe     FOS << R.MaxCount;
224153cf5302Sweihe     FOS.PadToColumn(ColumnOffset[2]);
224253cf5302Sweihe     FOS << R.EntryCount;
224353cf5302Sweihe     FOS.PadToColumn(ColumnOffset[3]);
224453cf5302Sweihe     FOS << R.FuncName << "\n";
224553cf5302Sweihe   }
224653cf5302Sweihe   return;
224753cf5302Sweihe }
224853cf5302Sweihe 
224953cf5302Sweihe static int
225053cf5302Sweihe showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles,
225153cf5302Sweihe                     ProfileSummary &PS, raw_fd_ostream &OS) {
225253cf5302Sweihe   using namespace sampleprof;
225353cf5302Sweihe 
225453cf5302Sweihe   const uint32_t HotFuncCutoff = 990000;
225553cf5302Sweihe   auto &SummaryVector = PS.getDetailedSummary();
225653cf5302Sweihe   uint64_t MinCountThreshold = 0;
2257546be088SFangrui Song   for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
225853cf5302Sweihe     if (SummaryEntry.Cutoff == HotFuncCutoff) {
225953cf5302Sweihe       MinCountThreshold = SummaryEntry.MinCount;
226053cf5302Sweihe       break;
226153cf5302Sweihe     }
226253cf5302Sweihe   }
226353cf5302Sweihe 
226453cf5302Sweihe   // Traverse all functions in the profile and keep only hot functions.
226553cf5302Sweihe   // The following loop also calculates the sum of total samples of all
226653cf5302Sweihe   // functions.
226753cf5302Sweihe   std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
226853cf5302Sweihe                 std::greater<uint64_t>>
226953cf5302Sweihe       HotFunc;
227053cf5302Sweihe   uint64_t ProfileTotalSample = 0;
227153cf5302Sweihe   uint64_t HotFuncSample = 0;
227253cf5302Sweihe   uint64_t HotFuncCount = 0;
2273*540489deSweihe 
227453cf5302Sweihe   for (const auto &I : Profiles) {
2275*540489deSweihe     FuncSampleStats FuncStats;
2276546be088SFangrui Song     const FunctionSamples &FuncProf = I.second;
227753cf5302Sweihe     ProfileTotalSample += FuncProf.getTotalSamples();
2278*540489deSweihe     getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
227953cf5302Sweihe 
2280*540489deSweihe     if (isFunctionHot(FuncStats, MinCountThreshold)) {
228153cf5302Sweihe       HotFunc.emplace(FuncProf.getTotalSamples(),
2282*540489deSweihe                       std::make_pair(&(I.second), FuncStats.MaxSample));
228353cf5302Sweihe       HotFuncSample += FuncProf.getTotalSamples();
228453cf5302Sweihe       ++HotFuncCount;
228553cf5302Sweihe     }
228653cf5302Sweihe   }
228753cf5302Sweihe 
228853cf5302Sweihe   std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
228953cf5302Sweihe                                        "Entry sample", "Function name"};
229053cf5302Sweihe   std::vector<int> ColumnOffset{0, 24, 42, 58};
229153cf5302Sweihe   std::string Metric =
229253cf5302Sweihe       std::string("max sample >= ") + std::to_string(MinCountThreshold);
229353cf5302Sweihe   std::vector<HotFuncInfo> PrintValues;
229453cf5302Sweihe   for (const auto &FuncPair : HotFunc) {
2295546be088SFangrui Song     const FunctionSamples &Func = *FuncPair.second.first;
229653cf5302Sweihe     double TotalSamplePercent =
229753cf5302Sweihe         (ProfileTotalSample > 0)
2298546be088SFangrui Song             ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
229953cf5302Sweihe             : 0;
230053cf5302Sweihe     PrintValues.emplace_back(HotFuncInfo(
2301546be088SFangrui Song         Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent,
2302546be088SFangrui Song         FuncPair.second.second, Func.getEntrySamples()));
230353cf5302Sweihe   }
230453cf5302Sweihe   dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
230553cf5302Sweihe                       Profiles.size(), HotFuncSample, ProfileTotalSample,
230653cf5302Sweihe                       Metric, OS);
230753cf5302Sweihe 
230853cf5302Sweihe   return 0;
230953cf5302Sweihe }
231053cf5302Sweihe 
23111afc1de4SBenjamin Kramer static int showSampleProfile(const std::string &Filename, bool ShowCounts,
231217fc6518SWenlei He                              bool ShowAllFunctions, bool ShowDetailedSummary,
23131afc1de4SBenjamin Kramer                              const std::string &ShowFunction,
2314eee532cdSWei Mi                              bool ShowProfileSymbolList,
231553cf5302Sweihe                              bool ShowSectionInfoOnly, bool ShowHotFuncList,
231653cf5302Sweihe                              raw_fd_ostream &OS) {
2317d5336ae2SDiego Novillo   using namespace sampleprof;
231803b42e41SMehdi Amini   LLVMContext Context;
231903b42e41SMehdi Amini   auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
2320fcd55607SDiego Novillo   if (std::error_code EC = ReaderOrErr.getError())
23214f823667SNathan Slingerland     exitWithErrorCode(EC, Filename);
2322d5336ae2SDiego Novillo 
2323fcd55607SDiego Novillo   auto Reader = std::move(ReaderOrErr.get());
2324eee532cdSWei Mi 
2325eee532cdSWei Mi   if (ShowSectionInfoOnly) {
2326eee532cdSWei Mi     showSectionInfo(Reader.get(), OS);
2327eee532cdSWei Mi     return 0;
2328eee532cdSWei Mi   }
2329eee532cdSWei Mi 
2330c6d032abSDiego Novillo   if (std::error_code EC = Reader->read())
23314f823667SNathan Slingerland     exitWithErrorCode(EC, Filename);
2332c6d032abSDiego Novillo 
2333d5336ae2SDiego Novillo   if (ShowAllFunctions || ShowFunction.empty())
2334d5336ae2SDiego Novillo     Reader->dump(OS);
2335d5336ae2SDiego Novillo   else
2336d5336ae2SDiego Novillo     Reader->dumpFunctionProfile(ShowFunction, OS);
2337d5336ae2SDiego Novillo 
2338798e59b8SWei Mi   if (ShowProfileSymbolList) {
2339798e59b8SWei Mi     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
2340798e59b8SWei Mi         Reader->getProfileSymbolList();
2341798e59b8SWei Mi     ReaderList->dump(OS);
2342798e59b8SWei Mi   }
2343798e59b8SWei Mi 
234417fc6518SWenlei He   if (ShowDetailedSummary) {
234517fc6518SWenlei He     auto &PS = Reader->getSummary();
234617fc6518SWenlei He     PS.printSummary(OS);
234717fc6518SWenlei He     PS.printDetailedSummary(OS);
234817fc6518SWenlei He   }
234917fc6518SWenlei He 
235053cf5302Sweihe   if (ShowHotFuncList)
235153cf5302Sweihe     showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS);
235253cf5302Sweihe 
2353d5336ae2SDiego Novillo   return 0;
2354d5336ae2SDiego Novillo }
2355d5336ae2SDiego Novillo 
2356f044d3f9SBenjamin Kramer static int show_main(int argc, const char *argv[]) {
2357d5336ae2SDiego Novillo   cl::opt<std::string> Filename(cl::Positional, cl::Required,
2358d5336ae2SDiego Novillo                                 cl::desc("<profdata-file>"));
2359d5336ae2SDiego Novillo 
2360d5336ae2SDiego Novillo   cl::opt<bool> ShowCounts("counts", cl::init(false),
2361d5336ae2SDiego Novillo                            cl::desc("Show counter values for shown functions"));
23626f7c19a4SXinliang David Li   cl::opt<bool> TextFormat(
23636f7c19a4SXinliang David Li       "text", cl::init(false),
23646f7c19a4SXinliang David Li       cl::desc("Show instr profile data in text dump format"));
23659e9a057aSJustin Bogner   cl::opt<bool> ShowIndirectCallTargets(
23669e9a057aSJustin Bogner       "ic-targets", cl::init(false),
23679e9a057aSJustin Bogner       cl::desc("Show indirect call site target values for shown functions"));
236860faea19SRong Xu   cl::opt<bool> ShowMemOPSizes(
236960faea19SRong Xu       "memop-sizes", cl::init(false),
237060faea19SRong Xu       cl::desc("Show the profiled sizes of the memory intrinsic calls "
237160faea19SRong Xu                "for shown functions"));
2372183ebbe0SEaswaran Raman   cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
2373183ebbe0SEaswaran Raman                                     cl::desc("Show detailed profile summary"));
2374183ebbe0SEaswaran Raman   cl::list<uint32_t> DetailedSummaryCutoffs(
2375183ebbe0SEaswaran Raman       cl::CommaSeparated, "detailed-summary-cutoffs",
2376183ebbe0SEaswaran Raman       cl::desc(
2377183ebbe0SEaswaran Raman           "Cutoff percentages (times 10000) for generating detailed summary"),
2378183ebbe0SEaswaran Raman       cl::value_desc("800000,901000,999999"));
237953cf5302Sweihe   cl::opt<bool> ShowHotFuncList(
238053cf5302Sweihe       "hot-func-list", cl::init(false),
238153cf5302Sweihe       cl::desc("Show profile summary of a list of hot functions"));
2382d5336ae2SDiego Novillo   cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
2383d5336ae2SDiego Novillo                                  cl::desc("Details for every function"));
2384a6ff69f6SRong Xu   cl::opt<bool> ShowCS("showcs", cl::init(false),
2385a6ff69f6SRong Xu                        cl::desc("Show context sensitive counts"));
2386d5336ae2SDiego Novillo   cl::opt<std::string> ShowFunction("function",
2387d5336ae2SDiego Novillo                                     cl::desc("Details for matching functions"));
2388d5336ae2SDiego Novillo 
2389d5336ae2SDiego Novillo   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
2390d5336ae2SDiego Novillo                                       cl::init("-"), cl::desc("Output file"));
2391d5336ae2SDiego Novillo   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
2392d5336ae2SDiego Novillo                             cl::aliasopt(OutputFilename));
2393d5336ae2SDiego Novillo   cl::opt<ProfileKinds> ProfileKind(
2394d5336ae2SDiego Novillo       cl::desc("Profile kind:"), cl::init(instr),
2395d5336ae2SDiego Novillo       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
2396732afdd0SMehdi Amini                  clEnumVal(sample, "Sample profile")));
2397801b5319SXinliang David Li   cl::opt<uint32_t> TopNFunctions(
2398801b5319SXinliang David Li       "topn", cl::init(0),
2399801b5319SXinliang David Li       cl::desc("Show the list of functions with the largest internal counts"));
240052aa224aSRong Xu   cl::opt<uint32_t> ValueCutoff(
240152aa224aSRong Xu       "value-cutoff", cl::init(0),
240252aa224aSRong Xu       cl::desc("Set the count value cutoff. Functions with the maximum count "
240352aa224aSRong Xu                "less than this value will not be printed out. (Default is 0)"));
240452aa224aSRong Xu   cl::opt<bool> OnlyListBelow(
240552aa224aSRong Xu       "list-below-cutoff", cl::init(false),
240652aa224aSRong Xu       cl::desc("Only output names of functions whose max count values are "
240752aa224aSRong Xu                "below the cutoff value"));
2408798e59b8SWei Mi   cl::opt<bool> ShowProfileSymbolList(
2409798e59b8SWei Mi       "show-prof-sym-list", cl::init(false),
2410798e59b8SWei Mi       cl::desc("Show profile symbol list if it exists in the profile. "));
2411eee532cdSWei Mi   cl::opt<bool> ShowSectionInfoOnly(
2412eee532cdSWei Mi       "show-sec-info-only", cl::init(false),
2413eee532cdSWei Mi       cl::desc("Show the information of each section in the sample profile. "
2414eee532cdSWei Mi                "The flag is only usable when the sample profile is in "
2415eee532cdSWei Mi                "extbinary format"));
2416798e59b8SWei Mi 
2417d5336ae2SDiego Novillo   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
2418d5336ae2SDiego Novillo 
2419d5336ae2SDiego Novillo   if (OutputFilename.empty())
2420d5336ae2SDiego Novillo     OutputFilename = "-";
2421d5336ae2SDiego Novillo 
2422f0d3dcecSRong Xu   if (!Filename.compare(OutputFilename)) {
2423f0d3dcecSRong Xu     errs() << sys::path::filename(argv[0])
2424f0d3dcecSRong Xu            << ": Input file name cannot be the same as the output file name!\n";
2425f0d3dcecSRong Xu     return 1;
2426f0d3dcecSRong Xu   }
2427f0d3dcecSRong Xu 
2428d5336ae2SDiego Novillo   std::error_code EC;
2429d9b948b6SFangrui Song   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text);
2430d5336ae2SDiego Novillo   if (EC)
24314f823667SNathan Slingerland     exitWithErrorCode(EC, OutputFilename);
2432d5336ae2SDiego Novillo 
2433d5336ae2SDiego Novillo   if (ShowAllFunctions && !ShowFunction.empty())
2434e46b7565SJonas Devlieghere     WithColor::warning() << "-function argument ignored: showing all functions\n";
2435d5336ae2SDiego Novillo 
2436d5336ae2SDiego Novillo   if (ProfileKind == instr)
2437801b5319SXinliang David Li     return showInstrProfile(Filename, ShowCounts, TopNFunctions,
2438801b5319SXinliang David Li                             ShowIndirectCallTargets, ShowMemOPSizes,
2439801b5319SXinliang David Li                             ShowDetailedSummary, DetailedSummaryCutoffs,
2440a6ff69f6SRong Xu                             ShowAllFunctions, ShowCS, ValueCutoff,
2441a6ff69f6SRong Xu                             OnlyListBelow, ShowFunction, TextFormat, OS);
2442d5336ae2SDiego Novillo   else
2443d5336ae2SDiego Novillo     return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
244417fc6518SWenlei He                              ShowDetailedSummary, ShowFunction,
244553cf5302Sweihe                              ShowProfileSymbolList, ShowSectionInfoOnly,
244653cf5302Sweihe                              ShowHotFuncList, OS);
2447d5336ae2SDiego Novillo }
2448d5336ae2SDiego Novillo 
2449618bcea7SJustin Bogner int main(int argc, const char *argv[]) {
2450197194b6SRui Ueyama   InitLLVM X(argc, argv);
2451618bcea7SJustin Bogner 
2452618bcea7SJustin Bogner   StringRef ProgName(sys::path::filename(argv[0]));
2453618bcea7SJustin Bogner   if (argc > 1) {
2454e6cb63e4SCraig Topper     int (*func)(int, const char *[]) = nullptr;
2455618bcea7SJustin Bogner 
2456618bcea7SJustin Bogner     if (strcmp(argv[1], "merge") == 0)
2457618bcea7SJustin Bogner       func = merge_main;
24589af28ef9SJustin Bogner     else if (strcmp(argv[1], "show") == 0)
24599af28ef9SJustin Bogner       func = show_main;
2460998b97f6SRong Xu     else if (strcmp(argv[1], "overlap") == 0)
2461998b97f6SRong Xu       func = overlap_main;
2462618bcea7SJustin Bogner 
2463618bcea7SJustin Bogner     if (func) {
2464618bcea7SJustin Bogner       std::string Invocation(ProgName.str() + " " + argv[1]);
2465618bcea7SJustin Bogner       argv[1] = Invocation.c_str();
2466618bcea7SJustin Bogner       return func(argc - 1, argv + 1);
2467618bcea7SJustin Bogner     }
2468618bcea7SJustin Bogner 
2469d3babdbcSDiego Novillo     if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
2470618bcea7SJustin Bogner         strcmp(argv[1], "--help") == 0) {
2471618bcea7SJustin Bogner 
2472618bcea7SJustin Bogner       errs() << "OVERVIEW: LLVM profile data tools\n\n"
2473618bcea7SJustin Bogner              << "USAGE: " << ProgName << " <command> [args...]\n"
2474618bcea7SJustin Bogner              << "USAGE: " << ProgName << " <command> -help\n\n"
2475253eb17bSJustin Bogner              << "See each individual command --help for more details.\n"
2476998b97f6SRong Xu              << "Available commands: merge, show, overlap\n";
2477618bcea7SJustin Bogner       return 0;
2478618bcea7SJustin Bogner     }
2479618bcea7SJustin Bogner   }
2480618bcea7SJustin Bogner 
2481618bcea7SJustin Bogner   if (argc < 2)
2482618bcea7SJustin Bogner     errs() << ProgName << ": No command specified!\n";
2483618bcea7SJustin Bogner   else
2484618bcea7SJustin Bogner     errs() << ProgName << ": Unknown command!\n";
2485618bcea7SJustin Bogner 
2486998b97f6SRong Xu   errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n";
2487618bcea7SJustin Bogner   return 1;
2488618bcea7SJustin Bogner }
2489