diff --git a/llvm/test/tools/llvm-profgen/disassemble.s b/llvm/test/tools/llvm-profgen/disassemble.s --- a/llvm/test/tools/llvm-profgen/disassemble.s +++ b/llvm/test/tools/llvm-profgen/disassemble.s @@ -1,6 +1,7 @@ # REQUIRES: x86-registered-target # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t -# RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 -show-disassembly -x86-asm-syntax=intel | FileCheck %s --match-full-lines +# RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 -show-disassembly-only -x86-asm-syntax=intel +# RUN: FileCheck %s --input-file %t1 --match-full-lines # CHECK: Disassembly of section .text [0x0, 0x66]: # CHECK: : diff --git a/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test b/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test --- a/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test +++ b/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test @@ -1,4 +1,5 @@ -; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-pseudo-probe --show-disassembly | FileCheck %s +; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-pseudo-probe --show-disassembly-only +; RUN: FileCheck %s --input-file %t PERF_RECORD_MMAP2 2854748/2854748: [0x400000(0x1000) @ 0 00:1d 123291722 526021]: r-xp /home/inline-cs-pseudoprobe.perfbin diff --git a/llvm/test/tools/llvm-profgen/symbolize.ll b/llvm/test/tools/llvm-profgen/symbolize.ll --- a/llvm/test/tools/llvm-profgen/symbolize.ll +++ b/llvm/test/tools/llvm-profgen/symbolize.ll @@ -1,6 +1,7 @@ ; REQUIRES: x86-registered-target ; RUN: llc -filetype=obj %s -o %t -; RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 --show-disassembly -x86-asm-syntax=intel --show-source-locations | FileCheck %s --match-full-lines +; RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 --show-disassembly-only -x86-asm-syntax=intel --show-source-locations +; RUN: FileCheck %s --input-file %t1 --match-full-lines ; CHECK: Disassembly of section .text [0x0, 0x4a]: ; CHECK: : diff --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h --- a/llvm/tools/llvm-profgen/PerfReader.h +++ b/llvm/tools/llvm-profgen/PerfReader.h @@ -59,6 +59,7 @@ // The type of perfscript enum PerfScriptType { + PERF_UNKNOWN = -1, PERF_INVILID = 0, PERF_LBR = 1, // Only LBR sample PERF_LBR_STACK = 2, // Hybrid sample including call stack and LBR stack. @@ -502,19 +503,52 @@ class PerfReader { public: - PerfReader(cl::list &BinaryFilenames); - - // Hybrid sample(call stack + LBRs) profile traces are seprated by double line - // break, search for that within the first 4k charactors to avoid going - // through the whole file. - static bool isHybridPerfScript(StringRef FileName) { - auto BufOrError = MemoryBuffer::getFileOrSTDIN(FileName, 4000); - if (!BufOrError) - exitWithError(BufOrError.getError(), FileName); - auto Buffer = std::move(BufOrError.get()); - if (Buffer->getBuffer().find("\n\n") == StringRef::npos) + PerfReader(cl::list &BinaryFilenames, + cl::list &PerfTraceFilenames); + + // A LBR sample is like: + // 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ... + // A heuristic for fast detection by checking whether a + // leading " 0x" and the '/' exist. + static bool isLBRSample(StringRef Line) { + if (!Line.startswith(" 0x")) return false; - return true; + if (Line.find('/') != StringRef::npos) + return true; + return false; + } + + // The raw hybird sample is like + // e.g. + // 4005dc # call stack leaf + // 400634 + // 400684 # call stack root + // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... + // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries + // Determine the perfscript contains hybrid samples(call stack + LBRs) by + // checking whether there is a non-empty call stack immediately followed by + // a LBR sample + static PerfScriptType checkPerfScriptType(StringRef FileName) { + TraceStream TraceIt(FileName); + uint64_t FrameAddr = 0; + while (!TraceIt.isAtEoF()) { + int32_t Count = 0; + while (!TraceIt.isAtEoF() && + !TraceIt.getCurrentLine().ltrim().getAsInteger(16, FrameAddr)) { + Count++; + TraceIt.advance(); + } + if (!TraceIt.isAtEoF()) { + if (isLBRSample(TraceIt.getCurrentLine())) { + if (Count > 0) + return PERF_LBR_STACK; + else + return PERF_LBR; + } + TraceIt.advance(); + } + } + return PERF_INVILID; } // The parsed MMap event @@ -538,8 +572,15 @@ const BinarySampleCounterMap &getBinarySampleCounters() const { return BinarySampleCounters; } + static StringRef getOutputFilename(); + static bool ShowDisassemblyOnly() { + return ProfiledBinary::isShowDisassemblyOnlyEnabled(); + } private: + /// Validate the command line input + void validateCommandLine(cl::list &BinaryFilenames, + cl::list &PerfTraceFilenames); /// Parse a single line of a PERF_RECORD_MMAP2 event looking for a /// mapping between the binary name and its memory layout. /// @@ -574,7 +615,7 @@ BinarySampleCounterMap BinarySampleCounters; // Samples with the repeating time generated by the perf reader AggregatedCounter AggregatedSamples; - PerfScriptType PerfType; + PerfScriptType PerfType = PERF_UNKNOWN; }; } // end namespace sampleprof diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp --- a/llvm/tools/llvm-profgen/PerfReader.cpp +++ b/llvm/tools/llvm-profgen/PerfReader.cpp @@ -8,6 +8,11 @@ #include "PerfReader.h" #include "ProfileGenerator.h" +static cl::opt OutputFilename("output", cl::value_desc("output"), + cl::Required, + cl::desc("Output profile file")); +static cl::alias OutputA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); static cl::opt ShowMmapEvents("show-mmap-events", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore, cl::desc("Print binary load events.")); @@ -230,7 +235,45 @@ return true; } -PerfReader::PerfReader(cl::list &BinaryFilenames) { +void PerfReader::validateCommandLine( + cl::list &BinaryFilenames, + cl::list &PerfTraceFilenames) { + // Allow the invalid perfscript if we only use to show binary disassembly + if (!ProfiledBinary::isShowDisassemblyOnlyEnabled()) { + for (auto &File : PerfTraceFilenames) { + if (!llvm::sys::fs::exists(File)) { + std::string Msg = "Input perf script(" + File + ") doesn't exist!"; + exitWithError(Msg); + } + } + } + if (BinaryFilenames.size() > 1) { + // TODO: remove this if everything is ready to support multiple binaries. + exitWithError( + "Currently only support one input binary, multiple binaries' " + "profile will be merged in one profile and make profile " + "summary info inaccurate. Please use `llvm-perfdata` to merge " + "profiles from multiple binaries."); + } + for (auto &Binary : BinaryFilenames) { + if (!llvm::sys::fs::exists(Binary)) { + std::string Msg = "Input binary(" + Binary + ") doesn't exist!"; + exitWithError(Msg); + } + } + if (CSProfileGenerator::MaxCompressionSize < -1) { + exitWithError("Value of --compress-recursion should >= -1"); + } + if (ProfiledBinary::isShowSourceLocationsEnabled() && + !ProfiledBinary::ShowDisassembly()) { + exitWithError("--show-source-locations should work together with " + "--show-disassembly!"); + } +} + +PerfReader::PerfReader(cl::list &BinaryFilenames, + cl::list &PerfTraceFilenames) { + validateCommandLine(BinaryFilenames, PerfTraceFilenames); // Load the binaries. for (auto Filename : BinaryFilenames) loadBinary(Filename, /*AllowNameConflict*/ false); @@ -244,7 +287,8 @@ StringRef BinaryName = llvm::sys::path::filename(BinaryPath); // Call to load the binary in the ctor of ProfiledBinary. - auto Ret = BinaryTable.insert({BinaryName, ProfiledBinary(BinaryPath)}); + auto Ret = BinaryTable.insert( + {BinaryName, ProfiledBinary(BinaryPath, OutputFilename)}); if (!Ret.second && !AllowNameConflict) { std::string ErrorMsg = "Binary name conflict: " + BinaryPath.str() + @@ -591,27 +635,13 @@ void PerfReader::checkAndSetPerfType( cl::list &PerfTraceFilenames) { - bool HasHybridPerf = true; for (auto FileName : PerfTraceFilenames) { - if (!isHybridPerfScript(FileName)) { - HasHybridPerf = false; - break; - } - } - - if (HasHybridPerf) { - PerfType = PERF_LBR_STACK; - } else { - // TODO: Support other type of perf script - PerfType = PERF_INVILID; - } - - if (BinaryTable.size() > 1) { - // TODO: remove this if everything is ready to support multiple binaries. - exitWithError("Currently only support one input binary, multiple binaries' " - "profile will be merged in one profile and make profile " - "summary info inaccurate. Please use `perfdata` to merge " - "profiles from multiple binaries."); + PerfScriptType Type = checkPerfScriptType(FileName); + if (Type == PERF_INVILID) + exitWithError("Invalid perf script input!"); + if (PerfType != PERF_UNKNOWN && PerfType != Type) + exitWithError("Inconsistent sample among different perf scripts"); + PerfType = Type; } } @@ -634,5 +664,7 @@ generateRawProfile(); } +StringRef PerfReader::getOutputFilename() { return OutputFilename; } + } // end namespace sampleprof } // end namespace llvm diff --git a/llvm/tools/llvm-profgen/ProfileGenerator.cpp b/llvm/tools/llvm-profgen/ProfileGenerator.cpp --- a/llvm/tools/llvm-profgen/ProfileGenerator.cpp +++ b/llvm/tools/llvm-profgen/ProfileGenerator.cpp @@ -8,10 +8,6 @@ #include "ProfileGenerator.h" -static cl::opt OutputFilename("output", cl::value_desc("output"), - cl::Required, - cl::desc("Output profile file")); - static cl::opt OutputFormat( "format", cl::desc("Format of output profile"), cl::init(SPF_Text), cl::values( @@ -82,6 +78,7 @@ } void ProfileGenerator::write() { + StringRef OutputFilename = PerfReader::getOutputFilename(); auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); if (std::error_code EC = WriterOrErr.getError()) exitWithError(EC, OutputFilename); diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h --- a/llvm/tools/llvm-profgen/ProfiledBinary.h +++ b/llvm/tools/llvm-profgen/ProfiledBinary.h @@ -130,6 +130,12 @@ // The symbolizer used to get inline context for an instruction. std::unique_ptr Symbolizer; + // Disassembly output file name stream + StringRef OutputFilename; + + // Disassembly output stream + std::unique_ptr AsmOStream; + // Pseudo probe decoder PseudoProbeDecoder ProbeDecoder; @@ -169,7 +175,8 @@ } public: - ProfiledBinary(StringRef Path) : Path(Path), ProEpilogTracker(this) { + ProfiledBinary(StringRef Path, StringRef FileName) + : Path(Path), ProEpilogTracker(this), OutputFilename(FileName) { setupSymbolizer(); load(); } @@ -258,6 +265,10 @@ const PseudoProbeFuncDesc *getInlinerDescForProbe(const PseudoProbe *Probe) { return ProbeDecoder.getInlinerDescForProbe(Probe); } + static bool ShowDisassembly(); + static bool isShowDisassemblyOnlyEnabled(); + static bool isShowSourceLocationsEnabled(); + static bool isShowPseudoProbeEnabled(); }; } // end namespace sampleprof diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp --- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp +++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp @@ -18,12 +18,22 @@ #define DEBUG_TYPE "load-binary" +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + using namespace llvm; using namespace sampleprof; -static cl::opt ShowDisassembly("show-disassembly", cl::ReallyHidden, - cl::init(false), cl::ZeroOrMore, - cl::desc("Print disassembled code.")); +static cl::opt ShowDisAsm("show-disassembly", cl::ReallyHidden, + cl::init(false), cl::ZeroOrMore, + cl::desc("Print disassembled code.")); + +static cl::opt + ShowDisassemblyOnly("show-disassembly-only", cl::ReallyHidden, + cl::init(false), cl::ZeroOrMore, + cl::desc("Only print disassembled code to the file " + "specified with the --output")); static cl::opt ShowSourceLocations("show-source-locations", cl::ReallyHidden, cl::init(false), @@ -81,6 +91,16 @@ } void ProfiledBinary::load() { + if (ShowDisassemblyOnly) { + std::error_code EC; + AsmOStream.reset( + new raw_fd_ostream(OutputFilename, EC, llvm::sys::fs::OF_Text)); + if (EC) + exitWithError(EC); + } else { + // output to stdout + AsmOStream.reset(new raw_fd_ostream(STDOUT_FILENO, true)); + } // Attempt to open the binary. OwningBinary OBinary = unwrapOrError(createBinary(Path), Path); Binary &Binary = *OBinary.getBinary(); @@ -192,13 +212,13 @@ } if (ShowPseudoProbe) - ProbeDecoder.printGUID2FuncDescMap(outs()); + ProbeDecoder.printGUID2FuncDescMap(*AsmOStream); } bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef Bytes, SectionSymbolsTy &Symbols, const SectionRef &Section) { - + auto &OS = *AsmOStream; std::size_t SE = Symbols.size(); uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress; uint64_t SectSize = Section.getSize(); @@ -210,8 +230,8 @@ return true; std::string &&SymbolName = Symbols[SI].Name.str(); - if (ShowDisassembly) - outs() << '<' << SymbolName << ">:\n"; + if (ShowDisassembly()) + OS << '<' << SymbolName << ">:\n"; uint64_t Offset = StartOffset; while (Offset < EndOffset) { @@ -222,22 +242,21 @@ Offset + PreferredBaseAddress, nulls())) return false; - if (ShowDisassembly) { + if (ShowDisassembly()) { if (ShowPseudoProbe) { - ProbeDecoder.printProbeForAddress(outs(), - Offset + PreferredBaseAddress); + ProbeDecoder.printProbeForAddress(OS, Offset + PreferredBaseAddress); } - outs() << format("%8" PRIx64 ":", Offset); - size_t Start = outs().tell(); - IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs()); + OS << format("%8" PRIx64 ":", Offset); + size_t Start = OS.tell(); + IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), OS); if (ShowSourceLocations) { - unsigned Cur = outs().tell() - Start; + unsigned Cur = OS.tell() - Start; if (Cur < 40) - outs().indent(40 - Cur); + OS.indent(40 - Cur); InstructionPointer Inst(this, Offset); - outs() << getReversedLocWithContext(symbolize(Inst)); + OS << getReversedLocWithContext(symbolize(Inst)); } - outs() << "\n"; + OS << "\n"; } const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode()); @@ -256,8 +275,8 @@ Offset += Size; } - if (ShowDisassembly) - outs() << "\n"; + if (ShowDisassembly()) + OS << "\n"; FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str(); return true; @@ -303,6 +322,7 @@ } void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) { + auto &OS = *AsmOStream; // Set up disassembler and related components. setUpDisassembler(Obj); @@ -322,8 +342,8 @@ for (std::pair &SecSyms : AllSymbols) stable_sort(SecSyms.second); - if (ShowDisassembly) - outs() << "\nDisassembly of " << FileName << ":\n"; + if (ShowDisassembly()) + OS << "\nDisassembly of " << FileName << ":\n"; // Dissassemble a text section. for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end(); @@ -341,11 +361,11 @@ // Register the text section. TextSections.insert({SectionOffset, SectSize}); - if (ShowDisassembly) { + if (ShowDisassembly()) { StringRef SectionName = unwrapOrError(Section.getName(), FileName); - outs() << "\nDisassembly of section " << SectionName; - outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", " - << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n"; + OS << "\nDisassembly of section " << SectionName; + OS << " [" << format("0x%" PRIx64, SectionOffset) << ", " + << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n"; } // Get the section data. @@ -401,6 +421,20 @@ return CallStack; } +bool ProfiledBinary::ShowDisassembly() { + return ShowDisAsm || ShowDisassemblyOnly; +} + +bool ProfiledBinary::isShowDisassemblyOnlyEnabled() { + return ShowDisassemblyOnly; +} + +bool ProfiledBinary::isShowSourceLocationsEnabled() { + return ShowSourceLocations; +} + +bool ProfiledBinary::isShowPseudoProbeEnabled() { return ShowPseudoProbe; } + InstructionPointer::InstructionPointer(ProfiledBinary *Binary, uint64_t Address, bool RoundToNext) : Binary(Binary), Address(Address) { diff --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp --- a/llvm/tools/llvm-profgen/llvm-profgen.cpp +++ b/llvm/tools/llvm-profgen/llvm-profgen.cpp @@ -43,7 +43,9 @@ cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n"); // Load binaries and parse perf events and samples - PerfReader Reader(BinaryFilenames); + PerfReader Reader(BinaryFilenames, PerfTraceFilenames); + if (Reader.ShowDisassemblyOnly()) + return EXIT_SUCCESS; Reader.parsePerfTraces(PerfTraceFilenames); std::unique_ptr Generator = ProfileGenerator::create(