xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
1 //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // llvm-profdata merges .profdata files.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/ADT/SmallSet.h"
14 #include "llvm/ADT/SmallVector.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/IR/LLVMContext.h"
17 #include "llvm/ProfileData/InstrProfReader.h"
18 #include "llvm/ProfileData/InstrProfWriter.h"
19 #include "llvm/ProfileData/ProfileCommon.h"
20 #include "llvm/ProfileData/SampleProfReader.h"
21 #include "llvm/ProfileData/SampleProfWriter.h"
22 #include "llvm/Support/CommandLine.h"
23 #include "llvm/Support/Errc.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/Format.h"
26 #include "llvm/Support/FormattedStream.h"
27 #include "llvm/Support/InitLLVM.h"
28 #include "llvm/Support/MemoryBuffer.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/ThreadPool.h"
31 #include "llvm/Support/Threading.h"
32 #include "llvm/Support/WithColor.h"
33 #include "llvm/Support/raw_ostream.h"
34 #include <algorithm>
35 
36 using namespace llvm;
37 
38 enum ProfileFormat {
39   PF_None = 0,
40   PF_Text,
41   PF_Compact_Binary,
42   PF_Ext_Binary,
43   PF_GCC,
44   PF_Binary
45 };
46 
47 static void warn(Twine Message, std::string Whence = "",
48                  std::string Hint = "") {
49   WithColor::warning();
50   if (!Whence.empty())
51     errs() << Whence << ": ";
52   errs() << Message << "\n";
53   if (!Hint.empty())
54     WithColor::note() << Hint << "\n";
55 }
56 
57 static void exitWithError(Twine Message, std::string Whence = "",
58                           std::string Hint = "") {
59   WithColor::error();
60   if (!Whence.empty())
61     errs() << Whence << ": ";
62   errs() << Message << "\n";
63   if (!Hint.empty())
64     WithColor::note() << Hint << "\n";
65   ::exit(1);
66 }
67 
68 static void exitWithError(Error E, StringRef Whence = "") {
69   if (E.isA<InstrProfError>()) {
70     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
71       instrprof_error instrError = IPE.get();
72       StringRef Hint = "";
73       if (instrError == instrprof_error::unrecognized_format) {
74         // Hint for common error of forgetting --sample for sample profiles.
75         Hint = "Perhaps you forgot to use the --sample option?";
76       }
77       exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
78     });
79   }
80 
81   exitWithError(toString(std::move(E)), std::string(Whence));
82 }
83 
84 static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
85   exitWithError(EC.message(), std::string(Whence));
86 }
87 
88 namespace {
89 enum ProfileKinds { instr, sample };
90 enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid };
91 }
92 
93 static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
94                                  StringRef Whence = "") {
95   if (FailMode == failIfAnyAreInvalid)
96     exitWithErrorCode(EC, Whence);
97   else
98     warn(EC.message(), std::string(Whence));
99 }
100 
101 static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
102                                    StringRef WhenceFunction = "",
103                                    bool ShowHint = true) {
104   if (!WhenceFile.empty())
105     errs() << WhenceFile << ": ";
106   if (!WhenceFunction.empty())
107     errs() << WhenceFunction << ": ";
108 
109   auto IPE = instrprof_error::success;
110   E = handleErrors(std::move(E),
111                    [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
112                      IPE = E->get();
113                      return Error(std::move(E));
114                    });
115   errs() << toString(std::move(E)) << "\n";
116 
117   if (ShowHint) {
118     StringRef Hint = "";
119     if (IPE != instrprof_error::success) {
120       switch (IPE) {
121       case instrprof_error::hash_mismatch:
122       case instrprof_error::count_mismatch:
123       case instrprof_error::value_site_count_mismatch:
124         Hint = "Make sure that all profile data to be merged is generated "
125                "from the same binary.";
126         break;
127       default:
128         break;
129       }
130     }
131 
132     if (!Hint.empty())
133       errs() << Hint << "\n";
134   }
135 }
136 
137 namespace {
138 /// A remapper from original symbol names to new symbol names based on a file
139 /// containing a list of mappings from old name to new name.
140 class SymbolRemapper {
141   std::unique_ptr<MemoryBuffer> File;
142   DenseMap<StringRef, StringRef> RemappingTable;
143 
144 public:
145   /// Build a SymbolRemapper from a file containing a list of old/new symbols.
146   static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
147     auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
148     if (!BufOrError)
149       exitWithErrorCode(BufOrError.getError(), InputFile);
150 
151     auto Remapper = std::make_unique<SymbolRemapper>();
152     Remapper->File = std::move(BufOrError.get());
153 
154     for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
155          !LineIt.is_at_eof(); ++LineIt) {
156       std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
157       if (Parts.first.empty() || Parts.second.empty() ||
158           Parts.second.count(' ')) {
159         exitWithError("unexpected line in remapping file",
160                       (InputFile + ":" + Twine(LineIt.line_number())).str(),
161                       "expected 'old_symbol new_symbol'");
162       }
163       Remapper->RemappingTable.insert(Parts);
164     }
165     return Remapper;
166   }
167 
168   /// Attempt to map the given old symbol into a new symbol.
169   ///
170   /// \return The new symbol, or \p Name if no such symbol was found.
171   StringRef operator()(StringRef Name) {
172     StringRef New = RemappingTable.lookup(Name);
173     return New.empty() ? Name : New;
174   }
175 };
176 }
177 
178 struct WeightedFile {
179   std::string Filename;
180   uint64_t Weight;
181 };
182 typedef SmallVector<WeightedFile, 5> WeightedFileVector;
183 
184 /// Keep track of merged data and reported errors.
185 struct WriterContext {
186   std::mutex Lock;
187   InstrProfWriter Writer;
188   std::vector<std::pair<Error, std::string>> Errors;
189   std::mutex &ErrLock;
190   SmallSet<instrprof_error, 4> &WriterErrorCodes;
191 
192   WriterContext(bool IsSparse, std::mutex &ErrLock,
193                 SmallSet<instrprof_error, 4> &WriterErrorCodes)
194       : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock),
195         WriterErrorCodes(WriterErrorCodes) {}
196 };
197 
198 /// Computer the overlap b/w profile BaseFilename and TestFileName,
199 /// and store the program level result to Overlap.
200 static void overlapInput(const std::string &BaseFilename,
201                          const std::string &TestFilename, WriterContext *WC,
202                          OverlapStats &Overlap,
203                          const OverlapFuncFilters &FuncFilter,
204                          raw_fd_ostream &OS, bool IsCS) {
205   auto ReaderOrErr = InstrProfReader::create(TestFilename);
206   if (Error E = ReaderOrErr.takeError()) {
207     // Skip the empty profiles by returning sliently.
208     instrprof_error IPE = InstrProfError::take(std::move(E));
209     if (IPE != instrprof_error::empty_raw_profile)
210       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename);
211     return;
212   }
213 
214   auto Reader = std::move(ReaderOrErr.get());
215   for (auto &I : *Reader) {
216     OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
217     FuncOverlap.setFuncInfo(I.Name, I.Hash);
218 
219     WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
220     FuncOverlap.dump(OS);
221   }
222 }
223 
224 /// Load an input into a writer context.
225 static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
226                       WriterContext *WC) {
227   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
228 
229   // Copy the filename, because llvm::ThreadPool copied the input "const
230   // WeightedFile &" by value, making a reference to the filename within it
231   // invalid outside of this packaged task.
232   std::string Filename = Input.Filename;
233 
234   auto ReaderOrErr = InstrProfReader::create(Input.Filename);
235   if (Error E = ReaderOrErr.takeError()) {
236     // Skip the empty profiles by returning sliently.
237     instrprof_error IPE = InstrProfError::take(std::move(E));
238     if (IPE != instrprof_error::empty_raw_profile)
239       WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
240     return;
241   }
242 
243   auto Reader = std::move(ReaderOrErr.get());
244   bool IsIRProfile = Reader->isIRLevelProfile();
245   bool HasCSIRProfile = Reader->hasCSIRLevelProfile();
246   if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) {
247     WC->Errors.emplace_back(
248         make_error<StringError>(
249             "Merge IR generated profile with Clang generated profile.",
250             std::error_code()),
251         Filename);
252     return;
253   }
254 
255   for (auto &I : *Reader) {
256     if (Remapper)
257       I.Name = (*Remapper)(I.Name);
258     const StringRef FuncName = I.Name;
259     bool Reported = false;
260     WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
261       if (Reported) {
262         consumeError(std::move(E));
263         return;
264       }
265       Reported = true;
266       // Only show hint the first time an error occurs.
267       instrprof_error IPE = InstrProfError::take(std::move(E));
268       std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
269       bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
270       handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
271                              FuncName, firstTime);
272     });
273   }
274   if (Reader->hasError())
275     if (Error E = Reader->getError())
276       WC->Errors.emplace_back(std::move(E), Filename);
277 }
278 
279 /// Merge the \p Src writer context into \p Dst.
280 static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
281   for (auto &ErrorPair : Src->Errors)
282     Dst->Errors.push_back(std::move(ErrorPair));
283   Src->Errors.clear();
284 
285   Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
286     instrprof_error IPE = InstrProfError::take(std::move(E));
287     std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
288     bool firstTime = Dst->WriterErrorCodes.insert(IPE).second;
289     if (firstTime)
290       warn(toString(make_error<InstrProfError>(IPE)));
291   });
292 }
293 
294 static void writeInstrProfile(StringRef OutputFilename,
295                               ProfileFormat OutputFormat,
296                               InstrProfWriter &Writer) {
297   std::error_code EC;
298   raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None);
299   if (EC)
300     exitWithErrorCode(EC, OutputFilename);
301 
302   if (OutputFormat == PF_Text) {
303     if (Error E = Writer.writeText(Output))
304       exitWithError(std::move(E));
305   } else {
306     Writer.write(Output);
307   }
308 }
309 
310 static void mergeInstrProfile(const WeightedFileVector &Inputs,
311                               SymbolRemapper *Remapper,
312                               StringRef OutputFilename,
313                               ProfileFormat OutputFormat, bool OutputSparse,
314                               unsigned NumThreads, FailureMode FailMode) {
315   if (OutputFilename.compare("-") == 0)
316     exitWithError("Cannot write indexed profdata format to stdout.");
317 
318   if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
319       OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text)
320     exitWithError("Unknown format is specified.");
321 
322   std::mutex ErrorLock;
323   SmallSet<instrprof_error, 4> WriterErrorCodes;
324 
325   // If NumThreads is not specified, auto-detect a good default.
326   if (NumThreads == 0)
327     NumThreads = std::min(hardware_concurrency().compute_thread_count(),
328                           unsigned((Inputs.size() + 1) / 2));
329   // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails
330   // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't
331   // merged, thus the emitted file ends up with a PF_Unknown kind.
332 
333   // Initialize the writer contexts.
334   SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
335   for (unsigned I = 0; I < NumThreads; ++I)
336     Contexts.emplace_back(std::make_unique<WriterContext>(
337         OutputSparse, ErrorLock, WriterErrorCodes));
338 
339   if (NumThreads == 1) {
340     for (const auto &Input : Inputs)
341       loadInput(Input, Remapper, Contexts[0].get());
342   } else {
343     ThreadPool Pool(hardware_concurrency(NumThreads));
344 
345     // Load the inputs in parallel (N/NumThreads serial steps).
346     unsigned Ctx = 0;
347     for (const auto &Input : Inputs) {
348       Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get());
349       Ctx = (Ctx + 1) % NumThreads;
350     }
351     Pool.wait();
352 
353     // Merge the writer contexts together (~ lg(NumThreads) serial steps).
354     unsigned Mid = Contexts.size() / 2;
355     unsigned End = Contexts.size();
356     assert(Mid > 0 && "Expected more than one context");
357     do {
358       for (unsigned I = 0; I < Mid; ++I)
359         Pool.async(mergeWriterContexts, Contexts[I].get(),
360                    Contexts[I + Mid].get());
361       Pool.wait();
362       if (End & 1) {
363         Pool.async(mergeWriterContexts, Contexts[0].get(),
364                    Contexts[End - 1].get());
365         Pool.wait();
366       }
367       End = Mid;
368       Mid /= 2;
369     } while (Mid > 0);
370   }
371 
372   // Handle deferred errors encountered during merging. If the number of errors
373   // is equal to the number of inputs the merge failed.
374   unsigned NumErrors = 0;
375   for (std::unique_ptr<WriterContext> &WC : Contexts) {
376     for (auto &ErrorPair : WC->Errors) {
377       ++NumErrors;
378       warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
379     }
380   }
381   if (NumErrors == Inputs.size() ||
382       (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
383     exitWithError("No profiles could be merged.");
384 
385   writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
386 }
387 
388 /// Make a copy of the given function samples with all symbol names remapped
389 /// by the provided symbol remapper.
390 static sampleprof::FunctionSamples
391 remapSamples(const sampleprof::FunctionSamples &Samples,
392              SymbolRemapper &Remapper, sampleprof_error &Error) {
393   sampleprof::FunctionSamples Result;
394   Result.setName(Remapper(Samples.getName()));
395   Result.addTotalSamples(Samples.getTotalSamples());
396   Result.addHeadSamples(Samples.getHeadSamples());
397   for (const auto &BodySample : Samples.getBodySamples()) {
398     Result.addBodySamples(BodySample.first.LineOffset,
399                           BodySample.first.Discriminator,
400                           BodySample.second.getSamples());
401     for (const auto &Target : BodySample.second.getCallTargets()) {
402       Result.addCalledTargetSamples(BodySample.first.LineOffset,
403                                     BodySample.first.Discriminator,
404                                     Remapper(Target.first()), Target.second);
405     }
406   }
407   for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
408     sampleprof::FunctionSamplesMap &Target =
409         Result.functionSamplesAt(CallsiteSamples.first);
410     for (const auto &Callsite : CallsiteSamples.second) {
411       sampleprof::FunctionSamples Remapped =
412           remapSamples(Callsite.second, Remapper, Error);
413       MergeResult(Error,
414                   Target[std::string(Remapped.getName())].merge(Remapped));
415     }
416   }
417   return Result;
418 }
419 
420 static sampleprof::SampleProfileFormat FormatMap[] = {
421     sampleprof::SPF_None,
422     sampleprof::SPF_Text,
423     sampleprof::SPF_Compact_Binary,
424     sampleprof::SPF_Ext_Binary,
425     sampleprof::SPF_GCC,
426     sampleprof::SPF_Binary};
427 
428 static std::unique_ptr<MemoryBuffer>
429 getInputFileBuf(const StringRef &InputFile) {
430   if (InputFile == "")
431     return {};
432 
433   auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
434   if (!BufOrError)
435     exitWithErrorCode(BufOrError.getError(), InputFile);
436 
437   return std::move(*BufOrError);
438 }
439 
440 static void populateProfileSymbolList(MemoryBuffer *Buffer,
441                                       sampleprof::ProfileSymbolList &PSL) {
442   if (!Buffer)
443     return;
444 
445   SmallVector<StringRef, 32> SymbolVec;
446   StringRef Data = Buffer->getBuffer();
447   Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
448 
449   for (StringRef symbol : SymbolVec)
450     PSL.add(symbol);
451 }
452 
453 static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
454                                   ProfileFormat OutputFormat,
455                                   MemoryBuffer *Buffer,
456                                   sampleprof::ProfileSymbolList &WriterList,
457                                   bool CompressAllSections, bool UseMD5,
458                                   bool GenPartialProfile) {
459   populateProfileSymbolList(Buffer, WriterList);
460   if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
461     warn("Profile Symbol list is not empty but the output format is not "
462          "ExtBinary format. The list will be lost in the output. ");
463 
464   Writer.setProfileSymbolList(&WriterList);
465 
466   if (CompressAllSections) {
467     if (OutputFormat != PF_Ext_Binary)
468       warn("-compress-all-section is ignored. Specify -extbinary to enable it");
469     else
470       Writer.setToCompressAllSections();
471   }
472   if (UseMD5) {
473     if (OutputFormat != PF_Ext_Binary)
474       warn("-use-md5 is ignored. Specify -extbinary to enable it");
475     else
476       Writer.setUseMD5();
477   }
478   if (GenPartialProfile) {
479     if (OutputFormat != PF_Ext_Binary)
480       warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
481     else
482       Writer.setPartialProfile();
483   }
484 }
485 
486 static void
487 mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
488                    StringRef OutputFilename, ProfileFormat OutputFormat,
489                    StringRef ProfileSymbolListFile, bool CompressAllSections,
490                    bool UseMD5, bool GenPartialProfile, FailureMode FailMode) {
491   using namespace sampleprof;
492   StringMap<FunctionSamples> ProfileMap;
493   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
494   LLVMContext Context;
495   sampleprof::ProfileSymbolList WriterList;
496   for (const auto &Input : Inputs) {
497     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
498     if (std::error_code EC = ReaderOrErr.getError()) {
499       warnOrExitGivenError(FailMode, EC, Input.Filename);
500       continue;
501     }
502 
503     // We need to keep the readers around until after all the files are
504     // read so that we do not lose the function names stored in each
505     // reader's memory. The function names are needed to write out the
506     // merged profile map.
507     Readers.push_back(std::move(ReaderOrErr.get()));
508     const auto Reader = Readers.back().get();
509     if (std::error_code EC = Reader->read()) {
510       warnOrExitGivenError(FailMode, EC, Input.Filename);
511       Readers.pop_back();
512       continue;
513     }
514 
515     StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
516     for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
517                                               E = Profiles.end();
518          I != E; ++I) {
519       sampleprof_error Result = sampleprof_error::success;
520       FunctionSamples Remapped =
521           Remapper ? remapSamples(I->second, *Remapper, Result)
522                    : FunctionSamples();
523       FunctionSamples &Samples = Remapper ? Remapped : I->second;
524       StringRef FName = Samples.getName();
525       MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight));
526       if (Result != sampleprof_error::success) {
527         std::error_code EC = make_error_code(Result);
528         handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
529       }
530     }
531 
532     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
533         Reader->getProfileSymbolList();
534     if (ReaderList)
535       WriterList.merge(*ReaderList);
536   }
537   auto WriterOrErr =
538       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
539   if (std::error_code EC = WriterOrErr.getError())
540     exitWithErrorCode(EC, OutputFilename);
541 
542   auto Writer = std::move(WriterOrErr.get());
543   // WriterList will have StringRef refering to string in Buffer.
544   // Make sure Buffer lives as long as WriterList.
545   auto Buffer = getInputFileBuf(ProfileSymbolListFile);
546   handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
547                         CompressAllSections, UseMD5, GenPartialProfile);
548   Writer->write(ProfileMap);
549 }
550 
551 static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
552   StringRef WeightStr, FileName;
553   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
554 
555   uint64_t Weight;
556   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
557     exitWithError("Input weight must be a positive integer.");
558 
559   return {std::string(FileName), Weight};
560 }
561 
562 static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
563   StringRef Filename = WF.Filename;
564   uint64_t Weight = WF.Weight;
565 
566   // If it's STDIN just pass it on.
567   if (Filename == "-") {
568     WNI.push_back({std::string(Filename), Weight});
569     return;
570   }
571 
572   llvm::sys::fs::file_status Status;
573   llvm::sys::fs::status(Filename, Status);
574   if (!llvm::sys::fs::exists(Status))
575     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
576                       Filename);
577   // If it's a source file, collect it.
578   if (llvm::sys::fs::is_regular_file(Status)) {
579     WNI.push_back({std::string(Filename), Weight});
580     return;
581   }
582 
583   if (llvm::sys::fs::is_directory(Status)) {
584     std::error_code EC;
585     for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
586          F != E && !EC; F.increment(EC)) {
587       if (llvm::sys::fs::is_regular_file(F->path())) {
588         addWeightedInput(WNI, {F->path(), Weight});
589       }
590     }
591     if (EC)
592       exitWithErrorCode(EC, Filename);
593   }
594 }
595 
596 static void parseInputFilenamesFile(MemoryBuffer *Buffer,
597                                     WeightedFileVector &WFV) {
598   if (!Buffer)
599     return;
600 
601   SmallVector<StringRef, 8> Entries;
602   StringRef Data = Buffer->getBuffer();
603   Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
604   for (const StringRef &FileWeightEntry : Entries) {
605     StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
606     // Skip comments.
607     if (SanitizedEntry.startswith("#"))
608       continue;
609     // If there's no comma, it's an unweighted profile.
610     else if (SanitizedEntry.find(',') == StringRef::npos)
611       addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
612     else
613       addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
614   }
615 }
616 
617 static int merge_main(int argc, const char *argv[]) {
618   cl::list<std::string> InputFilenames(cl::Positional,
619                                        cl::desc("<filename...>"));
620   cl::list<std::string> WeightedInputFilenames("weighted-input",
621                                                cl::desc("<weight>,<filename>"));
622   cl::opt<std::string> InputFilenamesFile(
623       "input-files", cl::init(""),
624       cl::desc("Path to file containing newline-separated "
625                "[<weight>,]<filename> entries"));
626   cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
627                                 cl::aliasopt(InputFilenamesFile));
628   cl::opt<bool> DumpInputFileList(
629       "dump-input-file-list", cl::init(false), cl::Hidden,
630       cl::desc("Dump the list of input files and their weights, then exit"));
631   cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
632                                      cl::desc("Symbol remapping file"));
633   cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
634                            cl::aliasopt(RemappingFile));
635   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
636                                       cl::init("-"), cl::Required,
637                                       cl::desc("Output file"));
638   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
639                             cl::aliasopt(OutputFilename));
640   cl::opt<ProfileKinds> ProfileKind(
641       cl::desc("Profile kind:"), cl::init(instr),
642       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
643                  clEnumVal(sample, "Sample profile")));
644   cl::opt<ProfileFormat> OutputFormat(
645       cl::desc("Format of output profile"), cl::init(PF_Binary),
646       cl::values(
647           clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
648           clEnumValN(PF_Compact_Binary, "compbinary",
649                      "Compact binary encoding"),
650           clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"),
651           clEnumValN(PF_Text, "text", "Text encoding"),
652           clEnumValN(PF_GCC, "gcc",
653                      "GCC encoding (only meaningful for -sample)")));
654   cl::opt<FailureMode> FailureMode(
655       "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"),
656       cl::values(clEnumValN(failIfAnyAreInvalid, "any",
657                             "Fail if any profile is invalid."),
658                  clEnumValN(failIfAllAreInvalid, "all",
659                             "Fail only if all profiles are invalid.")));
660   cl::opt<bool> OutputSparse("sparse", cl::init(false),
661       cl::desc("Generate a sparse profile (only meaningful for -instr)"));
662   cl::opt<unsigned> NumThreads(
663       "num-threads", cl::init(0),
664       cl::desc("Number of merge threads to use (default: autodetect)"));
665   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
666                         cl::aliasopt(NumThreads));
667   cl::opt<std::string> ProfileSymbolListFile(
668       "prof-sym-list", cl::init(""),
669       cl::desc("Path to file containing the list of function symbols "
670                "used to populate profile symbol list"));
671   cl::opt<bool> CompressAllSections(
672       "compress-all-sections", cl::init(false), cl::Hidden,
673       cl::desc("Compress all sections when writing the profile (only "
674                "meaningful for -extbinary)"));
675   cl::opt<bool> UseMD5(
676       "use-md5", cl::init(false), cl::Hidden,
677       cl::desc("Choose to use MD5 to represent string in name table (only "
678                "meaningful for -extbinary)"));
679   cl::opt<bool> GenPartialProfile(
680       "gen-partial-profile", cl::init(false), cl::Hidden,
681       cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
682 
683   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
684 
685   WeightedFileVector WeightedInputs;
686   for (StringRef Filename : InputFilenames)
687     addWeightedInput(WeightedInputs, {std::string(Filename), 1});
688   for (StringRef WeightedFilename : WeightedInputFilenames)
689     addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
690 
691   // Make sure that the file buffer stays alive for the duration of the
692   // weighted input vector's lifetime.
693   auto Buffer = getInputFileBuf(InputFilenamesFile);
694   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
695 
696   if (WeightedInputs.empty())
697     exitWithError("No input files specified. See " +
698                   sys::path::filename(argv[0]) + " -help");
699 
700   if (DumpInputFileList) {
701     for (auto &WF : WeightedInputs)
702       outs() << WF.Weight << "," << WF.Filename << "\n";
703     return 0;
704   }
705 
706   std::unique_ptr<SymbolRemapper> Remapper;
707   if (!RemappingFile.empty())
708     Remapper = SymbolRemapper::create(RemappingFile);
709 
710   if (ProfileKind == instr)
711     mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename,
712                       OutputFormat, OutputSparse, NumThreads, FailureMode);
713   else
714     mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
715                        OutputFormat, ProfileSymbolListFile, CompressAllSections,
716                        UseMD5, GenPartialProfile, FailureMode);
717 
718   return 0;
719 }
720 
721 /// Computer the overlap b/w profile BaseFilename and profile TestFilename.
722 static void overlapInstrProfile(const std::string &BaseFilename,
723                                 const std::string &TestFilename,
724                                 const OverlapFuncFilters &FuncFilter,
725                                 raw_fd_ostream &OS, bool IsCS) {
726   std::mutex ErrorLock;
727   SmallSet<instrprof_error, 4> WriterErrorCodes;
728   WriterContext Context(false, ErrorLock, WriterErrorCodes);
729   WeightedFile WeightedInput{BaseFilename, 1};
730   OverlapStats Overlap;
731   Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
732   if (E)
733     exitWithError(std::move(E), "Error in getting profile count sums");
734   if (Overlap.Base.CountSum < 1.0f) {
735     OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
736     exit(0);
737   }
738   if (Overlap.Test.CountSum < 1.0f) {
739     OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
740     exit(0);
741   }
742   loadInput(WeightedInput, nullptr, &Context);
743   overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
744                IsCS);
745   Overlap.dump(OS);
746 }
747 
748 static int overlap_main(int argc, const char *argv[]) {
749   cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
750                                     cl::desc("<base profile file>"));
751   cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
752                                     cl::desc("<test profile file>"));
753   cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"),
754                               cl::desc("Output file"));
755   cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output));
756   cl::opt<bool> IsCS("cs", cl::init(false),
757                      cl::desc("For context sensitive counts"));
758   cl::opt<unsigned long long> ValueCutoff(
759       "value-cutoff", cl::init(-1),
760       cl::desc(
761           "Function level overlap information for every function in test "
762           "profile with max count value greater then the parameter value"));
763   cl::opt<std::string> FuncNameFilter(
764       "function",
765       cl::desc("Function level overlap information for matching functions"));
766   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n");
767 
768   std::error_code EC;
769   raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text);
770   if (EC)
771     exitWithErrorCode(EC, Output);
772 
773   overlapInstrProfile(BaseFilename, TestFilename,
774                       OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS,
775                       IsCS);
776 
777   return 0;
778 }
779 
780 typedef struct ValueSitesStats {
781   ValueSitesStats()
782       : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
783         TotalNumValues(0) {}
784   uint64_t TotalNumValueSites;
785   uint64_t TotalNumValueSitesWithValueProfile;
786   uint64_t TotalNumValues;
787   std::vector<unsigned> ValueSitesHistogram;
788 } ValueSitesStats;
789 
790 static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
791                                   ValueSitesStats &Stats, raw_fd_ostream &OS,
792                                   InstrProfSymtab *Symtab) {
793   uint32_t NS = Func.getNumValueSites(VK);
794   Stats.TotalNumValueSites += NS;
795   for (size_t I = 0; I < NS; ++I) {
796     uint32_t NV = Func.getNumValueDataForSite(VK, I);
797     std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
798     Stats.TotalNumValues += NV;
799     if (NV) {
800       Stats.TotalNumValueSitesWithValueProfile++;
801       if (NV > Stats.ValueSitesHistogram.size())
802         Stats.ValueSitesHistogram.resize(NV, 0);
803       Stats.ValueSitesHistogram[NV - 1]++;
804     }
805 
806     uint64_t SiteSum = 0;
807     for (uint32_t V = 0; V < NV; V++)
808       SiteSum += VD[V].Count;
809     if (SiteSum == 0)
810       SiteSum = 1;
811 
812     for (uint32_t V = 0; V < NV; V++) {
813       OS << "\t[ " << format("%2u", I) << ", ";
814       if (Symtab == nullptr)
815         OS << format("%4" PRIu64, VD[V].Value);
816       else
817         OS << Symtab->getFuncName(VD[V].Value);
818       OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
819          << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
820     }
821   }
822 }
823 
824 static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
825                                 ValueSitesStats &Stats) {
826   OS << "  Total number of sites: " << Stats.TotalNumValueSites << "\n";
827   OS << "  Total number of sites with values: "
828      << Stats.TotalNumValueSitesWithValueProfile << "\n";
829   OS << "  Total number of profiled values: " << Stats.TotalNumValues << "\n";
830 
831   OS << "  Value sites histogram:\n\tNumTargets, SiteCount\n";
832   for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
833     if (Stats.ValueSitesHistogram[I] > 0)
834       OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
835   }
836 }
837 
838 static int showInstrProfile(const std::string &Filename, bool ShowCounts,
839                             uint32_t TopN, bool ShowIndirectCallTargets,
840                             bool ShowMemOPSizes, bool ShowDetailedSummary,
841                             std::vector<uint32_t> DetailedSummaryCutoffs,
842                             bool ShowAllFunctions, bool ShowCS,
843                             uint64_t ValueCutoff, bool OnlyListBelow,
844                             const std::string &ShowFunction, bool TextFormat,
845                             raw_fd_ostream &OS) {
846   auto ReaderOrErr = InstrProfReader::create(Filename);
847   std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
848   if (ShowDetailedSummary && Cutoffs.empty()) {
849     Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
850   }
851   InstrProfSummaryBuilder Builder(std::move(Cutoffs));
852   if (Error E = ReaderOrErr.takeError())
853     exitWithError(std::move(E), Filename);
854 
855   auto Reader = std::move(ReaderOrErr.get());
856   bool IsIRInstr = Reader->isIRLevelProfile();
857   size_t ShownFunctions = 0;
858   size_t BelowCutoffFunctions = 0;
859   int NumVPKind = IPVK_Last - IPVK_First + 1;
860   std::vector<ValueSitesStats> VPStats(NumVPKind);
861 
862   auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
863                    const std::pair<std::string, uint64_t> &v2) {
864     return v1.second > v2.second;
865   };
866 
867   std::priority_queue<std::pair<std::string, uint64_t>,
868                       std::vector<std::pair<std::string, uint64_t>>,
869                       decltype(MinCmp)>
870       HottestFuncs(MinCmp);
871 
872   if (!TextFormat && OnlyListBelow) {
873     OS << "The list of functions with the maximum counter less than "
874        << ValueCutoff << ":\n";
875   }
876 
877   // Add marker so that IR-level instrumentation round-trips properly.
878   if (TextFormat && IsIRInstr)
879     OS << ":ir\n";
880 
881   for (const auto &Func : *Reader) {
882     if (Reader->isIRLevelProfile()) {
883       bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
884       if (FuncIsCS != ShowCS)
885         continue;
886     }
887     bool Show =
888         ShowAllFunctions || (!ShowFunction.empty() &&
889                              Func.Name.find(ShowFunction) != Func.Name.npos);
890 
891     bool doTextFormatDump = (Show && TextFormat);
892 
893     if (doTextFormatDump) {
894       InstrProfSymtab &Symtab = Reader->getSymtab();
895       InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
896                                          OS);
897       continue;
898     }
899 
900     assert(Func.Counts.size() > 0 && "function missing entry counter");
901     Builder.addRecord(Func);
902 
903     uint64_t FuncMax = 0;
904     uint64_t FuncSum = 0;
905     for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
906       FuncMax = std::max(FuncMax, Func.Counts[I]);
907       FuncSum += Func.Counts[I];
908     }
909 
910     if (FuncMax < ValueCutoff) {
911       ++BelowCutoffFunctions;
912       if (OnlyListBelow) {
913         OS << "  " << Func.Name << ": (Max = " << FuncMax
914            << " Sum = " << FuncSum << ")\n";
915       }
916       continue;
917     } else if (OnlyListBelow)
918       continue;
919 
920     if (TopN) {
921       if (HottestFuncs.size() == TopN) {
922         if (HottestFuncs.top().second < FuncMax) {
923           HottestFuncs.pop();
924           HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
925         }
926       } else
927         HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
928     }
929 
930     if (Show) {
931       if (!ShownFunctions)
932         OS << "Counters:\n";
933 
934       ++ShownFunctions;
935 
936       OS << "  " << Func.Name << ":\n"
937          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
938          << "    Counters: " << Func.Counts.size() << "\n";
939       if (!IsIRInstr)
940         OS << "    Function count: " << Func.Counts[0] << "\n";
941 
942       if (ShowIndirectCallTargets)
943         OS << "    Indirect Call Site Count: "
944            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
945 
946       uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
947       if (ShowMemOPSizes && NumMemOPCalls > 0)
948         OS << "    Number of Memory Intrinsics Calls: " << NumMemOPCalls
949            << "\n";
950 
951       if (ShowCounts) {
952         OS << "    Block counts: [";
953         size_t Start = (IsIRInstr ? 0 : 1);
954         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
955           OS << (I == Start ? "" : ", ") << Func.Counts[I];
956         }
957         OS << "]\n";
958       }
959 
960       if (ShowIndirectCallTargets) {
961         OS << "    Indirect Target Results:\n";
962         traverseAllValueSites(Func, IPVK_IndirectCallTarget,
963                               VPStats[IPVK_IndirectCallTarget], OS,
964                               &(Reader->getSymtab()));
965       }
966 
967       if (ShowMemOPSizes && NumMemOPCalls > 0) {
968         OS << "    Memory Intrinsic Size Results:\n";
969         traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
970                               nullptr);
971       }
972     }
973   }
974   if (Reader->hasError())
975     exitWithError(Reader->getError(), Filename);
976 
977   if (TextFormat)
978     return 0;
979   std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
980   OS << "Instrumentation level: "
981      << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n";
982   if (ShowAllFunctions || !ShowFunction.empty())
983     OS << "Functions shown: " << ShownFunctions << "\n";
984   OS << "Total functions: " << PS->getNumFunctions() << "\n";
985   if (ValueCutoff > 0) {
986     OS << "Number of functions with maximum count (< " << ValueCutoff
987        << "): " << BelowCutoffFunctions << "\n";
988     OS << "Number of functions with maximum count (>= " << ValueCutoff
989        << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
990   }
991   OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
992   OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
993 
994   if (TopN) {
995     std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
996     while (!HottestFuncs.empty()) {
997       SortedHottestFuncs.emplace_back(HottestFuncs.top());
998       HottestFuncs.pop();
999     }
1000     OS << "Top " << TopN
1001        << " functions with the largest internal block counts: \n";
1002     for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
1003       OS << "  " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
1004   }
1005 
1006   if (ShownFunctions && ShowIndirectCallTargets) {
1007     OS << "Statistics for indirect call sites profile:\n";
1008     showValueSitesStats(OS, IPVK_IndirectCallTarget,
1009                         VPStats[IPVK_IndirectCallTarget]);
1010   }
1011 
1012   if (ShownFunctions && ShowMemOPSizes) {
1013     OS << "Statistics for memory intrinsic calls sizes profile:\n";
1014     showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
1015   }
1016 
1017   if (ShowDetailedSummary) {
1018     OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
1019     OS << "Total count: " << PS->getTotalCount() << "\n";
1020     PS->printDetailedSummary(OS);
1021   }
1022   return 0;
1023 }
1024 
1025 static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
1026                             raw_fd_ostream &OS) {
1027   if (!Reader->dumpSectionInfo(OS)) {
1028     WithColor::warning() << "-show-sec-info-only is only supported for "
1029                          << "sample profile in extbinary format and is "
1030                          << "ignored for other formats.\n";
1031     return;
1032   }
1033 }
1034 
1035 namespace {
1036 struct HotFuncInfo {
1037   StringRef FuncName;
1038   uint64_t TotalCount;
1039   double TotalCountPercent;
1040   uint64_t MaxCount;
1041   uint64_t EntryCount;
1042 
1043   HotFuncInfo()
1044       : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0),
1045         EntryCount(0) {}
1046 
1047   HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
1048       : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS),
1049         EntryCount(ES) {}
1050 };
1051 } // namespace
1052 
1053 // Print out detailed information about hot functions in PrintValues vector.
1054 // Users specify titles and offset of every columns through ColumnTitle and
1055 // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
1056 // and at least 4. Besides, users can optionally give a HotFuncMetric string to
1057 // print out or let it be an empty string.
1058 static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
1059                                 const std::vector<int> &ColumnOffset,
1060                                 const std::vector<HotFuncInfo> &PrintValues,
1061                                 uint64_t HotFuncCount, uint64_t TotalFuncCount,
1062                                 uint64_t HotProfCount, uint64_t TotalProfCount,
1063                                 const std::string &HotFuncMetric,
1064                                 raw_fd_ostream &OS) {
1065   assert(ColumnOffset.size() == ColumnTitle.size());
1066   assert(ColumnTitle.size() >= 4);
1067   assert(TotalFuncCount > 0);
1068   double TotalProfPercent = 0;
1069   if (TotalProfCount > 0)
1070     TotalProfPercent = ((double)HotProfCount) / TotalProfCount * 100;
1071 
1072   formatted_raw_ostream FOS(OS);
1073   FOS << HotFuncCount << " out of " << TotalFuncCount
1074       << " functions with profile ("
1075       << format("%.2f%%", (((double)HotFuncCount) / TotalFuncCount * 100))
1076       << ") are considered hot functions";
1077   if (!HotFuncMetric.empty())
1078     FOS << " (" << HotFuncMetric << ")";
1079   FOS << ".\n";
1080   FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
1081       << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
1082 
1083   for (size_t I = 0; I < ColumnTitle.size(); ++I) {
1084     FOS.PadToColumn(ColumnOffset[I]);
1085     FOS << ColumnTitle[I];
1086   }
1087   FOS << "\n";
1088 
1089   for (const HotFuncInfo &R : PrintValues) {
1090     FOS.PadToColumn(ColumnOffset[0]);
1091     FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
1092     FOS.PadToColumn(ColumnOffset[1]);
1093     FOS << R.MaxCount;
1094     FOS.PadToColumn(ColumnOffset[2]);
1095     FOS << R.EntryCount;
1096     FOS.PadToColumn(ColumnOffset[3]);
1097     FOS << R.FuncName << "\n";
1098   }
1099   return;
1100 }
1101 
1102 static int
1103 showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles,
1104                     ProfileSummary &PS, raw_fd_ostream &OS) {
1105   using namespace sampleprof;
1106 
1107   const uint32_t HotFuncCutoff = 990000;
1108   auto &SummaryVector = PS.getDetailedSummary();
1109   uint64_t MinCountThreshold = 0;
1110   for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
1111     if (SummaryEntry.Cutoff == HotFuncCutoff) {
1112       MinCountThreshold = SummaryEntry.MinCount;
1113       break;
1114     }
1115   }
1116   assert(MinCountThreshold != 0);
1117 
1118   // Traverse all functions in the profile and keep only hot functions.
1119   // The following loop also calculates the sum of total samples of all
1120   // functions.
1121   std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
1122                 std::greater<uint64_t>>
1123       HotFunc;
1124   uint64_t ProfileTotalSample = 0;
1125   uint64_t HotFuncSample = 0;
1126   uint64_t HotFuncCount = 0;
1127   uint64_t MaxCount = 0;
1128   for (const auto &I : Profiles) {
1129     const FunctionSamples &FuncProf = I.second;
1130     ProfileTotalSample += FuncProf.getTotalSamples();
1131     MaxCount = FuncProf.getMaxCountInside();
1132 
1133     // MinCountThreshold is a block/line threshold computed for a given cutoff.
1134     // We intentionally compare the maximum sample count in a function with this
1135     // threshold to get an approximate threshold for hot functions.
1136     if (MaxCount >= MinCountThreshold) {
1137       HotFunc.emplace(FuncProf.getTotalSamples(),
1138                       std::make_pair(&(I.second), MaxCount));
1139       HotFuncSample += FuncProf.getTotalSamples();
1140       ++HotFuncCount;
1141     }
1142   }
1143 
1144   std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
1145                                        "Entry sample", "Function name"};
1146   std::vector<int> ColumnOffset{0, 24, 42, 58};
1147   std::string Metric =
1148       std::string("max sample >= ") + std::to_string(MinCountThreshold);
1149   std::vector<HotFuncInfo> PrintValues;
1150   for (const auto &FuncPair : HotFunc) {
1151     const FunctionSamples &Func = *FuncPair.second.first;
1152     double TotalSamplePercent =
1153         (ProfileTotalSample > 0)
1154             ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
1155             : 0;
1156     PrintValues.emplace_back(HotFuncInfo(
1157         Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent,
1158         FuncPair.second.second, Func.getEntrySamples()));
1159   }
1160   dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
1161                       Profiles.size(), HotFuncSample, ProfileTotalSample,
1162                       Metric, OS);
1163 
1164   return 0;
1165 }
1166 
1167 static int showSampleProfile(const std::string &Filename, bool ShowCounts,
1168                              bool ShowAllFunctions, bool ShowDetailedSummary,
1169                              const std::string &ShowFunction,
1170                              bool ShowProfileSymbolList,
1171                              bool ShowSectionInfoOnly, bool ShowHotFuncList,
1172                              raw_fd_ostream &OS) {
1173   using namespace sampleprof;
1174   LLVMContext Context;
1175   auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
1176   if (std::error_code EC = ReaderOrErr.getError())
1177     exitWithErrorCode(EC, Filename);
1178 
1179   auto Reader = std::move(ReaderOrErr.get());
1180 
1181   if (ShowSectionInfoOnly) {
1182     showSectionInfo(Reader.get(), OS);
1183     return 0;
1184   }
1185 
1186   if (std::error_code EC = Reader->read())
1187     exitWithErrorCode(EC, Filename);
1188 
1189   if (ShowAllFunctions || ShowFunction.empty())
1190     Reader->dump(OS);
1191   else
1192     Reader->dumpFunctionProfile(ShowFunction, OS);
1193 
1194   if (ShowProfileSymbolList) {
1195     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
1196         Reader->getProfileSymbolList();
1197     ReaderList->dump(OS);
1198   }
1199 
1200   if (ShowDetailedSummary) {
1201     auto &PS = Reader->getSummary();
1202     PS.printSummary(OS);
1203     PS.printDetailedSummary(OS);
1204   }
1205 
1206   if (ShowHotFuncList)
1207     showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS);
1208 
1209   return 0;
1210 }
1211 
1212 static int show_main(int argc, const char *argv[]) {
1213   cl::opt<std::string> Filename(cl::Positional, cl::Required,
1214                                 cl::desc("<profdata-file>"));
1215 
1216   cl::opt<bool> ShowCounts("counts", cl::init(false),
1217                            cl::desc("Show counter values for shown functions"));
1218   cl::opt<bool> TextFormat(
1219       "text", cl::init(false),
1220       cl::desc("Show instr profile data in text dump format"));
1221   cl::opt<bool> ShowIndirectCallTargets(
1222       "ic-targets", cl::init(false),
1223       cl::desc("Show indirect call site target values for shown functions"));
1224   cl::opt<bool> ShowMemOPSizes(
1225       "memop-sizes", cl::init(false),
1226       cl::desc("Show the profiled sizes of the memory intrinsic calls "
1227                "for shown functions"));
1228   cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
1229                                     cl::desc("Show detailed profile summary"));
1230   cl::list<uint32_t> DetailedSummaryCutoffs(
1231       cl::CommaSeparated, "detailed-summary-cutoffs",
1232       cl::desc(
1233           "Cutoff percentages (times 10000) for generating detailed summary"),
1234       cl::value_desc("800000,901000,999999"));
1235   cl::opt<bool> ShowHotFuncList(
1236       "hot-func-list", cl::init(false),
1237       cl::desc("Show profile summary of a list of hot functions"));
1238   cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
1239                                  cl::desc("Details for every function"));
1240   cl::opt<bool> ShowCS("showcs", cl::init(false),
1241                        cl::desc("Show context sensitive counts"));
1242   cl::opt<std::string> ShowFunction("function",
1243                                     cl::desc("Details for matching functions"));
1244 
1245   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
1246                                       cl::init("-"), cl::desc("Output file"));
1247   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
1248                             cl::aliasopt(OutputFilename));
1249   cl::opt<ProfileKinds> ProfileKind(
1250       cl::desc("Profile kind:"), cl::init(instr),
1251       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
1252                  clEnumVal(sample, "Sample profile")));
1253   cl::opt<uint32_t> TopNFunctions(
1254       "topn", cl::init(0),
1255       cl::desc("Show the list of functions with the largest internal counts"));
1256   cl::opt<uint32_t> ValueCutoff(
1257       "value-cutoff", cl::init(0),
1258       cl::desc("Set the count value cutoff. Functions with the maximum count "
1259                "less than this value will not be printed out. (Default is 0)"));
1260   cl::opt<bool> OnlyListBelow(
1261       "list-below-cutoff", cl::init(false),
1262       cl::desc("Only output names of functions whose max count values are "
1263                "below the cutoff value"));
1264   cl::opt<bool> ShowProfileSymbolList(
1265       "show-prof-sym-list", cl::init(false),
1266       cl::desc("Show profile symbol list if it exists in the profile. "));
1267   cl::opt<bool> ShowSectionInfoOnly(
1268       "show-sec-info-only", cl::init(false),
1269       cl::desc("Show the information of each section in the sample profile. "
1270                "The flag is only usable when the sample profile is in "
1271                "extbinary format"));
1272 
1273   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
1274 
1275   if (OutputFilename.empty())
1276     OutputFilename = "-";
1277 
1278   if (!Filename.compare(OutputFilename)) {
1279     errs() << sys::path::filename(argv[0])
1280            << ": Input file name cannot be the same as the output file name!\n";
1281     return 1;
1282   }
1283 
1284   std::error_code EC;
1285   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text);
1286   if (EC)
1287     exitWithErrorCode(EC, OutputFilename);
1288 
1289   if (ShowAllFunctions && !ShowFunction.empty())
1290     WithColor::warning() << "-function argument ignored: showing all functions\n";
1291 
1292   if (ProfileKind == instr)
1293     return showInstrProfile(Filename, ShowCounts, TopNFunctions,
1294                             ShowIndirectCallTargets, ShowMemOPSizes,
1295                             ShowDetailedSummary, DetailedSummaryCutoffs,
1296                             ShowAllFunctions, ShowCS, ValueCutoff,
1297                             OnlyListBelow, ShowFunction, TextFormat, OS);
1298   else
1299     return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
1300                              ShowDetailedSummary, ShowFunction,
1301                              ShowProfileSymbolList, ShowSectionInfoOnly,
1302                              ShowHotFuncList, OS);
1303 }
1304 
1305 int main(int argc, const char *argv[]) {
1306   InitLLVM X(argc, argv);
1307 
1308   StringRef ProgName(sys::path::filename(argv[0]));
1309   if (argc > 1) {
1310     int (*func)(int, const char *[]) = nullptr;
1311 
1312     if (strcmp(argv[1], "merge") == 0)
1313       func = merge_main;
1314     else if (strcmp(argv[1], "show") == 0)
1315       func = show_main;
1316     else if (strcmp(argv[1], "overlap") == 0)
1317       func = overlap_main;
1318 
1319     if (func) {
1320       std::string Invocation(ProgName.str() + " " + argv[1]);
1321       argv[1] = Invocation.c_str();
1322       return func(argc - 1, argv + 1);
1323     }
1324 
1325     if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
1326         strcmp(argv[1], "--help") == 0) {
1327 
1328       errs() << "OVERVIEW: LLVM profile data tools\n\n"
1329              << "USAGE: " << ProgName << " <command> [args...]\n"
1330              << "USAGE: " << ProgName << " <command> -help\n\n"
1331              << "See each individual command --help for more details.\n"
1332              << "Available commands: merge, show, overlap\n";
1333       return 0;
1334     }
1335   }
1336 
1337   if (argc < 2)
1338     errs() << ProgName << ": No command specified!\n";
1339   else
1340     errs() << ProgName << ": Unknown command!\n";
1341 
1342   errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n";
1343   return 1;
1344 }
1345