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