1 //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // llvm-profdata merges .profdata files.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ADT/SmallSet.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/IR/LLVMContext.h"
18 #include "llvm/ProfileData/InstrProfReader.h"
19 #include "llvm/ProfileData/InstrProfWriter.h"
20 #include "llvm/ProfileData/ProfileCommon.h"
21 #include "llvm/ProfileData/SampleProfReader.h"
22 #include "llvm/ProfileData/SampleProfWriter.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/Errc.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/Format.h"
27 #include "llvm/Support/ManagedStatic.h"
28 #include "llvm/Support/MemoryBuffer.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/PrettyStackTrace.h"
31 #include "llvm/Support/Signals.h"
32 #include "llvm/Support/raw_ostream.h"
33 #include <algorithm>
34 #include <tuple>
35 
36 using namespace llvm;
37 
38 enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC };
39 
40 static void exitWithError(const Twine &Message, StringRef Whence = "",
41                           StringRef Hint = "") {
42   errs() << "error: ";
43   if (!Whence.empty())
44     errs() << Whence << ": ";
45   errs() << Message << "\n";
46   if (!Hint.empty())
47     errs() << Hint << "\n";
48   ::exit(1);
49 }
50 
51 static void exitWithErrorCode(const std::error_code &Error,
52                               StringRef Whence = "") {
53   if (Error.category() == instrprof_category()) {
54     instrprof_error instrError = static_cast<instrprof_error>(Error.value());
55     if (instrError == instrprof_error::unrecognized_format) {
56       // Hint for common error of forgetting -sample for sample profiles.
57       exitWithError(Error.message(), Whence,
58                     "Perhaps you forgot to use the -sample option?");
59     }
60   }
61   exitWithError(Error.message(), Whence);
62 }
63 
64 namespace {
65 enum ProfileKinds { instr, sample };
66 }
67 
68 static void handleMergeWriterError(std::error_code &Error,
69                                    StringRef WhenceFile = "",
70                                    StringRef WhenceFunction = "",
71                                    bool ShowHint = true) {
72   if (!WhenceFile.empty())
73     errs() << WhenceFile << ": ";
74   if (!WhenceFunction.empty())
75     errs() << WhenceFunction << ": ";
76   errs() << Error.message() << "\n";
77 
78   if (ShowHint) {
79     StringRef Hint = "";
80     if (Error.category() == instrprof_category()) {
81       instrprof_error instrError = static_cast<instrprof_error>(Error.value());
82       switch (instrError) {
83       case instrprof_error::hash_mismatch:
84       case instrprof_error::count_mismatch:
85       case instrprof_error::value_site_count_mismatch:
86         Hint = "Make sure that all profile data to be merged is generated "
87                "from the same binary.";
88         break;
89       default:
90         break;
91       }
92     }
93 
94     if (!Hint.empty())
95       errs() << Hint << "\n";
96   }
97 }
98 
99 struct WeightedFile {
100   StringRef Filename;
101   uint64_t Weight;
102 
103   WeightedFile() {}
104 
105   WeightedFile(StringRef F, uint64_t W) : Filename{F}, Weight{W} {}
106 };
107 typedef SmallVector<WeightedFile, 5> WeightedFileVector;
108 
109 static void mergeInstrProfile(const WeightedFileVector &Inputs,
110                               StringRef OutputFilename,
111                               ProfileFormat OutputFormat, bool OutputSparse) {
112   if (OutputFilename.compare("-") == 0)
113     exitWithError("Cannot write indexed profdata format to stdout.");
114 
115   if (OutputFormat != PF_Binary && OutputFormat != PF_Text)
116     exitWithError("Unknown format is specified.");
117 
118   std::error_code EC;
119   raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None);
120   if (EC)
121     exitWithErrorCode(EC, OutputFilename);
122 
123   InstrProfWriter Writer(OutputSparse);
124   SmallSet<std::error_code, 4> WriterErrorCodes;
125   for (const auto &Input : Inputs) {
126     auto ReaderOrErr = InstrProfReader::create(Input.Filename);
127     if (std::error_code ec = ReaderOrErr.getError())
128       exitWithErrorCode(ec, Input.Filename);
129 
130     auto Reader = std::move(ReaderOrErr.get());
131     bool IsIRProfile = Reader->isIRLevelProfile();
132     if (Writer.setIsIRLevelProfile(IsIRProfile))
133       exitWithError("Merge IR generated profile with Clang generated profile.");
134 
135     for (auto &I : *Reader) {
136       if (std::error_code EC = Writer.addRecord(std::move(I), Input.Weight)) {
137         // Only show hint the first time an error occurs.
138         bool firstTime = WriterErrorCodes.insert(EC).second;
139         handleMergeWriterError(EC, Input.Filename, I.Name, firstTime);
140       }
141     }
142     if (Reader->hasError())
143       exitWithErrorCode(Reader->getError(), Input.Filename);
144   }
145   if (OutputFormat == PF_Text)
146     Writer.writeText(Output);
147   else
148     Writer.write(Output);
149 }
150 
151 static sampleprof::SampleProfileFormat FormatMap[] = {
152     sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary,
153     sampleprof::SPF_GCC};
154 
155 static void mergeSampleProfile(const WeightedFileVector &Inputs,
156                                StringRef OutputFilename,
157                                ProfileFormat OutputFormat) {
158   using namespace sampleprof;
159   auto WriterOrErr =
160       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
161   if (std::error_code EC = WriterOrErr.getError())
162     exitWithErrorCode(EC, OutputFilename);
163 
164   auto Writer = std::move(WriterOrErr.get());
165   StringMap<FunctionSamples> ProfileMap;
166   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
167   for (const auto &Input : Inputs) {
168     auto ReaderOrErr =
169         SampleProfileReader::create(Input.Filename, getGlobalContext());
170     if (std::error_code EC = ReaderOrErr.getError())
171       exitWithErrorCode(EC, Input.Filename);
172 
173     // We need to keep the readers around until after all the files are
174     // read so that we do not lose the function names stored in each
175     // reader's memory. The function names are needed to write out the
176     // merged profile map.
177     Readers.push_back(std::move(ReaderOrErr.get()));
178     const auto Reader = Readers.back().get();
179     if (std::error_code EC = Reader->read())
180       exitWithErrorCode(EC, Input.Filename);
181 
182     StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
183     for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
184                                               E = Profiles.end();
185          I != E; ++I) {
186       StringRef FName = I->first();
187       FunctionSamples &Samples = I->second;
188       sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight);
189       if (Result != sampleprof_error::success) {
190         std::error_code EC = make_error_code(Result);
191         handleMergeWriterError(EC, Input.Filename, FName);
192       }
193     }
194   }
195   Writer->write(ProfileMap);
196 }
197 
198 static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
199   StringRef WeightStr, FileName;
200   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
201 
202   uint64_t Weight;
203   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
204     exitWithError("Input weight must be a positive integer.");
205 
206   if (!sys::fs::exists(FileName))
207     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
208                       FileName);
209 
210   return WeightedFile(FileName, Weight);
211 }
212 
213 static int merge_main(int argc, const char *argv[]) {
214   cl::list<std::string> InputFilenames(cl::Positional,
215                                        cl::desc("<filename...>"));
216   cl::list<std::string> WeightedInputFilenames("weighted-input",
217                                                cl::desc("<weight>,<filename>"));
218   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
219                                       cl::init("-"), cl::Required,
220                                       cl::desc("Output file"));
221   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
222                             cl::aliasopt(OutputFilename));
223   cl::opt<ProfileKinds> ProfileKind(
224       cl::desc("Profile kind:"), cl::init(instr),
225       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
226                  clEnumVal(sample, "Sample profile"), clEnumValEnd));
227 
228   cl::opt<ProfileFormat> OutputFormat(
229       cl::desc("Format of output profile"), cl::init(PF_Binary),
230       cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
231                  clEnumValN(PF_Text, "text", "Text encoding"),
232                  clEnumValN(PF_GCC, "gcc",
233                             "GCC encoding (only meaningful for -sample)"),
234                  clEnumValEnd));
235 
236   cl::opt<bool> OutputSparse("sparse", cl::init(false),
237       cl::desc("Generate a sparse profile (only meaningful for -instr)"));
238 
239   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
240 
241   if (InputFilenames.empty() && WeightedInputFilenames.empty())
242     exitWithError("No input files specified. See " +
243                   sys::path::filename(argv[0]) + " -help");
244 
245   WeightedFileVector WeightedInputs;
246   for (StringRef Filename : InputFilenames)
247     WeightedInputs.push_back(WeightedFile(Filename, 1));
248   for (StringRef WeightedFilename : WeightedInputFilenames)
249     WeightedInputs.push_back(parseWeightedFile(WeightedFilename));
250 
251   if (ProfileKind == instr)
252     mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat,
253                       OutputSparse);
254   else
255     mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
256 
257   return 0;
258 }
259 
260 static int showInstrProfile(std::string Filename, bool ShowCounts,
261                             bool ShowIndirectCallTargets,
262                             bool ShowDetailedSummary,
263                             std::vector<uint32_t> DetailedSummaryCutoffs,
264                             bool ShowAllFunctions, std::string ShowFunction,
265                             bool TextFormat, raw_fd_ostream &OS) {
266   auto ReaderOrErr = InstrProfReader::create(Filename);
267   std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs);
268   if (ShowDetailedSummary && DetailedSummaryCutoffs.empty()) {
269     Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
270   }
271   InstrProfSummary PS(Cutoffs);
272   if (std::error_code EC = ReaderOrErr.getError())
273     exitWithErrorCode(EC, Filename);
274 
275   auto Reader = std::move(ReaderOrErr.get());
276   bool IsIRInstr = Reader->isIRLevelProfile();
277   size_t ShownFunctions = 0;
278   for (const auto &Func : *Reader) {
279     bool Show =
280         ShowAllFunctions || (!ShowFunction.empty() &&
281                              Func.Name.find(ShowFunction) != Func.Name.npos);
282 
283     bool doTextFormatDump = (Show && ShowCounts && TextFormat);
284 
285     if (doTextFormatDump) {
286       InstrProfSymtab &Symtab = Reader->getSymtab();
287       InstrProfWriter::writeRecordInText(Func, Symtab, OS);
288       continue;
289     }
290 
291     assert(Func.Counts.size() > 0 && "function missing entry counter");
292     PS.addRecord(Func);
293 
294     if (Show) {
295 
296       if (!ShownFunctions)
297         OS << "Counters:\n";
298 
299       ++ShownFunctions;
300 
301       OS << "  " << Func.Name << ":\n"
302          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
303          << "    Counters: " << Func.Counts.size() << "\n";
304       if (!IsIRInstr)
305         OS << "    Function count: " << Func.Counts[0] << "\n";
306 
307       if (ShowIndirectCallTargets)
308         OS << "    Indirect Call Site Count: "
309            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
310 
311       if (ShowCounts) {
312         OS << "    Block counts: [";
313         size_t Start = (IsIRInstr ? 0 : 1);
314         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
315           OS << (I == Start ? "" : ", ") << Func.Counts[I];
316         }
317         OS << "]\n";
318       }
319 
320       if (ShowIndirectCallTargets) {
321         InstrProfSymtab &Symtab = Reader->getSymtab();
322         uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget);
323         OS << "    Indirect Target Results: \n";
324         for (size_t I = 0; I < NS; ++I) {
325           uint32_t NV = Func.getNumValueDataForSite(IPVK_IndirectCallTarget, I);
326           std::unique_ptr<InstrProfValueData[]> VD =
327               Func.getValueForSite(IPVK_IndirectCallTarget, I);
328           for (uint32_t V = 0; V < NV; V++) {
329             OS << "\t[ " << I << ", ";
330             OS << Symtab.getFuncName(VD[V].Value) << ", " << VD[V].Count
331                << " ]\n";
332           }
333         }
334       }
335     }
336   }
337 
338   if (Reader->hasError())
339     exitWithErrorCode(Reader->getError(), Filename);
340 
341   if (ShowCounts && TextFormat)
342     return 0;
343 
344   if (ShowAllFunctions || !ShowFunction.empty())
345     OS << "Functions shown: " << ShownFunctions << "\n";
346   OS << "Total functions: " << PS.getNumFunctions() << "\n";
347   OS << "Maximum function count: " << PS.getMaxFunctionCount() << "\n";
348   OS << "Maximum internal block count: " << PS.getMaxInternalBlockCount() << "\n";
349 
350   if (ShowDetailedSummary) {
351     OS << "Detailed summary:\n";
352     OS << "Total number of blocks: " << PS.getNumBlocks() << "\n";
353     OS << "Total count: " << PS.getTotalCount() << "\n";
354     for (auto Entry : PS.getDetailedSummary()) {
355       OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount
356          << " account for "
357          << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100)
358          << " percentage of the total counts.\n";
359     }
360   }
361   return 0;
362 }
363 
364 static int showSampleProfile(std::string Filename, bool ShowCounts,
365                              bool ShowAllFunctions, std::string ShowFunction,
366                              raw_fd_ostream &OS) {
367   using namespace sampleprof;
368   auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext());
369   if (std::error_code EC = ReaderOrErr.getError())
370     exitWithErrorCode(EC, Filename);
371 
372   auto Reader = std::move(ReaderOrErr.get());
373   if (std::error_code EC = Reader->read())
374     exitWithErrorCode(EC, Filename);
375 
376   if (ShowAllFunctions || ShowFunction.empty())
377     Reader->dump(OS);
378   else
379     Reader->dumpFunctionProfile(ShowFunction, OS);
380 
381   return 0;
382 }
383 
384 static int show_main(int argc, const char *argv[]) {
385   cl::opt<std::string> Filename(cl::Positional, cl::Required,
386                                 cl::desc("<profdata-file>"));
387 
388   cl::opt<bool> ShowCounts("counts", cl::init(false),
389                            cl::desc("Show counter values for shown functions"));
390   cl::opt<bool> TextFormat(
391       "text", cl::init(false),
392       cl::desc("Show instr profile data in text dump format"));
393   cl::opt<bool> ShowIndirectCallTargets(
394       "ic-targets", cl::init(false),
395       cl::desc("Show indirect call site target values for shown functions"));
396   cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
397                                     cl::desc("Show detailed profile summary"));
398   cl::list<uint32_t> DetailedSummaryCutoffs(
399       cl::CommaSeparated, "detailed-summary-cutoffs",
400       cl::desc(
401           "Cutoff percentages (times 10000) for generating detailed summary"),
402       cl::value_desc("800000,901000,999999"));
403   cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
404                                  cl::desc("Details for every function"));
405   cl::opt<std::string> ShowFunction("function",
406                                     cl::desc("Details for matching functions"));
407 
408   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
409                                       cl::init("-"), cl::desc("Output file"));
410   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
411                             cl::aliasopt(OutputFilename));
412   cl::opt<ProfileKinds> ProfileKind(
413       cl::desc("Profile kind:"), cl::init(instr),
414       cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
415                  clEnumVal(sample, "Sample profile"), clEnumValEnd));
416 
417   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
418 
419   if (OutputFilename.empty())
420     OutputFilename = "-";
421 
422   std::error_code EC;
423   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text);
424   if (EC)
425     exitWithErrorCode(EC, OutputFilename);
426 
427   if (ShowAllFunctions && !ShowFunction.empty())
428     errs() << "warning: -function argument ignored: showing all functions\n";
429 
430   std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(),
431                                 DetailedSummaryCutoffs.end());
432   if (ProfileKind == instr)
433     return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets,
434                             ShowDetailedSummary, DetailedSummaryCutoffs,
435                             ShowAllFunctions, ShowFunction, TextFormat, OS);
436   else
437     return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
438                              ShowFunction, OS);
439 }
440 
441 int main(int argc, const char *argv[]) {
442   // Print a stack trace if we signal out.
443   sys::PrintStackTraceOnErrorSignal();
444   PrettyStackTraceProgram X(argc, argv);
445   llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
446 
447   StringRef ProgName(sys::path::filename(argv[0]));
448   if (argc > 1) {
449     int (*func)(int, const char *[]) = nullptr;
450 
451     if (strcmp(argv[1], "merge") == 0)
452       func = merge_main;
453     else if (strcmp(argv[1], "show") == 0)
454       func = show_main;
455 
456     if (func) {
457       std::string Invocation(ProgName.str() + " " + argv[1]);
458       argv[1] = Invocation.c_str();
459       return func(argc - 1, argv + 1);
460     }
461 
462     if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
463         strcmp(argv[1], "--help") == 0) {
464 
465       errs() << "OVERVIEW: LLVM profile data tools\n\n"
466              << "USAGE: " << ProgName << " <command> [args...]\n"
467              << "USAGE: " << ProgName << " <command> -help\n\n"
468              << "Available commands: merge, show\n";
469       return 0;
470     }
471   }
472 
473   if (argc < 2)
474     errs() << ProgName << ": No command specified!\n";
475   else
476     errs() << ProgName << ": Unknown command!\n";
477 
478   errs() << "USAGE: " << ProgName << " <merge|show> [args...]\n";
479   return 1;
480 }
481