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