| //===- 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<std::string> PerfScriptFilename( | |
| "perfscript", cl::value_desc("perfscript"), | |
| cl::desc("Path of a trace created by the Linux `perf script` command. For " | |
| "LBR or BRBE input, the raw perf data must contain branch " | |
| "stacks, for example from recording 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<std::string> PerfDataFilename( | |
| "perfdata", cl::value_desc("perfdata"), | |
| cl::desc("Path of raw perf data created by the Linux perf tool. For LBR or " | |
| "BRBE input, it must contain branch stacks, for example from " | |
| "recording 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<std::string> 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<std::string> 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<std::string> | |
| BinaryPath("binary", cl::value_desc("binary"), cl::Required, | |
| cl::desc("Path of profiled executable binary."), | |
| cl::cat(ProfGenCategory)); | |
| static cl::opt<uint32_t> | |
| 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<std::string> 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<std::string> 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<std::string> ETMPath("etm", cl::value_desc("etm"), | |
| cl::desc("Path of raw ETM trace file"), | |
| cl::cat(ProfGenCategory)); | |
| static cl::opt<unsigned> 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<std::string> | |
| 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<ProfiledBinary> Binary = | |
| std::make_unique<ProfiledBinary>(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<sampleprof::SampleProfileReader> Reader = | |
| std::move(ReaderOrErr.get()); | |
| Reader->read(); | |
| std::unique_ptr<ProfileGeneratorBase> Generator = | |
| ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(), | |
| Reader->profileIsCS()); | |
| Generator->generateProfile(); | |
| Generator->write(); | |
| } else { | |
| std::optional<uint32_t> PIDFilter; | |
| if (ProcessId.getNumOccurrences()) | |
| PIDFilter = ProcessId; | |
| InputFile File = getInputFile(); | |
| const ContextSampleCounterMap *Counters = nullptr; | |
| bool ProfileIsCS = false; | |
| std::unique_ptr<ETMReader> EtmReader; | |
| std::unique_ptr<PerfReaderBase> PerfReader; | |
| if (File.Format == InputFormat::ETMFormat) { | |
| EtmReader = std::make_unique<ETMReader>(Binary.get(), File.InputFilePath, | |
| static_cast<uint8_t>(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 <ip, data-addr> pairs, | |
| // symbolize the data-addr to data-symbol. If the data-addr is a vtable, | |
| // increment counters for the <ip, data-symbol> 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<ProfileGeneratorBase> Generator = | |
| ProfileGeneratorBase::create(Binary.get(), Counters, ProfileIsCS); | |
| Generator->generateProfile(); | |
| Generator->write(); | |
| } | |
| return EXIT_SUCCESS; | |
| } |