//===- llvm-profgen.cpp - LLVM SPGO profile generation tool -----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // llvm-profgen generates SPGO profiles from perf script ouput. // //===----------------------------------------------------------------------===// #include "ErrorHandling.h" #include "Options.h" #include "PerfReader.h" #include "ProfileGenerator.h" #include "ProfiledBinary.h" #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/VirtualFileSystem.h" using namespace llvm; using namespace sampleprof; namespace llvm { cl::OptionCategory ProfGenCategory("ProfGen Options"); static cl::opt PerfScriptFilename( "perfscript", cl::value_desc("perfscript"), cl::desc("Path of perf-script trace created by Linux perf tool with " "`script` command(the raw perf.data should be profiled with -b). " "Cannot be used with --perfdata, --unsymbolized-profile, or " "--llvm-sample-profile."), cl::cat(ProfGenCategory)); static cl::alias PSA("ps", cl::desc("Alias for --perfscript"), cl::aliasopt(PerfScriptFilename)); static cl::opt PerfDataFilename( "perfdata", cl::value_desc("perfdata"), cl::desc("Path of raw perf data created by Linux perf tool (it should be " "profiled with -b). Cannot be used with --perfscript, " "--unsymbolized-profile, or --llvm-sample-profile."), cl::cat(ProfGenCategory)); static cl::alias PDA("pd", cl::desc("Alias for --perfdata"), cl::aliasopt(PerfDataFilename)); static cl::opt UnsymbolizedProfFilename( "unsymbolized-profile", cl::value_desc("unsymbolized profile"), cl::desc("Path of the unsymbolized profile created by " "`llvm-profgen` with `--skip-symbolization`. " "Cannot be used with --perfscript, --perfdata, or " "--llvm-sample-profile."), cl::cat(ProfGenCategory)); static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"), cl::aliasopt(UnsymbolizedProfFilename)); static cl::opt SampleProfFilename( "llvm-sample-profile", cl::value_desc("llvm sample profile"), cl::desc("Path of the LLVM sample profile. Cannot be used with" "--perfscript, --perfdata, or --unsymbolized-profile"), cl::cat(ProfGenCategory)); static cl::opt BinaryPath("binary", cl::value_desc("binary"), cl::Required, cl::desc("Path of profiled executable binary."), cl::cat(ProfGenCategory)); static cl::opt ProcessId("pid", cl::value_desc("process Id"), cl::init(0), cl::desc("Process Id for the profiled executable binary."), cl::cat(ProfGenCategory)); static cl::opt DebugBinPath( "debug-binary", cl::value_desc("debug-binary"), cl::desc("Path of debug info binary, llvm-profgen will load the DWARF info " "from it instead of the executable binary."), cl::cat(ProfGenCategory)); static cl::opt DataAccessProfileFilename( "data-access-perftrace", cl::value_desc("data-access-perftrace"), cl::desc("File path of a Linux perf raw trace (generated by `perf report " "-D`) consisting of memory access events."), cl::cat(ProfGenCategory)); static cl::opt ETMPath("etm", cl::value_desc("etm"), cl::desc("Path of raw ETM trace file"), cl::cat(ProfGenCategory)); static cl::opt ETMTraceID( "etm-trace-id", cl::init(0x10), cl::desc("CoreSight Trace ID (CSID) used to route ETM trace data."), cl::cat(ProfGenCategory)); static cl::opt TargetTriple("target-triple", cl::value_desc("triple"), cl::desc("Override the target triple for the binary"), cl::cat(ProfGenCategory)); // Validate the command line input. static void validateCommandLine() { // Allow the missing perfscript if we only use to show binary disassembly. if (!ShowDisassemblyOnly) { // Validate input profile is provided only once bool HasPerfData = PerfDataFilename.getNumOccurrences() > 0; bool HasPerfScript = PerfScriptFilename.getNumOccurrences() > 0; bool HasUnsymbolizedProfile = UnsymbolizedProfFilename.getNumOccurrences() > 0; bool HasSampleProfile = SampleProfFilename.getNumOccurrences() > 0; bool HasEtm = ETMPath.getNumOccurrences() > 0; uint16_t S = HasPerfData + HasPerfScript + HasUnsymbolizedProfile + HasSampleProfile + HasEtm; if (S != 1) { std::string Msg = S > 1 ? "Only one of `--perfscript`, `--perfdata`, " "`--unsymbolized-profile`, " "`--sample-profile` or `--etm` can be used." : "Perf input file is missing. Please provide one of " "`--perfscript`, " "`--perfdata`, `--unsymbolized-profile`, `--sample-profile`, " "`--etm`."; exitWithError(Msg); } auto CheckFileExists = [](bool H, StringRef File) { if (H && !llvm::sys::fs::exists(File)) { std::string Msg = "Input perf file(" + File.str() + ") doesn't exist."; exitWithError(Msg); } }; CheckFileExists(HasPerfData, PerfDataFilename); CheckFileExists(HasPerfScript, PerfScriptFilename); CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename); CheckFileExists(HasSampleProfile, SampleProfFilename); CheckFileExists(HasEtm, ETMPath); } if (!llvm::sys::fs::exists(BinaryPath)) { std::string Msg = "Input binary(" + BinaryPath + ") doesn't exist."; exitWithError(Msg); } if (CSProfileGenerator::MaxCompressionSize < -1) { exitWithError("Value of --compress-recursion should >= -1"); } if (ShowSourceLocations && !ShowDisassemblyOnly) { exitWithError("--show-source-locations should work together with " "--show-disassembly-only!"); } } static InputFile getInputFile() { InputFile File; if (PerfDataFilename.getNumOccurrences()) { File.InputFilePath = PerfDataFilename; File.Format = InputFormat::PerfData; } else if (PerfScriptFilename.getNumOccurrences()) { File.InputFilePath = PerfScriptFilename; File.Format = InputFormat::PerfScript; } else if (UnsymbolizedProfFilename.getNumOccurrences()) { File.InputFilePath = UnsymbolizedProfFilename; File.Format = InputFormat::UnsymbolizedProfile; } else if (ETMPath.getNumOccurrences()) { File.InputFilePath = ETMPath; File.Format = InputFormat::ETMFormat; } return File; } } // end namespace llvm int main(int argc, const char *argv[]) { InitLLVM X(argc, argv); // Initialize targets and assembly printers/parsers. InitializeAllTargetInfos(); InitializeAllTargetMCs(); InitializeAllDisassemblers(); cl::HideUnrelatedOptions({&ProfGenCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n"); validateCommandLine(); // Load symbols and disassemble the code of a given binary. std::unique_ptr Binary = std::make_unique(BinaryPath, DebugBinPath); Binary->load(TargetTriple); if (ShowDisassemblyOnly) return EXIT_SUCCESS; if (SampleProfFilename.getNumOccurrences()) { LLVMContext Context; auto FS = vfs::getRealFileSystem(); auto ReaderOrErr = SampleProfileReader::create(SampleProfFilename, Context, *FS); if (std::error_code EC = ReaderOrErr.getError()) exitWithError(EC, SampleProfFilename); std::unique_ptr Reader = std::move(ReaderOrErr.get()); Reader->read(); std::unique_ptr Generator = ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(), Reader->profileIsCS()); Generator->generateProfile(); Generator->write(); } else { std::optional PIDFilter; if (ProcessId.getNumOccurrences()) PIDFilter = ProcessId; InputFile File = getInputFile(); const ContextSampleCounterMap *Counters = nullptr; bool ProfileIsCS = false; std::unique_ptr EtmReader; std::unique_ptr PerfReader; if (File.Format == InputFormat::ETMFormat) { EtmReader = std::make_unique(Binary.get(), File.InputFilePath, static_cast(ETMTraceID)); EtmReader->parseETMTraces(); Counters = &EtmReader->getSampleCounters(); } else { PerfReader = PerfReaderBase::create(Binary.get(), File, PIDFilter); // Parse perf events and samples PerfReader->parsePerfTraces(); if (!DataAccessProfileFilename.empty()) { if (PerfReader->profileIsCS() || Binary->usePseudoProbes()) { exitWithError("Symbolizing vtables from data access profiles is not " "yet supported for context-sensitive perf traces or " "when pseudo-probe based mapping is enabled. "); } // Parse the data access perf traces into pairs, // symbolize the data-addr to data-symbol. If the data-addr is a vtable, // increment counters for the pair. if (Error E = PerfReader->parseDataAccessPerfTraces( DataAccessProfileFilename, PIDFilter)) { handleAllErrors(std::move(E), [&](const StringError &SE) { exitWithError(SE.getMessage()); }); } } Counters = &PerfReader->getSampleCounters(); ProfileIsCS = PerfReader->profileIsCS(); } if (SkipSymbolization) return EXIT_SUCCESS; std::unique_ptr Generator = ProfileGeneratorBase::create(Binary.get(), Counters, ProfileIsCS); Generator->generateProfile(); Generator->write(); } return EXIT_SUCCESS; }