1 //===- llvm-profgen.cpp - LLVM SPGO profile generation tool -----*- C++ -*-===//
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-profgen generates SPGO profiles from perf script ouput.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "ErrorHandling.h"
14 #include "PerfReader.h"
15 #include "ProfileGenerator.h"
16 #include "ProfiledBinary.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/InitLLVM.h"
20 #include "llvm/Support/TargetSelect.h"
21 
22 static cl::OptionCategory ProfGenCategory("ProfGen Options");
23 
24 static cl::opt<std::string> PerfScriptFilename(
25     "perfscript", cl::value_desc("perfscript"), cl::ZeroOrMore,
26     llvm::cl::MiscFlags::CommaSeparated,
27     cl::desc("Path of perf-script trace created by Linux perf tool with "
28              "`script` command(the raw perf.data should be profiled with -b)"),
29     cl::cat(ProfGenCategory));
30 static cl::alias PSA("ps", cl::desc("Alias for --perfscript"),
31                      cl::aliasopt(PerfScriptFilename));
32 
33 static cl::opt<std::string> PerfDataFilename(
34     "perfdata", cl::value_desc("perfdata"), cl::ZeroOrMore,
35     llvm::cl::MiscFlags::CommaSeparated,
36     cl::desc("Path of raw perf data created by Linux perf tool (it should be "
37              "profiled with -b)"),
38     cl::cat(ProfGenCategory));
39 static cl::alias PDA("pd", cl::desc("Alias for --perfdata"),
40                      cl::aliasopt(PerfDataFilename));
41 
42 static cl::opt<std::string> UnsymbolizedProfFilename(
43     "unsymbolized-profile", cl::value_desc("unsymbolized profile"),
44     cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated,
45     cl::desc("Path of the unsymbolized profile created by "
46              "`llvm-profgen` with `--skip-symbolization`"),
47     cl::cat(ProfGenCategory));
48 static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"),
49                      cl::aliasopt(UnsymbolizedProfFilename));
50 
51 static cl::opt<std::string> BinaryPath(
52     "binary", cl::value_desc("binary"), cl::Required,
53     cl::desc("Path of profiled binary, only one binary is supported."),
54     cl::cat(ProfGenCategory));
55 
56 extern cl::opt<bool> ShowDisassemblyOnly;
57 extern cl::opt<bool> ShowSourceLocations;
58 extern cl::opt<bool> SkipSymbolization;
59 
60 using namespace llvm;
61 using namespace sampleprof;
62 
63 // Validate the command line input.
64 static void validateCommandLine() {
65   // Allow the missing perfscript if we only use to show binary disassembly.
66   if (!ShowDisassemblyOnly) {
67     // Validate input profile is provided only once
68     uint16_t HasPerfData = PerfDataFilename.getNumOccurrences();
69     uint16_t HasPerfScript = PerfScriptFilename.getNumOccurrences();
70     uint16_t HasUnsymbolizedProfile =
71         UnsymbolizedProfFilename.getNumOccurrences();
72     uint16_t S = HasPerfData + HasPerfScript + HasUnsymbolizedProfile;
73     if (S != 1) {
74       std::string Msg =
75           S > 1
76               ? "`--perfscript`, `--perfdata` and `--unsymbolized-profile` "
77                 "cannot be used together."
78               : "Perf input file is missing, please use one of `--perfscript`, "
79                 "`--perfdata` and `--unsymbolized-profile` for the input.";
80       exitWithError(Msg);
81     }
82 
83     auto CheckFileExists = [](bool H, StringRef File) {
84       if (H && !llvm::sys::fs::exists(File)) {
85         std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";
86         exitWithError(Msg);
87       }
88     };
89 
90     CheckFileExists(HasPerfData, PerfDataFilename);
91     CheckFileExists(HasPerfScript, PerfScriptFilename);
92     CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename);
93   }
94 
95   if (!llvm::sys::fs::exists(BinaryPath)) {
96     std::string Msg = "Input binary(" + BinaryPath + ") doesn't exist.";
97     exitWithError(Msg);
98   }
99 
100   if (CSProfileGenerator::MaxCompressionSize < -1) {
101     exitWithError("Value of --compress-recursion should >= -1");
102   }
103   if (ShowSourceLocations && !ShowDisassemblyOnly) {
104     exitWithError("--show-source-locations should work together with "
105                   "--show-disassembly-only!");
106   }
107 }
108 
109 static PerfInputFile getPerfInputFile() {
110   PerfInputFile File;
111   if (PerfDataFilename.getNumOccurrences()) {
112     File.InputFile = PerfDataFilename;
113     File.Format = PerfFormat::PerfData;
114   } else if (PerfScriptFilename.getNumOccurrences()) {
115     File.InputFile = PerfScriptFilename;
116     File.Format = PerfFormat::PerfScript;
117   } else if (UnsymbolizedProfFilename.getNumOccurrences()) {
118     File.InputFile = UnsymbolizedProfFilename;
119     File.Format = PerfFormat::UnsymbolizedProfile;
120   }
121   return File;
122 }
123 
124 int main(int argc, const char *argv[]) {
125   InitLLVM X(argc, argv);
126 
127   // Initialize targets and assembly printers/parsers.
128   InitializeAllTargetInfos();
129   InitializeAllTargetMCs();
130   InitializeAllDisassemblers();
131 
132   cl::HideUnrelatedOptions({&ProfGenCategory, &getColorCategory()});
133   cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n");
134   validateCommandLine();
135 
136   // Load symbols and disassemble the code of a given binary.
137   std::unique_ptr<ProfiledBinary> Binary =
138       std::make_unique<ProfiledBinary>(BinaryPath);
139   if (ShowDisassemblyOnly)
140     return EXIT_SUCCESS;
141 
142   PerfInputFile PerfFile = getPerfInputFile();
143   std::unique_ptr<PerfReaderBase> Reader =
144       PerfReaderBase::create(Binary.get(), PerfFile);
145   // Parse perf events and samples
146   Reader->parsePerfTraces();
147 
148   if (SkipSymbolization)
149     return EXIT_SUCCESS;
150 
151   std::unique_ptr<ProfileGeneratorBase> Generator =
152       ProfileGeneratorBase::create(Binary.get(), Reader->getSampleCounters(),
153                                    Reader->profileIsCSFlat());
154   Generator->generateProfile();
155   Generator->write();
156 
157   return EXIT_SUCCESS;
158 }
159