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