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