218 lines · cpp
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-exception6//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(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 once87 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 > 197 ? "`--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(Msg);102 }103 104 auto CheckFileExists = [](bool H, StringRef File) {105 if (H && !llvm::sys::fs::exists(File)) {106 std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";107 exitWithError(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(BinaryPath)) {118 std::string Msg = "Input binary(" + BinaryPath + ") doesn't exist.";119 exitWithError(Msg);120 }121 122 if (CSProfileGenerator::MaxCompressionSize < -1) {123 exitWithError("Value of --compress-recursion should >= -1");124 }125 if (ShowSourceLocations && !ShowDisassemblyOnly) {126 exitWithError("--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 llvm147 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({&ProfGenCategory, &getColorCategory()});157 cl::ParseCommandLineOptions(argc, argv, "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>(BinaryPath, 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(SampleProfFilename, Context, *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.get(), Reader->getProfiles(),176 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.get(), PerfFile, PIDFilter);186 // Parse perf events and samples187 Reader->parsePerfTraces();188 189 if (!DataAccessProfileFilename.empty()) {190 if (Reader->profileIsCS() || Binary->usePseudoProbes()) {191 exitWithError("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, symbolize196 // the data-addr to data-symbol. If the data-addr is a vtable, increment197 // counters for the <ip, data-symbol> pair.198 if (Error E = Reader->parseDataAccessPerfTraces(DataAccessProfileFilename,199 PIDFilter)) {200 handleAllErrors(std::move(E), [&](const StringError &SE) {201 exitWithError(SE.getMessage());202 });203 }204 }205 206 if (SkipSymbolization)207 return EXIT_SUCCESS;208 209 std::unique_ptr<ProfileGeneratorBase> Generator =210 ProfileGeneratorBase::create(Binary.get(), &Reader->getSampleCounters(),211 Reader->profileIsCS());212 Generator->generateProfile();213 Generator->write();214 }215 216 return EXIT_SUCCESS;217}218