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 "Options.h"
15#include "PerfReader.h"
16#include "ProfileGenerator.h"
17#include "ProfiledBinary.h"
18#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
19#include "llvm/Support/CommandLine.h"
20#include "llvm/Support/FileSystem.h"
21#include "llvm/Support/InitLLVM.h"
22#include "llvm/Support/LibraryOptionsContext.h"
23#include "llvm/Support/TargetSelect.h"
24#include "llvm/Support/VirtualFileSystem.h"
25
26using namespace llvm;
27using namespace sampleprof;
28
29namespace llvm {
30
31cl::OptionCategory ProfGenCategory("ProfGen Options");
32
33static cl::opt<std::string> PerfScriptFilename(
34 "perfscript", cl::value_desc("perfscript"),
35 cl::desc("Path of a trace created by the Linux `perf script` command. For "
36 "LBR or BRBE input, the raw perf data must contain branch "
37 "stacks, for example from recording with -b. "
38 "Cannot be used with --perfdata, --unsymbolized-profile, or "
39 "--llvm-sample-profile."),
40 cl::cat(ProfGenCategory));
41static cl::alias PSA("ps", cl::desc("Alias for --perfscript"),
42 cl::aliasopt(PerfScriptFilename));
43
44static cl::opt<std::string> PerfDataFilename(
45 "perfdata", cl::value_desc("perfdata"),
46 cl::desc("Path of raw perf data created by the Linux perf tool. For LBR or "
47 "BRBE input, it must contain branch stacks, for example from "
48 "recording with -b. Cannot be used with --perfscript, "
49 "--unsymbolized-profile, or --llvm-sample-profile."),
50 cl::cat(ProfGenCategory));
51static cl::alias PDA("pd", cl::desc("Alias for --perfdata"),
52 cl::aliasopt(PerfDataFilename));
53
54static cl::opt<std::string> UnsymbolizedProfFilename(
55 "unsymbolized-profile", cl::value_desc("unsymbolized profile"),
56 cl::desc("Path of the unsymbolized profile created by "
57 "`llvm-profgen` with `--skip-symbolization`. "
58 "Cannot be used with --perfscript, --perfdata, or "
59 "--llvm-sample-profile."),
60 cl::cat(ProfGenCategory));
61static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"),
62 cl::aliasopt(UnsymbolizedProfFilename));
63
64static cl::opt<std::string> SampleProfFilename(
65 "llvm-sample-profile", cl::value_desc("llvm sample profile"),
66 cl::desc("Path of the LLVM sample profile. Cannot be used with"
67 "--perfscript, --perfdata, or --unsymbolized-profile"),
68 cl::cat(ProfGenCategory));
69
70static cl::opt<std::string>
71 BinaryPath("binary", cl::value_desc("binary"), cl::Required,
72 cl::desc("Path of profiled executable binary."),
73 cl::cat(ProfGenCategory));
74
75static cl::opt<uint32_t>
76 ProcessId("pid", cl::value_desc("process Id"), cl::init(Val: 0),
77 cl::desc("Process Id for the profiled executable binary."),
78 cl::cat(ProfGenCategory));
79
80static cl::opt<std::string> DebugBinPath(
81 "debug-binary", cl::value_desc("debug-binary"),
82 cl::desc("Path of debug info binary, llvm-profgen will load the DWARF info "
83 "from it instead of the executable binary."),
84 cl::cat(ProfGenCategory));
85
86static cl::opt<std::string> DataAccessProfileFilename(
87 "data-access-perftrace", cl::value_desc("data-access-perftrace"),
88 cl::desc("File path of a Linux perf raw trace (generated by `perf report "
89 "-D`) consisting of memory access events."),
90 cl::cat(ProfGenCategory));
91
92static cl::opt<std::string> ETMPath("etm", cl::value_desc("etm"),
93 cl::desc("Path of raw ETM trace file"),
94 cl::cat(ProfGenCategory));
95
96static cl::opt<unsigned> ETMTraceID(
97 "etm-trace-id", cl::init(Val: 0x10),
98 cl::desc("CoreSight Trace ID (CSID) used to route ETM trace data."),
99 cl::cat(ProfGenCategory));
100
101static cl::opt<std::string>
102 TargetTriple("target-triple", cl::value_desc("triple"),
103 cl::desc("Override the target triple for the binary"),
104 cl::cat(ProfGenCategory));
105
106// Validate the command line input.
107static void validateCommandLine() {
108 // Allow the missing perfscript if we only use to show binary disassembly.
109 if (!ShowDisassemblyOnly) {
110 // Validate input profile is provided only once
111 bool HasPerfData = PerfDataFilename.getNumOccurrences() > 0;
112 bool HasPerfScript = PerfScriptFilename.getNumOccurrences() > 0;
113 bool HasUnsymbolizedProfile =
114 UnsymbolizedProfFilename.getNumOccurrences() > 0;
115 bool HasSampleProfile = SampleProfFilename.getNumOccurrences() > 0;
116 bool HasEtm = ETMPath.getNumOccurrences() > 0;
117 uint16_t S = HasPerfData + HasPerfScript + HasUnsymbolizedProfile +
118 HasSampleProfile + HasEtm;
119 if (S != 1) {
120 std::string Msg =
121 S > 1 ? "Only one of `--perfscript`, `--perfdata`, "
122 "`--unsymbolized-profile`, "
123 "`--sample-profile` or `--etm` can be used."
124 : "Perf input file is missing. Please provide one of "
125 "`--perfscript`, "
126 "`--perfdata`, `--unsymbolized-profile`, `--sample-profile`, "
127 "`--etm`.";
128 exitWithError(Message: Msg);
129 }
130
131 auto CheckFileExists = [](bool H, StringRef File) {
132 if (H && !llvm::sys::fs::exists(Path: File)) {
133 std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";
134 exitWithError(Message: Msg);
135 }
136 };
137
138 CheckFileExists(HasPerfData, PerfDataFilename);
139 CheckFileExists(HasPerfScript, PerfScriptFilename);
140 CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename);
141 CheckFileExists(HasSampleProfile, SampleProfFilename);
142 CheckFileExists(HasEtm, ETMPath);
143 }
144
145 if (!llvm::sys::fs::exists(Path: BinaryPath)) {
146 std::string Msg = "Input binary(" + BinaryPath + ") doesn't exist.";
147 exitWithError(Message: Msg);
148 }
149
150 if (CSProfileGenerator::MaxCompressionSize < -1) {
151 exitWithError(Message: "Value of --compress-recursion should >= -1");
152 }
153 if (ShowSourceLocations && !ShowDisassemblyOnly) {
154 exitWithError(Message: "--show-source-locations should work together with "
155 "--show-disassembly-only!");
156 }
157}
158
159static InputFile getInputFile() {
160 InputFile File;
161 if (PerfDataFilename.getNumOccurrences()) {
162 File.InputFilePath = PerfDataFilename;
163 File.Format = InputFormat::PerfData;
164 } else if (PerfScriptFilename.getNumOccurrences()) {
165 File.InputFilePath = PerfScriptFilename;
166 File.Format = InputFormat::PerfScript;
167 } else if (UnsymbolizedProfFilename.getNumOccurrences()) {
168 File.InputFilePath = UnsymbolizedProfFilename;
169 File.Format = InputFormat::UnsymbolizedProfile;
170 } else if (ETMPath.getNumOccurrences()) {
171 File.InputFilePath = ETMPath;
172 File.Format = InputFormat::ETMFormat;
173 }
174 return File;
175}
176
177} // end namespace llvm
178
179int main(int argc, const char *argv[]) {
180 InitLLVM X(argc, argv);
181
182 // Initialize targets and assembly printers/parsers.
183 InitializeAllTargetInfos();
184 InitializeAllTargetMCs();
185 InitializeAllDisassemblers();
186
187 cl::HideUnrelatedOptions(Categories: {&ProfGenCategory, &getColorCategory()});
188 cl::LibraryOptionsContext LibraryOptions;
189 cl::parseCommandLineOptions(Context&: LibraryOptions, Args: ArrayRef(argv, argc),
190 Overview: "llvm SPGO profile generator\n");
191 validateCommandLine();
192
193 // Load symbols and disassemble the code of a given binary.
194 std::unique_ptr<ProfiledBinary> Binary = std::make_unique<ProfiledBinary>(
195 args&: LibraryOptions, args&: BinaryPath, args&: DebugBinPath);
196 Binary->load(TripleStr: TargetTriple);
197
198 if (ShowDisassemblyOnly)
199 return EXIT_SUCCESS;
200
201 if (SampleProfFilename.getNumOccurrences()) {
202 LLVMContext Context;
203 auto FS = vfs::getRealFileSystem();
204 auto ReaderOrErr =
205 SampleProfileReader::create(Filename: SampleProfFilename, C&: Context, FS&: *FS);
206 if (std::error_code EC = ReaderOrErr.getError())
207 exitWithError(EC, Whence: SampleProfFilename);
208 std::unique_ptr<sampleprof::SampleProfileReader> Reader =
209 std::move(ReaderOrErr.get());
210 Reader->read();
211 std::unique_ptr<ProfileGeneratorBase> Generator =
212 ProfileGeneratorBase::create(Binary: Binary.get(), ProfileMap&: Reader->getProfiles(),
213 profileIsCS: Reader->profileIsCS());
214 Generator->generateProfile();
215 Generator->write();
216 } else {
217 std::optional<uint32_t> PIDFilter;
218 if (ProcessId.getNumOccurrences())
219 PIDFilter = ProcessId;
220 InputFile File = getInputFile();
221 const ContextSampleCounterMap *Counters = nullptr;
222 bool ProfileIsCS = false;
223 std::unique_ptr<ETMReader> EtmReader;
224 std::unique_ptr<PerfReaderBase> PerfReader;
225
226 if (File.Format == InputFormat::ETMFormat) {
227 EtmReader = std::make_unique<ETMReader>(args: Binary.get(), args&: File.InputFilePath,
228 args: static_cast<uint8_t>(ETMTraceID));
229 EtmReader->parseETMTraces();
230 Counters = &EtmReader->getSampleCounters();
231 } else {
232 PerfReader = PerfReaderBase::create(Binary: Binary.get(), Input&: File, PIDFilter);
233 // Parse perf events and samples
234 PerfReader->parsePerfTraces();
235
236 if (!DataAccessProfileFilename.empty()) {
237 if (PerfReader->profileIsCS() || Binary->usePseudoProbes()) {
238 exitWithError(Message: "Symbolizing vtables from data access profiles is not "
239 "yet supported for context-sensitive perf traces or "
240 "when pseudo-probe based mapping is enabled. ");
241 }
242 // Parse the data access perf traces into <ip, data-addr> pairs,
243 // symbolize the data-addr to data-symbol. If the data-addr is a vtable,
244 // increment counters for the <ip, data-symbol> pair.
245 if (Error E = PerfReader->parseDataAccessPerfTraces(
246 DataAccessPerfFile: DataAccessProfileFilename, PIDFilter)) {
247 handleAllErrors(E: std::move(E), Handlers: [&](const StringError &SE) {
248 exitWithError(Message: SE.getMessage());
249 });
250 }
251 }
252 Counters = &PerfReader->getSampleCounters();
253 ProfileIsCS = PerfReader->profileIsCS();
254 }
255
256 if (SkipSymbolization)
257 return EXIT_SUCCESS;
258
259 std::unique_ptr<ProfileGeneratorBase> Generator =
260 ProfileGeneratorBase::create(Binary: Binary.get(), Counters, profileIsCS: ProfileIsCS);
261 Generator->generateProfile();
262 Generator->write();
263 }
264
265 return EXIT_SUCCESS;
266}
267