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/InitLLVM.h"
27 #include "llvm/Support/MemoryBuffer.h"
28 #include "llvm/Support/Path.h"
29 #include "llvm/Support/WithColor.h"
30 #include "llvm/Support/ThreadPool.h"
31 #include "llvm/Support/raw_ostream.h"
32 #include <algorithm>
33 
34 using namespace llvm;
35 
36 enum ProfileFormat {
37   PF_None = 0,
38   PF_Text,
39   PF_Compact_Binary,
40   PF_GCC,
41   PF_Binary
42 };
43 
44 static void warn(Twine Message, std::string Whence = "",
45                  std::string Hint = "") {
46   WithColor::warning();
47   if (!Whence.empty())
48     errs() << Whence << ": ";
49   errs() << Message << "\n";
50   if (!Hint.empty())
51     WithColor::note() << Hint << "\n";
52 }
53 
54 static void exitWithError(Twine Message, std::string Whence = "",
55                           std::string Hint = "") {
56   WithColor::error();
57   if (!Whence.empty())
58     errs() << Whence << ": ";
59   errs() << Message << "\n";
60   if (!Hint.empty())
61     WithColor::note() << Hint << "\n";
62   ::exit(1);
63 }
64 
65 static void exitWithError(Error E, StringRef Whence = "") {
66   if (E.isA<InstrProfError>()) {
67     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
68       instrprof_error instrError = IPE.get();
69       StringRef Hint = "";
70       if (instrError == instrprof_error::unrecognized_format) {
71         // Hint for common error of forgetting -sample for sample profiles.
72         Hint = "Perhaps you forgot to use the -sample option?";
73       }
74       exitWithError(IPE.message(), Whence, Hint);
75     });
76   }
77 
78   exitWithError(toString(std::move(E)), Whence);
79 }
80 
81 static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
82   exitWithError(EC.message(), Whence);
83 }
84 
85 namespace {
86 enum ProfileKinds { instr, sample };
87 }
88 
89 static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
90                                    StringRef WhenceFunction = "",
91                                    bool ShowHint = true) {
92   if (!WhenceFile.empty())
93     errs() << WhenceFile << ": ";
94   if (!WhenceFunction.empty())
95     errs() << WhenceFunction << ": ";
96 
97   auto IPE = instrprof_error::success;
98   E = handleErrors(std::move(E),
99                    [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
100                      IPE = E->get();
101                      return Error(std::move(E));
102                    });
103   errs() << toString(std::move(E)) << "\n";
104 
105   if (ShowHint) {
106     StringRef Hint = "";
107     if (IPE != instrprof_error::success) {
108       switch (IPE) {
109       case instrprof_error::hash_mismatch:
110       case instrprof_error::count_mismatch:
111       case instrprof_error::value_site_count_mismatch:
112         Hint = "Make sure that all profile data to be merged is generated "
113                "from the same binary.";
114         break;
115       default:
116         break;
117       }
118     }
119 
120     if (!Hint.empty())
121       errs() << Hint << "\n";
122   }
123 }
124 
125 namespace {
126 /// A remapper from original symbol names to new symbol names based on a file
127 /// containing a list of mappings from old name to new name.
128 class SymbolRemapper {
129   std::unique_ptr<MemoryBuffer> File;
130   DenseMap<StringRef, StringRef> RemappingTable;
131 
132 public:
133   /// Build a SymbolRemapper from a file containing a list of old/new symbols.
134   static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
135     auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
136     if (!BufOrError)
137       exitWithErrorCode(BufOrError.getError(), InputFile);
138 
139     auto Remapper = llvm::make_unique<SymbolRemapper>();
140     Remapper->File = std::move(BufOrError.get());
141 
142     for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
143          !LineIt.is_at_eof(); ++LineIt) {
144       std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
145       if (Parts.first.empty() || Parts.second.empty() ||
146           Parts.second.count(' ')) {
147         exitWithError("unexpected line in remapping file",
148                       (InputFile + ":" + Twine(LineIt.line_number())).str(),
149                       "expected 'old_symbol new_symbol'");
150       }
151       Remapper->RemappingTable.insert(Parts);
152     }
153     return Remapper;
154   }
155 
156   /// Attempt to map the given old symbol into a new symbol.
157   ///
158   /// \return The new symbol, or \p Name if no such symbol was found.
159   StringRef operator()(StringRef Name) {
160     StringRef New = RemappingTable.lookup(Name);
161     return New.empty() ? Name : New;
162   }
163 };
164 }
165 
166 struct WeightedFile {
167   std::string Filename;
168   uint64_t Weight;
169 };
170 typedef SmallVector<WeightedFile, 5> WeightedFileVector;
171 
172 /// Keep track of merged data and reported errors.
173 struct WriterContext {
174   std::mutex Lock;
175   InstrProfWriter Writer;
176   Error Err;
177   std::string ErrWhence;
178   std::mutex &ErrLock;
179   SmallSet<instrprof_error, 4> &WriterErrorCodes;
180 
181   WriterContext(bool IsSparse, std::mutex &ErrLock,
182                 SmallSet<instrprof_error, 4> &WriterErrorCodes)
183       : Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""),
184         ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {}
185 };
186 
187 /// Determine whether an error is fatal for profile merging.
188 static bool isFatalError(instrprof_error IPE) {
189   switch (IPE) {
190   default:
191     return true;
192   case instrprof_error::success:
193   case instrprof_error::eof:
194   case instrprof_error::unknown_function:
195   case instrprof_error::hash_mismatch:
196   case instrprof_error::count_mismatch:
197   case instrprof_error::counter_overflow:
198   case instrprof_error::value_site_count_mismatch:
199     return false;
200   }
201 }
202 
203 /// Load an input into a writer context.
204 static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
205                       WriterContext *WC) {
206   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
207 
208   // If there's a pending hard error, don't do more work.
209   if (WC->Err)
210     return;
211 
212   // Copy the filename, because llvm::ThreadPool copied the input "const
213   // WeightedFile &" by value, making a reference to the filename within it
214   // invalid outside of this packaged task.
215   WC->ErrWhence = Input.Filename;
216 
217   auto ReaderOrErr = InstrProfReader::create(Input.Filename);
218   if (Error E = ReaderOrErr.takeError()) {
219     // Skip the empty profiles by returning sliently.
220     instrprof_error IPE = InstrProfError::take(std::move(E));
221     if (IPE != instrprof_error::empty_raw_profile)
222       WC->Err = make_error<InstrProfError>(IPE);
223     return;
224   }
225 
226   auto Reader = std::move(ReaderOrErr.get());
227   bool IsIRProfile = Reader->isIRLevelProfile();
228   if (WC->Writer.setIsIRLevelProfile(IsIRProfile)) {
229     WC->Err = make_error<StringError>(
230         "Merge IR generated profile with Clang generated profile.",
231         std::error_code());
232     return;
233   }
234 
235   for (auto &I : *Reader) {
236     if (Remapper)
237       I.Name = (*Remapper)(I.Name);
238     const StringRef FuncName = I.Name;
239     bool Reported = false;
240     WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
241       if (Reported) {
242         consumeError(std::move(E));
243         return;
244       }
245       Reported = true;
246       // Only show hint the first time an error occurs.
247       instrprof_error IPE = InstrProfError::take(std::move(E));
248       std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
249       bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
250       handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
251                              FuncName, firstTime);
252     });
253   }
254   if (Reader->hasError()) {
255     if (Error E = Reader->getError()) {
256       instrprof_error IPE = InstrProfError::take(std::move(E));
257       if (isFatalError(IPE))
258         WC->Err = make_error<InstrProfError>(IPE);
259     }
260   }
261 }
262 
263 /// Merge the \p Src writer context into \p Dst.
264 static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
265   // If we've already seen a hard error, continuing with the merge would
266   // clobber it.
267   if (Dst->Err || Src->Err)
268     return;
269 
270   bool Reported = false;
271   Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
272     if (Reported) {
273       consumeError(std::move(E));
274       return;
275     }
276     Reported = true;
277     Dst->Err = std::move(E);
278   });
279 }
280 
281 static void mergeInstrProfile(const WeightedFileVector &Inputs,
282                               SymbolRemapper *Remapper,
283                               StringRef OutputFilename,
284                               ProfileFormat OutputFormat, bool OutputSparse,
285                               unsigned NumThreads) {
286   if (OutputFilename.compare("-") == 0)
287     exitWithError("Cannot write indexed profdata format to stdout.");
288 
289   if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
290       OutputFormat != PF_Text)
291     exitWithError("Unknown format is specified.");
292 
293   std::error_code EC;
294   raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None);
295   if (EC)
296     exitWithErrorCode(EC, OutputFilename);
297 
298   std::mutex ErrorLock;
299   SmallSet<instrprof_error, 4> WriterErrorCodes;
300 
301   // If NumThreads is not specified, auto-detect a good default.
302   if (NumThreads == 0)
303     NumThreads =
304         std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2));
305 
306   // Initialize the writer contexts.
307   SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
308   for (unsigned I = 0; I < NumThreads; ++I)
309     Contexts.emplace_back(llvm::make_unique<WriterContext>(
310         OutputSparse, ErrorLock, WriterErrorCodes));
311 
312   if (NumThreads == 1) {
313     for (const auto &Input : Inputs)
314       loadInput(Input, Remapper, Contexts[0].get());
315   } else {
316     ThreadPool Pool(NumThreads);
317 
318     // Load the inputs in parallel (N/NumThreads serial steps).
319     unsigned Ctx = 0;
320     for (const auto &Input : Inputs) {
321       Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get());
322       Ctx = (Ctx + 1) % NumThreads;
323     }
324     Pool.wait();
325 
326     // Merge the writer contexts together (~ lg(NumThreads) serial steps).
327     unsigned Mid = Contexts.size() / 2;
328     unsigned End = Contexts.size();
329     assert(Mid > 0 && "Expected more than one context");
330     do {
331       for (unsigned I = 0; I < Mid; ++I)
332         Pool.async(mergeWriterContexts, Contexts[I].get(),
333                    Contexts[I + Mid].get());
334       Pool.wait();
335       if (End & 1) {
336         Pool.async(mergeWriterContexts, Contexts[0].get(),
337                    Contexts[End - 1].get());
338         Pool.wait();
339       }
340       End = Mid;
341       Mid /= 2;
342     } while (Mid > 0);
343   }
344 
345   // Handle deferred hard errors encountered during merging.
346   for (std::unique_ptr<WriterContext> &WC : Contexts) {
347     if (!WC->Err)
348       continue;
349     if (!WC->Err.isA<InstrProfError>())
350       exitWithError(std::move(WC->Err), WC->ErrWhence);
351 
352     instrprof_error IPE = InstrProfError::take(std::move(WC->Err));
353     if (isFatalError(IPE))
354       exitWithError(make_error<InstrProfError>(IPE), WC->ErrWhence);
355     else
356       warn(toString(make_error<InstrProfError>(IPE)),
357            WC->ErrWhence);
358   }
359 
360   InstrProfWriter &Writer = Contexts[0]->Writer;
361   if (OutputFormat == PF_Text) {
362     if (Error E = Writer.writeText(Output))
363       exitWithError(std::move(E));
364   } else {
365     Writer.write(Output);
366   }
367 }
368 
369 /// Make a copy of the given function samples with all symbol names remapped
370 /// by the provided symbol remapper.
371 static sampleprof::FunctionSamples
372 remapSamples(const sampleprof::FunctionSamples &Samples,
373              SymbolRemapper &Remapper, sampleprof_error &Error) {
374   sampleprof::FunctionSamples Result;
375   Result.setName(Remapper(Samples.getName()));
376   Result.addTotalSamples(Samples.getTotalSamples());
377   Result.addHeadSamples(Samples.getHeadSamples());
378   for (const auto &BodySample : Samples.getBodySamples()) {
379     Result.addBodySamples(BodySample.first.LineOffset,
380                           BodySample.first.Discriminator,
381                           BodySample.second.getSamples());
382     for (const auto &Target : BodySample.second.getCallTargets()) {
383       Result.addCalledTargetSamples(BodySample.first.LineOffset,
384                                     BodySample.first.Discriminator,
385                                     Remapper(Target.first()), Target.second);
386     }
387   }
388   for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
389     sampleprof::FunctionSamplesMap &Target =
390         Result.functionSamplesAt(CallsiteSamples.first);
391     for (const auto &Callsite : CallsiteSamples.second) {
392       sampleprof::FunctionSamples Remapped =
393           remapSamples(Callsite.second, Remapper, Error);
394       MergeResult(Error, Target[Remapped.getName()].merge(Remapped));
395     }
396   }
397   return Result;
398 }
399 
400 static sampleprof::SampleProfileFormat FormatMap[] = {
401     sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary,
402     sampleprof::SPF_GCC, sampleprof::SPF_Binary};
403 
404 static void mergeSampleProfile(const WeightedFileVector &Inputs,
405                                SymbolRemapper *Remapper,
406                                StringRef OutputFilename,
407                                ProfileFormat OutputFormat) {
408   using namespace sampleprof;
409   auto WriterOrErr =
410       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
411   if (std::error_code EC = WriterOrErr.getError())
412     exitWithErrorCode(EC, OutputFilename);
413 
414   auto Writer = std::move(WriterOrErr.get());
415   StringMap<FunctionSamples> ProfileMap;
416   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
417   LLVMContext Context;
418   for (const auto &Input : Inputs) {
419     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
420     if (std::error_code EC = ReaderOrErr.getError())
421       exitWithErrorCode(EC, Input.Filename);
422 
423     // We need to keep the readers around until after all the files are
424     // read so that we do not lose the function names stored in each
425     // reader's memory. The function names are needed to write out the
426     // merged profile map.
427     Readers.push_back(std::move(ReaderOrErr.get()));
428     const auto Reader = Readers.back().get();
429     if (std::error_code EC = Reader->read())
430       exitWithErrorCode(EC, Input.Filename);
431 
432     StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
433     for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
434                                               E = Profiles.end();
435          I != E; ++I) {
436       sampleprof_error Result = sampleprof_error::success;
437       FunctionSamples Remapped =
438           Remapper ? remapSamples(I->second, *Remapper, Result)
439                    : FunctionSamples();
440       FunctionSamples &Samples = Remapper ? Remapped : I->second;
441       StringRef FName = Samples.getName();
442       MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight));
443       if (Result != sampleprof_error::success) {
444         std::error_code EC = make_error_code(Result);
445         handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
446       }
447     }
448   }
449   Writer->write(ProfileMap);
450 }
451 
452 static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
453   StringRef WeightStr, FileName;
454   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
455 
456   uint64_t Weight;
457   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
458     exitWithError("Input weight must be a positive integer.");
459 
460   return {FileName, Weight};
461 }
462 
463 static std::unique_ptr<MemoryBuffer>
464 getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) {
465   if (InputFilenamesFile == "")
466     return {};
467 
468   auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile);
469   if (!BufOrError)
470     exitWithErrorCode(BufOrError.getError(), InputFilenamesFile);
471 
472   return std::move(*BufOrError);
473 }
474 
475 static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
476   StringRef Filename = WF.Filename;
477   uint64_t Weight = WF.Weight;
478 
479   // If it's STDIN just pass it on.
480   if (Filename == "-") {
481     WNI.push_back({Filename, Weight});
482     return;
483   }
484 
485   llvm::sys::fs::file_status Status;
486   llvm::sys::fs::status(Filename, Status);
487   if (!llvm::sys::fs::exists(Status))
488     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
489                       Filename);
490   // If it's a source file, collect it.
491   if (llvm::sys::fs::is_regular_file(Status)) {
492     WNI.push_back({Filename, Weight});
493     return;
494   }
495 
496   if (llvm::sys::fs::is_directory(Status)) {
497     std::error_code EC;
498     for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
499          F != E && !EC; F.increment(EC)) {
500       if (llvm::sys::fs::is_regular_file(F->path())) {
501         addWeightedInput(WNI, {F->path(), Weight});
502       }
503     }
504     if (EC)
505       exitWithErrorCode(EC, Filename);
506   }
507 }
508 
509 static void parseInputFilenamesFile(MemoryBuffer *Buffer,
510                                     WeightedFileVector &WFV) {
511   if (!Buffer)
512     return;
513 
514   SmallVector<StringRef, 8> Entries;
515   StringRef Data = Buffer->getBuffer();
516   Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
517   for (const StringRef &FileWeightEntry : Entries) {
518     StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
519     // Skip comments.
520     if (SanitizedEntry.startswith("#"))
521       continue;
522     // If there's no comma, it's an unweighted profile.
523     else if (SanitizedEntry.find(',') == StringRef::npos)
524       addWeightedInput(WFV, {SanitizedEntry, 1});
525     else
526       addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
527   }
528 }
529 
530 static int merge_main(int argc, const char *argv[]) {
531   cl::list<std::string> InputFilenames(cl::Positional,
532                                        cl::desc("<filename...>"));
533   cl::list<std::string> WeightedInputFilenames("weighted-input",
534                                                cl::desc("<weight>,<filename>"));
535   cl::opt<std::string> InputFilenamesFile(
536       "input-files", cl::init(""),
537       cl::desc("Path to file containing newline-separated "
538                "[<weight>,]<filename> entries"));
539   cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
540                                 cl::aliasopt(InputFilenamesFile));
541   cl::opt<bool> DumpInputFileList(
542       "dump-input-file-list", cl::init(false), cl::Hidden,
543       cl::desc("Dump the list of input files and their weights, then exit"));
544   cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
545                                      cl::desc("Symbol remapping file"));
546   cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
547                            cl::aliasopt(RemappingFile));
548   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
549                                       cl::init("-"), cl::Required,
550                                       cl::desc("Output file"));
551   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
552                             cl::aliasopt(OutputFilename));
553   cl::opt<ProfileKinds> ProfileKind(
554       cl::desc("Profile kind:"), cl::init(instr),
555       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
556                  clEnumVal(sample, "Sample profile")));
557   cl::opt<ProfileFormat> OutputFormat(
558       cl::desc("Format of output profile"), cl::init(PF_Binary),
559       cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
560                  clEnumValN(PF_Compact_Binary, "compbinary",
561                             "Compact binary encoding"),
562                  clEnumValN(PF_Text, "text", "Text encoding"),
563                  clEnumValN(PF_GCC, "gcc",
564                             "GCC encoding (only meaningful for -sample)")));
565   cl::opt<bool> OutputSparse("sparse", cl::init(false),
566       cl::desc("Generate a sparse profile (only meaningful for -instr)"));
567   cl::opt<unsigned> NumThreads(
568       "num-threads", cl::init(0),
569       cl::desc("Number of merge threads to use (default: autodetect)"));
570   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
571                         cl::aliasopt(NumThreads));
572 
573   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
574 
575   WeightedFileVector WeightedInputs;
576   for (StringRef Filename : InputFilenames)
577     addWeightedInput(WeightedInputs, {Filename, 1});
578   for (StringRef WeightedFilename : WeightedInputFilenames)
579     addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
580 
581   // Make sure that the file buffer stays alive for the duration of the
582   // weighted input vector's lifetime.
583   auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile);
584   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
585 
586   if (WeightedInputs.empty())
587     exitWithError("No input files specified. See " +
588                   sys::path::filename(argv[0]) + " -help");
589 
590   if (DumpInputFileList) {
591     for (auto &WF : WeightedInputs)
592       outs() << WF.Weight << "," << WF.Filename << "\n";
593     return 0;
594   }
595 
596   std::unique_ptr<SymbolRemapper> Remapper;
597   if (!RemappingFile.empty())
598     Remapper = SymbolRemapper::create(RemappingFile);
599 
600   if (ProfileKind == instr)
601     mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename,
602                       OutputFormat, OutputSparse, NumThreads);
603   else
604     mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
605                        OutputFormat);
606 
607   return 0;
608 }
609 
610 typedef struct ValueSitesStats {
611   ValueSitesStats()
612       : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
613         TotalNumValues(0) {}
614   uint64_t TotalNumValueSites;
615   uint64_t TotalNumValueSitesWithValueProfile;
616   uint64_t TotalNumValues;
617   std::vector<unsigned> ValueSitesHistogram;
618 } ValueSitesStats;
619 
620 static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
621                                   ValueSitesStats &Stats, raw_fd_ostream &OS,
622                                   InstrProfSymtab *Symtab) {
623   uint32_t NS = Func.getNumValueSites(VK);
624   Stats.TotalNumValueSites += NS;
625   for (size_t I = 0; I < NS; ++I) {
626     uint32_t NV = Func.getNumValueDataForSite(VK, I);
627     std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
628     Stats.TotalNumValues += NV;
629     if (NV) {
630       Stats.TotalNumValueSitesWithValueProfile++;
631       if (NV > Stats.ValueSitesHistogram.size())
632         Stats.ValueSitesHistogram.resize(NV, 0);
633       Stats.ValueSitesHistogram[NV - 1]++;
634     }
635 
636     uint64_t SiteSum = 0;
637     for (uint32_t V = 0; V < NV; V++)
638       SiteSum += VD[V].Count;
639     if (SiteSum == 0)
640       SiteSum = 1;
641 
642     for (uint32_t V = 0; V < NV; V++) {
643       OS << "\t[ " << format("%2u", I) << ", ";
644       if (Symtab == nullptr)
645         OS << format("%4u", VD[V].Value);
646       else
647         OS << Symtab->getFuncName(VD[V].Value);
648       OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
649          << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
650     }
651   }
652 }
653 
654 static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
655                                 ValueSitesStats &Stats) {
656   OS << "  Total number of sites: " << Stats.TotalNumValueSites << "\n";
657   OS << "  Total number of sites with values: "
658      << Stats.TotalNumValueSitesWithValueProfile << "\n";
659   OS << "  Total number of profiled values: " << Stats.TotalNumValues << "\n";
660 
661   OS << "  Value sites histogram:\n\tNumTargets, SiteCount\n";
662   for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
663     if (Stats.ValueSitesHistogram[I] > 0)
664       OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
665   }
666 }
667 
668 static int showInstrProfile(const std::string &Filename, bool ShowCounts,
669                             uint32_t TopN, bool ShowIndirectCallTargets,
670                             bool ShowMemOPSizes, bool ShowDetailedSummary,
671                             std::vector<uint32_t> DetailedSummaryCutoffs,
672                             bool ShowAllFunctions, uint64_t ValueCutoff,
673                             bool OnlyListBelow, const std::string &ShowFunction,
674                             bool TextFormat, raw_fd_ostream &OS) {
675   auto ReaderOrErr = InstrProfReader::create(Filename);
676   std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
677   if (ShowDetailedSummary && Cutoffs.empty()) {
678     Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
679   }
680   InstrProfSummaryBuilder Builder(std::move(Cutoffs));
681   if (Error E = ReaderOrErr.takeError())
682     exitWithError(std::move(E), Filename);
683 
684   auto Reader = std::move(ReaderOrErr.get());
685   bool IsIRInstr = Reader->isIRLevelProfile();
686   size_t ShownFunctions = 0;
687   size_t BelowCutoffFunctions = 0;
688   int NumVPKind = IPVK_Last - IPVK_First + 1;
689   std::vector<ValueSitesStats> VPStats(NumVPKind);
690 
691   auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
692                    const std::pair<std::string, uint64_t> &v2) {
693     return v1.second > v2.second;
694   };
695 
696   std::priority_queue<std::pair<std::string, uint64_t>,
697                       std::vector<std::pair<std::string, uint64_t>>,
698                       decltype(MinCmp)>
699       HottestFuncs(MinCmp);
700 
701   if (!TextFormat && OnlyListBelow) {
702     OS << "The list of functions with the maximum counter less than "
703        << ValueCutoff << ":\n";
704   }
705 
706   // Add marker so that IR-level instrumentation round-trips properly.
707   if (TextFormat && IsIRInstr)
708     OS << ":ir\n";
709 
710   for (const auto &Func : *Reader) {
711     bool Show =
712         ShowAllFunctions || (!ShowFunction.empty() &&
713                              Func.Name.find(ShowFunction) != Func.Name.npos);
714 
715     bool doTextFormatDump = (Show && TextFormat);
716 
717     if (doTextFormatDump) {
718       InstrProfSymtab &Symtab = Reader->getSymtab();
719       InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
720                                          OS);
721       continue;
722     }
723 
724     assert(Func.Counts.size() > 0 && "function missing entry counter");
725     Builder.addRecord(Func);
726 
727     uint64_t FuncMax = 0;
728     uint64_t FuncSum = 0;
729     for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
730       FuncMax = std::max(FuncMax, Func.Counts[I]);
731       FuncSum += Func.Counts[I];
732     }
733 
734     if (FuncMax < ValueCutoff) {
735       ++BelowCutoffFunctions;
736       if (OnlyListBelow) {
737         OS << "  " << Func.Name << ": (Max = " << FuncMax
738            << " Sum = " << FuncSum << ")\n";
739       }
740       continue;
741     } else if (OnlyListBelow)
742       continue;
743 
744     if (TopN) {
745       if (HottestFuncs.size() == TopN) {
746         if (HottestFuncs.top().second < FuncMax) {
747           HottestFuncs.pop();
748           HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
749         }
750       } else
751         HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
752     }
753 
754     if (Show) {
755       if (!ShownFunctions)
756         OS << "Counters:\n";
757 
758       ++ShownFunctions;
759 
760       OS << "  " << Func.Name << ":\n"
761          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
762          << "    Counters: " << Func.Counts.size() << "\n";
763       if (!IsIRInstr)
764         OS << "    Function count: " << Func.Counts[0] << "\n";
765 
766       if (ShowIndirectCallTargets)
767         OS << "    Indirect Call Site Count: "
768            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
769 
770       uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
771       if (ShowMemOPSizes && NumMemOPCalls > 0)
772         OS << "    Number of Memory Intrinsics Calls: " << NumMemOPCalls
773            << "\n";
774 
775       if (ShowCounts) {
776         OS << "    Block counts: [";
777         size_t Start = (IsIRInstr ? 0 : 1);
778         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
779           OS << (I == Start ? "" : ", ") << Func.Counts[I];
780         }
781         OS << "]\n";
782       }
783 
784       if (ShowIndirectCallTargets) {
785         OS << "    Indirect Target Results:\n";
786         traverseAllValueSites(Func, IPVK_IndirectCallTarget,
787                               VPStats[IPVK_IndirectCallTarget], OS,
788                               &(Reader->getSymtab()));
789       }
790 
791       if (ShowMemOPSizes && NumMemOPCalls > 0) {
792         OS << "    Memory Intrinsic Size Results:\n";
793         traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
794                               nullptr);
795       }
796     }
797   }
798   if (Reader->hasError())
799     exitWithError(Reader->getError(), Filename);
800 
801   if (TextFormat)
802     return 0;
803   std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
804   OS << "Instrumentation level: "
805      << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n";
806   if (ShowAllFunctions || !ShowFunction.empty())
807     OS << "Functions shown: " << ShownFunctions << "\n";
808   OS << "Total functions: " << PS->getNumFunctions() << "\n";
809   if (ValueCutoff > 0) {
810     OS << "Number of functions with maximum count (< " << ValueCutoff
811        << "): " << BelowCutoffFunctions << "\n";
812     OS << "Number of functions with maximum count (>= " << ValueCutoff
813        << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
814   }
815   OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
816   OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
817 
818   if (TopN) {
819     std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
820     while (!HottestFuncs.empty()) {
821       SortedHottestFuncs.emplace_back(HottestFuncs.top());
822       HottestFuncs.pop();
823     }
824     OS << "Top " << TopN
825        << " functions with the largest internal block counts: \n";
826     for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
827       OS << "  " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
828   }
829 
830   if (ShownFunctions && ShowIndirectCallTargets) {
831     OS << "Statistics for indirect call sites profile:\n";
832     showValueSitesStats(OS, IPVK_IndirectCallTarget,
833                         VPStats[IPVK_IndirectCallTarget]);
834   }
835 
836   if (ShownFunctions && ShowMemOPSizes) {
837     OS << "Statistics for memory intrinsic calls sizes profile:\n";
838     showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
839   }
840 
841   if (ShowDetailedSummary) {
842     OS << "Detailed summary:\n";
843     OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
844     OS << "Total count: " << PS->getTotalCount() << "\n";
845     for (auto Entry : PS->getDetailedSummary()) {
846       OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount
847          << " account for "
848          << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100)
849          << " percentage of the total counts.\n";
850     }
851   }
852   return 0;
853 }
854 
855 static int showSampleProfile(const std::string &Filename, bool ShowCounts,
856                              bool ShowAllFunctions,
857                              const std::string &ShowFunction,
858                              raw_fd_ostream &OS) {
859   using namespace sampleprof;
860   LLVMContext Context;
861   auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
862   if (std::error_code EC = ReaderOrErr.getError())
863     exitWithErrorCode(EC, Filename);
864 
865   auto Reader = std::move(ReaderOrErr.get());
866   if (std::error_code EC = Reader->read())
867     exitWithErrorCode(EC, Filename);
868 
869   if (ShowAllFunctions || ShowFunction.empty())
870     Reader->dump(OS);
871   else
872     Reader->dumpFunctionProfile(ShowFunction, OS);
873 
874   return 0;
875 }
876 
877 static int show_main(int argc, const char *argv[]) {
878   cl::opt<std::string> Filename(cl::Positional, cl::Required,
879                                 cl::desc("<profdata-file>"));
880 
881   cl::opt<bool> ShowCounts("counts", cl::init(false),
882                            cl::desc("Show counter values for shown functions"));
883   cl::opt<bool> TextFormat(
884       "text", cl::init(false),
885       cl::desc("Show instr profile data in text dump format"));
886   cl::opt<bool> ShowIndirectCallTargets(
887       "ic-targets", cl::init(false),
888       cl::desc("Show indirect call site target values for shown functions"));
889   cl::opt<bool> ShowMemOPSizes(
890       "memop-sizes", cl::init(false),
891       cl::desc("Show the profiled sizes of the memory intrinsic calls "
892                "for shown functions"));
893   cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
894                                     cl::desc("Show detailed profile summary"));
895   cl::list<uint32_t> DetailedSummaryCutoffs(
896       cl::CommaSeparated, "detailed-summary-cutoffs",
897       cl::desc(
898           "Cutoff percentages (times 10000) for generating detailed summary"),
899       cl::value_desc("800000,901000,999999"));
900   cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
901                                  cl::desc("Details for every function"));
902   cl::opt<std::string> ShowFunction("function",
903                                     cl::desc("Details for matching functions"));
904 
905   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
906                                       cl::init("-"), cl::desc("Output file"));
907   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
908                             cl::aliasopt(OutputFilename));
909   cl::opt<ProfileKinds> ProfileKind(
910       cl::desc("Profile kind:"), cl::init(instr),
911       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
912                  clEnumVal(sample, "Sample profile")));
913   cl::opt<uint32_t> TopNFunctions(
914       "topn", cl::init(0),
915       cl::desc("Show the list of functions with the largest internal counts"));
916   cl::opt<uint32_t> ValueCutoff(
917       "value-cutoff", cl::init(0),
918       cl::desc("Set the count value cutoff. Functions with the maximum count "
919                "less than this value will not be printed out. (Default is 0)"));
920   cl::opt<bool> OnlyListBelow(
921       "list-below-cutoff", cl::init(false),
922       cl::desc("Only output names of functions whose max count values are "
923                "below the cutoff value"));
924   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
925 
926   if (OutputFilename.empty())
927     OutputFilename = "-";
928 
929   std::error_code EC;
930   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text);
931   if (EC)
932     exitWithErrorCode(EC, OutputFilename);
933 
934   if (ShowAllFunctions && !ShowFunction.empty())
935     WithColor::warning() << "-function argument ignored: showing all functions\n";
936 
937   std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(),
938                                 DetailedSummaryCutoffs.end());
939   if (ProfileKind == instr)
940     return showInstrProfile(Filename, ShowCounts, TopNFunctions,
941                             ShowIndirectCallTargets, ShowMemOPSizes,
942                             ShowDetailedSummary, DetailedSummaryCutoffs,
943                             ShowAllFunctions, ValueCutoff, OnlyListBelow,
944                             ShowFunction, TextFormat, OS);
945   else
946     return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
947                              ShowFunction, OS);
948 }
949 
950 int main(int argc, const char *argv[]) {
951   InitLLVM X(argc, argv);
952 
953   StringRef ProgName(sys::path::filename(argv[0]));
954   if (argc > 1) {
955     int (*func)(int, const char *[]) = nullptr;
956 
957     if (strcmp(argv[1], "merge") == 0)
958       func = merge_main;
959     else if (strcmp(argv[1], "show") == 0)
960       func = show_main;
961 
962     if (func) {
963       std::string Invocation(ProgName.str() + " " + argv[1]);
964       argv[1] = Invocation.c_str();
965       return func(argc - 1, argv + 1);
966     }
967 
968     if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
969         strcmp(argv[1], "--help") == 0) {
970 
971       errs() << "OVERVIEW: LLVM profile data tools\n\n"
972              << "USAGE: " << ProgName << " <command> [args...]\n"
973              << "USAGE: " << ProgName << " <command> -help\n\n"
974              << "See each individual command --help for more details.\n"
975              << "Available commands: merge, show\n";
976       return 0;
977     }
978   }
979 
980   if (argc < 2)
981     errs() << ProgName << ": No command specified!\n";
982   else
983     errs() << ProgName << ": Unknown command!\n";
984 
985   errs() << "USAGE: " << ProgName << " <merge|show> [args...]\n";
986   return 1;
987 }
988