From: wlei Date: Wed, 10 Feb 2021 00:41:44 +0000 (-0800) Subject: [CSSPGO][llvm-profgen] Renovate perfscript check and command line input validation X-Git-Tag: llvmorg-14-init~15203 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=426e326a19fe7888e1975e94cd6b1cd8233e5268;p=platform%2Fupstream%2Fllvm.git [CSSPGO][llvm-profgen] Renovate perfscript check and command line input validation This include some changes related with PerfReader's the input check and command line change: 1) It appears there might be thousands of leading MMAP-Event line in the perfscript for large workload. For this case, the 4k threshold is not eligible to determine it's a hybrid sample. This change renovated the `isHybridPerfScript` by going through the script without threshold limitation checking whether there is a non-empty call stack immediately followed by a LBR sample. It will stop once it find a valid one. 2) Added several input validations for the command line switches in PerfReader. 3) Changed the command line `show-disassembly` to `show-disassembly-only`, it will print to stdout and exit early which leave an empty output profile. Reviewed By: hoy, wenlei Differential Revision: https://reviews.llvm.org/D96387 --- diff --git a/llvm/test/tools/llvm-profgen/disassemble.s b/llvm/test/tools/llvm-profgen/disassemble.s index fc85fbe..be03b5a 100644 --- a/llvm/test/tools/llvm-profgen/disassemble.s +++ b/llvm/test/tools/llvm-profgen/disassemble.s @@ -1,6 +1,6 @@ # 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 | FileCheck %s --match-full-lines # CHECK: Disassembly of section .text [0x0, 0x66]: # CHECK: : diff --git a/llvm/test/tools/llvm-profgen/invalid-perfscript.test b/llvm/test/tools/llvm-profgen/invalid-perfscript.test new file mode 100644 index 0000000..d795f85 --- /dev/null +++ b/llvm/test/tools/llvm-profgen/invalid-perfscript.test @@ -0,0 +1,9 @@ +; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t 2>%t1 +; RUN: FileCheck %s --input-file %t1 + + 4005dc + 400634 + 400684 + 7f68c5788793 + +; XFAIL: * diff --git a/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test b/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test index 5feaa97..1d93a06 100644 --- a/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test +++ b/llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test @@ -1,4 +1,4 @@ -; 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 | FileCheck %s 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 index 2fbc59e..9a436de 100644 --- a/llvm/test/tools/llvm-profgen/symbolize.ll +++ b/llvm/test/tools/llvm-profgen/symbolize.ll @@ -1,6 +1,6 @@ ; 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 | FileCheck %s --match-full-lines ; CHECK: Disassembly of section .text [0x0, 0x4a]: ; CHECK: : diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp index e59d8d9..2e0b71f 100644 --- a/llvm/tools/llvm-profgen/PerfReader.cpp +++ b/llvm/tools/llvm-profgen/PerfReader.cpp @@ -17,6 +17,9 @@ static cl::opt ShowUnwinderOutput("show-unwinder-output", cl::ZeroOrMore, cl::desc("Print unwinder output")); +extern cl::opt ShowDisassemblyOnly; +extern cl::opt ShowSourceLocations; + namespace llvm { namespace sampleprof { @@ -230,7 +233,44 @@ bool VirtualUnwinder::unwind(const HybridSample *Sample, uint64_t Repeat) { 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 (!ShowDisassemblyOnly) { + 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 (ShowSourceLocations && !ShowDisassemblyOnly) { + exitWithError("--show-source-locations should work together with " + "--show-disassembly-only!"); + } +} + +PerfReader::PerfReader(cl::list &BinaryFilenames, + cl::list &PerfTraceFilenames) { + validateCommandLine(BinaryFilenames, PerfTraceFilenames); // Load the binaries. for (auto Filename : BinaryFilenames) loadBinary(Filename, /*AllowNameConflict*/ false); @@ -591,27 +631,13 @@ void PerfReader::parseAndAggregateTrace(StringRef Filename) { 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_INVALID) + exitWithError("Invalid perf script input!"); + if (PerfType != PERF_UNKNOWN && PerfType != Type) + exitWithError("Inconsistent sample among different perf scripts"); + PerfType = Type; } } diff --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h index 7eaa4b8..b802c21 100644 --- a/llvm/tools/llvm-profgen/PerfReader.h +++ b/llvm/tools/llvm-profgen/PerfReader.h @@ -59,9 +59,10 @@ public: // The type of perfscript enum PerfScriptType { - PERF_INVILID = 0, - PERF_LBR = 1, // Only LBR sample - PERF_LBR_STACK = 2, // Hybrid sample including call stack and LBR stack. + PERF_UNKNOWN = 0, + PERF_INVALID = 1, + PERF_LBR = 2, // Only LBR sample + PERF_LBR_STACK = 3, // Hybrid sample including call stack and LBR stack. }; // The parsed LBR sample entry. @@ -502,19 +503,52 @@ using BinarySampleCounterMap = 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_INVALID; } // The parsed MMap event @@ -540,6 +574,9 @@ public: } 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 +611,7 @@ private: 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/ProfileGenerator.cpp b/llvm/tools/llvm-profgen/ProfileGenerator.cpp index 0a7dddc..553ea71 100644 --- a/llvm/tools/llvm-profgen/ProfileGenerator.cpp +++ b/llvm/tools/llvm-profgen/ProfileGenerator.cpp @@ -11,6 +11,8 @@ 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 OutputFormat( "format", cl::desc("Format of output profile"), cl::init(SPF_Text), diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp index e1549b14..d7588d6 100644 --- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp +++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp @@ -22,16 +22,15 @@ using namespace llvm; using namespace sampleprof; -static cl::opt ShowDisassembly("show-disassembly", cl::ReallyHidden, - cl::init(false), cl::ZeroOrMore, - cl::desc("Print disassembled code.")); +cl::opt ShowDisassemblyOnly("show-disassembly-only", cl::ReallyHidden, + cl::init(false), cl::ZeroOrMore, + cl::desc("Print disassembled code.")); -static cl::opt ShowSourceLocations("show-source-locations", - cl::ReallyHidden, cl::init(false), - cl::ZeroOrMore, - cl::desc("Print source locations.")); +cl::opt ShowSourceLocations("show-source-locations", cl::ReallyHidden, + cl::init(false), cl::ZeroOrMore, + cl::desc("Print source locations.")); -static cl::opt ShowPseudoProbe( +cl::opt ShowPseudoProbe( "show-pseudo-probe", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore, cl::desc("Print pseudo probe section and disassembled info.")); @@ -199,7 +198,6 @@ void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) { bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef Bytes, SectionSymbolsTy &Symbols, const SectionRef &Section) { - std::size_t SE = Symbols.size(); uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress; uint64_t SectSize = Section.getSize(); @@ -211,7 +209,7 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef Bytes, return true; std::string &&SymbolName = Symbols[SI].Name.str(); - if (ShowDisassembly) + if (ShowDisassemblyOnly) outs() << '<' << SymbolName << ">:\n"; uint64_t Offset = StartOffset; @@ -223,7 +221,7 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef Bytes, Offset + PreferredBaseAddress, nulls())) return false; - if (ShowDisassembly) { + if (ShowDisassemblyOnly) { if (ShowPseudoProbe) { ProbeDecoder.printProbeForAddress(outs(), Offset + PreferredBaseAddress); @@ -257,7 +255,7 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef Bytes, Offset += Size; } - if (ShowDisassembly) + if (ShowDisassemblyOnly) outs() << "\n"; FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str(); @@ -323,7 +321,7 @@ void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) { for (std::pair &SecSyms : AllSymbols) stable_sort(SecSyms.second); - if (ShowDisassembly) + if (ShowDisassemblyOnly) outs() << "\nDisassembly of " << FileName << ":\n"; // Dissassemble a text section. @@ -342,7 +340,7 @@ void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) { // Register the text section. TextSections.insert({SectionOffset, SectSize}); - if (ShowDisassembly) { + if (ShowDisassemblyOnly) { StringRef SectionName = unwrapOrError(Section.getName(), FileName); outs() << "\nDisassembly of section " << SectionName; outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", " diff --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp index 0f4d8f0..081f1bb 100644 --- a/llvm/tools/llvm-profgen/llvm-profgen.cpp +++ b/llvm/tools/llvm-profgen/llvm-profgen.cpp @@ -29,6 +29,8 @@ static cl::list llvm::cl::MiscFlags::CommaSeparated, cl::desc("Path of profiled binary files")); +extern cl::opt ShowDisassemblyOnly; + using namespace llvm; using namespace sampleprof; @@ -43,7 +45,9 @@ int main(int argc, const char *argv[]) { 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 (ShowDisassemblyOnly) + return EXIT_SUCCESS; Reader.parsePerfTraces(PerfTraceFilenames); std::unique_ptr Generator = ProfileGenerator::create(