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/DebugInfo/Symbolize/SymbolizableModule.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/InitLLVM.h"
21 #include "llvm/Support/TargetSelect.h"
22
23 static cl::OptionCategory ProfGenCategory("ProfGen Options");
24
25 static cl::opt<std::string> PerfScriptFilename(
26 "perfscript", cl::value_desc("perfscript"),
27 llvm::cl::MiscFlags::CommaSeparated,
28 cl::desc("Path of perf-script trace created by Linux perf tool with "
29 "`script` command(the raw perf.data should be profiled with -b)"),
30 cl::cat(ProfGenCategory));
31 static cl::alias PSA("ps", cl::desc("Alias for --perfscript"),
32 cl::aliasopt(PerfScriptFilename));
33
34 static cl::opt<std::string> PerfDataFilename(
35 "perfdata", cl::value_desc("perfdata"), 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 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> SampleProfFilename(
52 "llvm-sample-profile", cl::value_desc("llvm sample profile"),
53 cl::desc("Path of the LLVM sample profile"), cl::cat(ProfGenCategory));
54
55 static cl::opt<std::string>
56 BinaryPath("binary", cl::value_desc("binary"), cl::Required,
57 cl::desc("Path of profiled executable binary."),
58 cl::cat(ProfGenCategory));
59
60 static cl::opt<uint32_t>
61 ProcessId("pid", cl::value_desc("process Id"), cl::init(0),
62 cl::desc("Process Id for the profiled executable binary."),
63 cl::cat(ProfGenCategory));
64
65 static cl::opt<std::string> DebugBinPath(
66 "debug-binary", cl::value_desc("debug-binary"),
67 cl::desc("Path of debug info binary, llvm-profgen will load the DWARF info "
68 "from it instead of the executable binary."),
69 cl::cat(ProfGenCategory));
70
71 extern cl::opt<bool> ShowDisassemblyOnly;
72 extern cl::opt<bool> ShowSourceLocations;
73 extern cl::opt<bool> SkipSymbolization;
74
75 using namespace llvm;
76 using namespace sampleprof;
77
78 // Validate the command line input.
validateCommandLine()79 static void validateCommandLine() {
80 // Allow the missing perfscript if we only use to show binary disassembly.
81 if (!ShowDisassemblyOnly) {
82 // Validate input profile is provided only once
83 uint16_t HasPerfData = PerfDataFilename.getNumOccurrences();
84 uint16_t HasPerfScript = PerfScriptFilename.getNumOccurrences();
85 uint16_t HasUnsymbolizedProfile =
86 UnsymbolizedProfFilename.getNumOccurrences();
87 uint16_t HasSampleProfile = SampleProfFilename.getNumOccurrences();
88 uint16_t S =
89 HasPerfData + HasPerfScript + HasUnsymbolizedProfile + HasSampleProfile;
90 if (S != 1) {
91 std::string Msg =
92 S > 1
93 ? "`--perfscript`, `--perfdata` and `--unsymbolized-profile` "
94 "cannot be used together."
95 : "Perf input file is missing, please use one of `--perfscript`, "
96 "`--perfdata` and `--unsymbolized-profile` for the input.";
97 exitWithError(Msg);
98 }
99
100 auto CheckFileExists = [](bool H, StringRef File) {
101 if (H && !llvm::sys::fs::exists(File)) {
102 std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";
103 exitWithError(Msg);
104 }
105 };
106
107 CheckFileExists(HasPerfData, PerfDataFilename);
108 CheckFileExists(HasPerfScript, PerfScriptFilename);
109 CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename);
110 CheckFileExists(HasSampleProfile, SampleProfFilename);
111 }
112
113 if (!llvm::sys::fs::exists(BinaryPath)) {
114 std::string Msg = "Input binary(" + BinaryPath + ") doesn't exist.";
115 exitWithError(Msg);
116 }
117
118 if (CSProfileGenerator::MaxCompressionSize < -1) {
119 exitWithError("Value of --compress-recursion should >= -1");
120 }
121 if (ShowSourceLocations && !ShowDisassemblyOnly) {
122 exitWithError("--show-source-locations should work together with "
123 "--show-disassembly-only!");
124 }
125 }
126
getPerfInputFile()127 static PerfInputFile getPerfInputFile() {
128 PerfInputFile File;
129 if (PerfDataFilename.getNumOccurrences()) {
130 File.InputFile = PerfDataFilename;
131 File.Format = PerfFormat::PerfData;
132 } else if (PerfScriptFilename.getNumOccurrences()) {
133 File.InputFile = PerfScriptFilename;
134 File.Format = PerfFormat::PerfScript;
135 } else if (UnsymbolizedProfFilename.getNumOccurrences()) {
136 File.InputFile = UnsymbolizedProfFilename;
137 File.Format = PerfFormat::UnsymbolizedProfile;
138 }
139 return File;
140 }
141
main(int argc,const char * argv[])142 int main(int argc, const char *argv[]) {
143 InitLLVM X(argc, argv);
144
145 // Initialize targets and assembly printers/parsers.
146 InitializeAllTargetInfos();
147 InitializeAllTargetMCs();
148 InitializeAllDisassemblers();
149
150 cl::HideUnrelatedOptions({&ProfGenCategory, &getColorCategory()});
151 cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n");
152 validateCommandLine();
153
154 // Load symbols and disassemble the code of a given binary.
155 std::unique_ptr<ProfiledBinary> Binary =
156 std::make_unique<ProfiledBinary>(BinaryPath, DebugBinPath);
157 if (ShowDisassemblyOnly)
158 return EXIT_SUCCESS;
159
160 if (SampleProfFilename.getNumOccurrences()) {
161 LLVMContext Context;
162 auto ReaderOrErr = SampleProfileReader::create(SampleProfFilename, Context);
163 std::unique_ptr<sampleprof::SampleProfileReader> Reader =
164 std::move(ReaderOrErr.get());
165 Reader->read();
166 std::unique_ptr<ProfileGeneratorBase> Generator =
167 ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(),
168 Reader->profileIsCS());
169 Generator->generateProfile();
170 Generator->write();
171 } else {
172 Optional<uint32_t> PIDFilter;
173 if (ProcessId.getNumOccurrences())
174 PIDFilter = ProcessId;
175 PerfInputFile PerfFile = getPerfInputFile();
176 std::unique_ptr<PerfReaderBase> Reader =
177 PerfReaderBase::create(Binary.get(), PerfFile, PIDFilter);
178 // Parse perf events and samples
179 Reader->parsePerfTraces();
180
181 if (SkipSymbolization)
182 return EXIT_SUCCESS;
183
184 std::unique_ptr<ProfileGeneratorBase> Generator =
185 ProfileGeneratorBase::create(Binary.get(), &Reader->getSampleCounters(),
186 Reader->profileIsCS());
187 Generator->generateProfile();
188 Generator->write();
189 }
190
191 return EXIT_SUCCESS;
192 }
193