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