diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h --- a/llvm/include/llvm/ProfileData/SampleProf.h +++ b/llvm/include/llvm/ProfileData/SampleProf.h @@ -495,28 +495,32 @@ State = UnknownContext; Name = ContextStr; } else { - // Remove encapsulating '[' and ']' if any - ContextStr = ContextStr.substr(1, ContextStr.size() - 2); CSNameTable.emplace_back(); SampleContextFrameVector &Context = CSNameTable.back(); - /// Create a context vector from a given context string and save it in - /// `Context`. - StringRef ContextRemain = ContextStr; - StringRef ChildContext; - StringRef CalleeName; - while (!ContextRemain.empty()) { - auto ContextSplit = ContextRemain.split(" @ "); - ChildContext = ContextSplit.first; - ContextRemain = ContextSplit.second; - LineLocation CallSiteLoc(0, 0); - decodeContextString(ChildContext, CalleeName, CallSiteLoc); - Context.emplace_back(CalleeName, CallSiteLoc); - } - + createCtxVectorFromStr(ContextStr, Context); setContext(Context, CState); } } + /// Create a context vector from a given context string and save it in + /// `Context`. + static void createCtxVectorFromStr(StringRef ContextStr, + SampleContextFrameVector &Context) { + // Remove encapsulating '[' and ']' if any + ContextStr = ContextStr.substr(1, ContextStr.size() - 2); + StringRef ContextRemain = ContextStr; + StringRef ChildContext; + StringRef CalleeName; + while (!ContextRemain.empty()) { + auto ContextSplit = ContextRemain.split(" @ "); + ChildContext = ContextSplit.first; + ContextRemain = ContextSplit.second; + LineLocation CallSiteLoc(0, 0); + decodeContextString(ChildContext, CalleeName, CallSiteLoc); + Context.emplace_back(CalleeName, CallSiteLoc); + } + } + // Promote context by removing top frames with the length of // `ContextFramesToRemove`. Note that with array representation of context, // the promotion is effectively a slice operation with first diff --git a/llvm/test/tools/llvm-profgen/inline-noprobe.test b/llvm/test/tools/llvm-profgen/inline-noprobe.test --- a/llvm/test/tools/llvm-profgen/inline-noprobe.test +++ b/llvm/test/tools/llvm-profgen/inline-noprobe.test @@ -2,8 +2,8 @@ ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-RAW-PROFILE ; RUN: llvm-profgen --format=text --use-dwarf-correlation --perfscript=%S/Inputs/inline-noprobe.perfscript --binary=%S/Inputs/inline-noprobe.perfbin --output=%t --skip-symbolization ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-RAW-PROFILE -; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-noprobe.perfscript --binary=%S/Inputs/inline-noprobe.perfbin --output=%t -; RUN: FileCheck %s --input-file %t --check-prefix=CHECK +; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/inline-noprobe.perfbin --output=%t1 +; RUN: FileCheck %s --input-file %t1 --check-prefix=CHECK ; RUN: llvm-profgen --format=text --use-dwarf-correlation --perfscript=%S/Inputs/inline-noprobe.perfscript --binary=%S/Inputs/inline-noprobe.perfbin --output=%t ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK diff --git a/llvm/test/tools/llvm-profgen/inline-noprobe2.test b/llvm/test/tools/llvm-profgen/inline-noprobe2.test --- a/llvm/test/tools/llvm-profgen/inline-noprobe2.test +++ b/llvm/test/tools/llvm-profgen/inline-noprobe2.test @@ -2,8 +2,8 @@ ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-ARTIFICIAL-BRANCH ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-noprobe2.perfscript --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t --skip-symbolization --use-offset=0 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-RAW-PROFILE -; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-noprobe2.perfscript --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t -; RUN: FileCheck %s --input-file %t --check-prefix=CHECK +; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t1 --use-offset=0 +; RUN: FileCheck %s --input-file %t1 --check-prefix=CHECK ; RUN: llvm-profgen --format=extbinary --perfscript=%S/Inputs/inline-noprobe2.perfscript --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t --populate-profile-symbol-list=1 ; RUN: llvm-profdata show -show-prof-sym-list -sample %t | FileCheck %s --check-prefix=CHECK-SYM-LIST diff --git a/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test b/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test --- a/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test +++ b/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test @@ -2,8 +2,8 @@ ; REQUIRES: x86_64-linux ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --skip-symbolization --profile-summary-cold-count=0 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-UNWINDER -; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 -; RUN: FileCheck %s --input-file %t +; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t1 --profile-summary-cold-count=0 +; RUN: FileCheck %s --input-file %t1 ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --ignore-stack-samples ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-STRIP-CTX ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.aggperfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --skip-symbolization --profile-summary-cold-count=0 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 @@ -58,12 +58,25 @@ } }; -// The type of perfscript -enum PerfScriptType { - 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 type of input format. +enum PerfFormat { + PerfData = 0x01, // Raw linux perf.data. + PerfScript = 0x02, // Perf script create by `perf script` command. + UnsymbolizedProfile = 0x04, // Unsymbolized profile generated by llvm-profgen. + +}; + +// The type of perfscript content. +enum PerfContent { + LBR = 0x01, // Only LBR sample. + LBRStack = 0x02, // Hybrid sample including call stack and LBR stack. + Aggregated = 0x04, // Aggregated perf script. +}; + +struct PerfInputFile { + std::string InputFile; + uint32_t Format = 0; + uint32_t Content = 0; }; // The parsed LBR sample entry. @@ -503,19 +516,17 @@ // Read perf trace to parse the events and samples. class PerfReaderBase { public: - PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace, - PerfScriptType Type = PERF_UNKNOWN) - : Binary(B), PerfTraceFile(PerfTrace), PerfType(Type) { + PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace) + : Binary(B), PerfTraceFile(PerfTrace) { // Initialize the base address to preferred address. Binary->setBaseAddress(Binary->getPreferredBaseAddress()); }; virtual ~PerfReaderBase() = default; - static std::unique_ptr - create(ProfiledBinary *Binary, StringRef PerfInputFile, bool IsPerfData); + static std::unique_ptr create(ProfiledBinary *Binary, + PerfInputFile &PerfInput); - PerfScriptType getPerfScriptType() const { return PerfType; } // Entry of the reader to parse multiple perf traces - void parsePerfTraces(); + virtual void parsePerfTraces(); const ContextSampleCounterMap &getSampleCounters() const { return SampleCounters; } @@ -531,14 +542,14 @@ StringRef BinaryPath; }; // Generate perf script from perf data - static std::string convertPerfDataToTrace(ProfiledBinary *Binary, - StringRef PerfData); + static PerfInputFile convertPerfDataToTrace(ProfiledBinary *Binary, + PerfInputFile &File); // Check whether a given line is LBR sample static bool isLBRSample(StringRef Line); // Check whether a given line is MMAP event static bool isMMap2Event(StringRef Line); // Extract perf script type by peaking at the input - static PerfScriptType checkPerfScriptType(StringRef FileName); + static uint32_t checkPerfScriptType(StringRef FileName); // Parse a single line of a PERF_RECORD_MMAP2 event looking for a // mapping between the binary name and its memory layout. static bool extractMMap2EventForBinary(ProfiledBinary *Binary, StringRef Line, @@ -567,19 +578,18 @@ void parseSample(TraceStream &TraceIt); // An aggregated count is given to indicate how many times the sample is // repeated. - virtual void parseSample(TraceStream &TraceIt, uint64_t Count) = 0; + virtual void parseSample(TraceStream &TraceIt, uint64_t Count){}; // Post process the profile after trace aggregation, we will do simple range // overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample). - virtual void generateRawProfile() = 0; - void writeRawProfile(StringRef Filename); - void writeRawProfile(raw_fd_ostream &OS); + virtual void generateUnsymbolizedProfile(){}; + void writeUnsymbolizedProfile(StringRef Filename); + void writeUnsymbolizedProfile(raw_fd_ostream &OS); ProfiledBinary *Binary = nullptr; StringRef PerfTraceFile; ContextSampleCounterMap SampleCounters; // Samples with the repeating time generated by the perf reader AggregatedCounter AggregatedSamples; - PerfScriptType PerfType = PERF_UNKNOWN; // Keep track of all invalid return addresses std::set InvalidReturnAddresses; @@ -594,12 +604,11 @@ */ class LBRPerfReader : public PerfReaderBase { public: - LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace, - PerfScriptType Type = PERF_LBR) - : PerfReaderBase(Binary, PerfTrace, Type){}; + LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace) + : PerfReaderBase(Binary, PerfTrace){}; // Parse the LBR only sample. virtual void parseSample(TraceStream &TraceIt, uint64_t Count) override; - virtual void generateRawProfile() override; + virtual void generateUnsymbolizedProfile() override; private: void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat); @@ -617,16 +626,46 @@ class HybridPerfReader : public LBRPerfReader { public: HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace) - : LBRPerfReader(Binary, PerfTrace, PERF_LBR_STACK){}; + : LBRPerfReader(Binary, PerfTrace){}; // Parse the hybrid sample including the call and LBR line void parseSample(TraceStream &TraceIt, uint64_t Count) override; - void generateRawProfile() override; + void generateUnsymbolizedProfile() override; private: // Unwind the hybrid samples after aggregration void unwindSamples(); }; +/* + Format of unsymbolized profile: + + [context stack1] + number of entries in RangeCounter + from_1-to_1:count_1 + from_2-to_2:count_2 + ...... + from_n-to_n:count_n + number of entries in BranchCounter + src_1->dst_1:count_1 + src_2->dst_2:count_2 + ...... + src_n->dst_n:count_n + [context stack2] + ...... +*/ +class UnsymbolizedProfileReader : public PerfReaderBase { +public: + UnsymbolizedProfileReader(ProfiledBinary *Binary, StringRef PerfTrace) + : PerfReaderBase(Binary, PerfTrace){}; + void parsePerfTraces() override; + +private: + void readSampleCounters(TraceStream &TraceIt, SampleCounter &SCounters); + void readUnsymbolizedProfile(StringRef Filename); + + std::unordered_set ContextStrSet; +}; + } // end namespace sampleprof } // end namespace llvm 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 @@ -24,7 +24,8 @@ static cl::opt UseOffset("use-offset", cl::init(true), cl::ZeroOrMore, - cl::desc("Work with `--skip-symbolization` to dump the " + cl::desc("Work with `--skip-symbolization` or " + "`--unsymbolized-profile` to write/read the " "offset instead of virtual address.")); static cl::opt IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore, @@ -277,24 +278,28 @@ return true; } -std::unique_ptr PerfReaderBase::create(ProfiledBinary *Binary, - StringRef PerfInputFile, - bool IsPerfData) { - // For perf data input, we need to convert them into perf script first. - if (IsPerfData) { - std::string ConvertedPerfScript = - convertPerfDataToTrace(Binary, PerfInputFile); - // Let commoand opt own the string for converted perf trace file name - PerfTraceFilename = ConvertedPerfScript; - PerfInputFile = PerfTraceFilename; +std::unique_ptr +PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput) { + std::unique_ptr PerfReader; + + if (PerfInput.Format & PerfFormat::UnsymbolizedProfile) { + PerfReader.reset( + new UnsymbolizedProfileReader(Binary, PerfInput.InputFile)); + return PerfReader; } - PerfScriptType PerfType = checkPerfScriptType(PerfInputFile); - std::unique_ptr PerfReader; - if (PerfType == PERF_LBR_STACK) { - PerfReader.reset(new HybridPerfReader(Binary, PerfInputFile)); - } else if (PerfType == PERF_LBR) { - PerfReader.reset(new LBRPerfReader(Binary, PerfInputFile)); + // For perf data input, we need to convert them into perf script first. + if (PerfInput.Format & PerfFormat::PerfData) + PerfInput = convertPerfDataToTrace(Binary, PerfInput); + + assert((PerfInput.Format & PerfFormat::PerfScript) && + "Should be a perfscript!"); + + PerfInput.Content |= checkPerfScriptType(PerfInput.InputFile); + if (PerfInput.Content & PerfContent::LBRStack) { + PerfReader.reset(new HybridPerfReader(Binary, PerfInput.InputFile)); + } else if (PerfInput.Content & PerfContent::LBR) { + PerfReader.reset(new LBRPerfReader(Binary, PerfInput.InputFile)); } else { exitWithError("Unsupported perfscript!"); } @@ -302,8 +307,9 @@ return PerfReader; } -std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary, - StringRef PerfData) { +PerfInputFile PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary, + PerfInputFile &File) { + StringRef PerfData = File.InputFile; // Run perf script to retrieve PIDs matching binary we're interested in. auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf"); if (!PerfExecutable) { @@ -348,7 +354,10 @@ PIDs, "-i", PerfData}; sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, llvm::None, Redirects); - return PerfTraceFile; + PerfInputFile RetFile; + RetFile.InputFile = PerfTraceFile; + RetFile.Format |= PerfFormat::PerfScript; + return RetFile; } void PerfReaderBase::updateBinaryAddress(const MMapEvent &Event) { @@ -663,43 +672,28 @@ } } -void PerfReaderBase::writeRawProfile(StringRef Filename) { +void PerfReaderBase::writeUnsymbolizedProfile(StringRef Filename) { std::error_code EC; raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF); if (EC) exitWithError(EC, Filename); - writeRawProfile(OS); + writeUnsymbolizedProfile(OS); } // Use ordered map to make the output deterministic using OrderedCounterForPrint = std::map; -void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) { - /* - Format: - [context string] - number of entries in RangeCounter - from_1-to_1:count_1 - from_2-to_2:count_2 - ...... - from_n-to_n:count_n - number of entries in BranchCounter - src_1->dst_1:count_1 - src_2->dst_2:count_2 - ...... - src_n->dst_n:count_n - */ - +void PerfReaderBase::writeUnsymbolizedProfile(raw_fd_ostream &OS) { OrderedCounterForPrint OrderedCounters; for (auto &CI : SampleCounters) { OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second; } - auto SCounterPrinter = [&](RangeSample Counter, StringRef Separator, + auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator, uint32_t Indent) { OS.indent(Indent); OS << Counter.size() << "\n"; - for (auto I : Counter) { + for (auto &I : Counter) { uint64_t Start = UseOffset ? I.first.first : Binary->offsetToVirtualAddr(I.first.first); uint64_t End = UseOffset ? I.first.second @@ -712,7 +706,7 @@ for (auto &CI : OrderedCounters) { uint32_t Indent = 0; - if (!CI.first.empty()) { + if (ProfileIsCS) { // Context string key OS << "[" << CI.first << "]\n"; Indent = 2; @@ -723,6 +717,97 @@ SCounterPrinter(Counter.BranchCounter, "->", Indent); } } +// Format of input: +// number of entries in RangeCounter +// from_1-to_1:count_1 +// from_2-to_2:count_2 +// ...... +// from_n-to_n:count_n +// number of entries in BranchCounter +// src_1->dst_1:count_1 +// src_2->dst_2:count_2 +// ...... +// src_n->dst_n:count_n +void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt, + SampleCounter &SCounters) { + auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) { + std::string Msg = TraceIt.isAtEoF() + ? "Invalid raw profile!" + : "Invalid raw profile at line " + + Twine(TraceIt.getLineNumber()).str() + ": " + + TraceIt.getCurrentLine().str(); + exitWithError(Msg); + }; + auto ReadNumber = [&](uint64_t &Num) { + if (TraceIt.isAtEoF()) + exitWithErrorForTraceLine(TraceIt); + if (TraceIt.getCurrentLine().ltrim().getAsInteger(10, Num)) + exitWithErrorForTraceLine(TraceIt); + TraceIt.advance(); + }; + + auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) { + uint64_t Num = 0; + ReadNumber(Num); + while (Num--) { + if (TraceIt.isAtEoF()) + exitWithErrorForTraceLine(TraceIt); + StringRef Line = TraceIt.getCurrentLine().ltrim(); + + uint64_t Count = 0; + auto LineSplit = Line.split(":"); + if (LineSplit.second.empty() || LineSplit.second.getAsInteger(10, Count)) + exitWithErrorForTraceLine(TraceIt); + + uint64_t Source = 0; + uint64_t Target = 0; + auto Range = LineSplit.first.split(Separator); + if (Range.second.empty() || Range.first.getAsInteger(16, Source) || + Range.second.getAsInteger(16, Target)) + exitWithErrorForTraceLine(TraceIt); + + if (!UseOffset) { + Source = Binary->virtualAddrToOffset(Source); + Target = Binary->virtualAddrToOffset(Target); + } + + Counter[{Source, Target}] += Count; + TraceIt.advance(); + } + }; + + ReadCounter(SCounters.RangeCounter, "-"); + ReadCounter(SCounters.BranchCounter, "->"); +} + +void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) { + TraceStream TraceIt(FileName); + while (!TraceIt.isAtEoF()) { + std::shared_ptr Key = + std::make_shared(); + StringRef Line = TraceIt.getCurrentLine(); + if (Line.startswith("[")) { + ProfileIsCS = true; + auto I = ContextStrSet.insert(Line.str()); + SampleContext::createCtxVectorFromStr(*I.first, Key->Context); + TraceIt.advance(); + } + Key->genHashCode(); + auto Ret = + SampleCounters.emplace(Hashable(Key), SampleCounter()); + readSampleCounters(TraceIt, Ret.first->second); + } +} + +void UnsymbolizedProfileReader::parsePerfTraces() { + readUnsymbolizedProfile(PerfTraceFile); + + if (SkipSymbolization) { + WithColor::warning() + << "Input and output are both an unsymbolized profile!"; + writeUnsymbolizedProfile(OutputFilename); + } +} void LBRPerfReader::computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat) { @@ -755,7 +840,7 @@ } } -void LBRPerfReader::generateRawProfile() { +void LBRPerfReader::generateUnsymbolizedProfile() { // There is no context for LBR only sample, so initialize one entry with // fake "empty" context key. assert(SampleCounters.empty() && @@ -890,14 +975,19 @@ // 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 -PerfScriptType PerfReaderBase::checkPerfScriptType(StringRef FileName) { +uint32_t PerfReaderBase::checkPerfScriptType(StringRef FileName) { + uint32_t Content = 0; TraceStream TraceIt(FileName); uint64_t FrameAddr = 0; while (!TraceIt.isAtEoF()) { // Skip the aggregated count + FrameAddr = 0; if (!TraceIt.getCurrentLine().getAsInteger(10, FrameAddr)) TraceIt.advance(); + if (FrameAddr) + Content |= PerfContent::Aggregated; + // Detect sample with call stack int32_t Count = 0; while (!TraceIt.isAtEoF() && @@ -908,24 +998,25 @@ if (!TraceIt.isAtEoF()) { if (isLBRSample(TraceIt.getCurrentLine())) { if (Count > 0) - return PERF_LBR_STACK; + Content |= PerfContent::LBRStack; else - return PERF_LBR; + Content |= PerfContent::LBR; + return Content; } TraceIt.advance(); } } exitWithError("Invalid perf script input!"); - return PERF_INVALID; + return Content; } -void HybridPerfReader::generateRawProfile() { +void HybridPerfReader::generateUnsymbolizedProfile() { ProfileIsCS = !IgnoreStackSamples; if (ProfileIsCS) unwindSamples(); else - LBRPerfReader::generateRawProfile(); + LBRPerfReader::generateUnsymbolizedProfile(); } void PerfReaderBase::warnTruncatedStack() { @@ -943,10 +1034,10 @@ // Generate unsymbolized profile. warnTruncatedStack(); - generateRawProfile(); + generateUnsymbolizedProfile(); if (SkipSymbolization) - writeRawProfile(OutputFilename); + writeUnsymbolizedProfile(OutputFilename); } } // end namespace sampleprof 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 @@ -21,12 +21,14 @@ static cl::OptionCategory ProfGenCategory("ProfGen Options"); -cl::opt PerfTraceFilename( +static cl::opt PerfScriptFilename( "perfscript", cl::value_desc("perfscript"), cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated, cl::desc("Path of perf-script trace created by Linux perf tool with " "`script` command(the raw perf.data should be profiled with -b)"), 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::ZeroOrMore, @@ -34,6 +36,17 @@ cl::desc("Path of raw perf data created by Linux perf tool (it should be " "profiled with -b)"), 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::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated, + cl::desc("Path of the raw profile created by " + "`llvm-profgen` with `--skip-symbolization`"), + cl::cat(ProfGenCategory)); +static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"), + cl::aliasopt(UnsymbolizedProfFilename)); static cl::opt BinaryPath( "binary", cl::value_desc("binary"), cl::Required, @@ -49,36 +62,34 @@ // Validate the command line input. static void validateCommandLine() { - // Validate input profile is provided only once - if (PerfDataFilename.getNumOccurrences() && - PerfTraceFilename.getNumOccurrences()) { - std::string Msg = "`-perfdata` and `-perfscript` cannot be used together."; - exitWithError(Msg); - } - - // Validate input profile is provided - if (!PerfDataFilename.getNumOccurrences() && - !PerfTraceFilename.getNumOccurrences()) { - std::string Msg = - "Use `-perfdata` or `-perfscript` to provide input perf profile."; - exitWithError(Msg); - } - - // Allow the invalid perfscript if we only use to show binary disassembly. + // Allow the missing perfscript if we only use to show binary disassembly. if (!ShowDisassemblyOnly) { - if (PerfTraceFilename.getNumOccurrences() && - !llvm::sys::fs::exists(PerfTraceFilename)) { + // Validate input profile is provided only once + uint16_t HasPerfData = PerfDataFilename.getNumOccurrences(); + uint16_t HasPerfScript = PerfScriptFilename.getNumOccurrences(); + uint16_t HasUnsymbolizedProfile = + UnsymbolizedProfFilename.getNumOccurrences(); + uint16_t S = HasPerfData + HasPerfScript + HasUnsymbolizedProfile; + if (S != 1) { std::string Msg = - "Input perf script(" + PerfTraceFilename + ") doesn't exist."; + S > 1 + ? "`--perfscript`, `--perfdata` and `--raw-profile` cannot be " + "used together." + : "Perf input file is missing, please use one of `--perfscript`, " + "`--perfdata` and `--raw-profile` for the input."; exitWithError(Msg); } - if (PerfDataFilename.getNumOccurrences() && - !llvm::sys::fs::exists(PerfDataFilename)) { - std::string Msg = - "Input perf data(" + PerfDataFilename + ") doesn't exist."; - 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); } if (!llvm::sys::fs::exists(BinaryPath)) { @@ -95,6 +106,21 @@ } } +static PerfInputFile getPerfInputFile() { + PerfInputFile File; + if (PerfDataFilename.getNumOccurrences()) { + File.InputFile = PerfDataFilename; + File.Format |= PerfFormat::PerfData; + } else if (PerfScriptFilename.getNumOccurrences()) { + File.InputFile = PerfScriptFilename; + File.Format |= PerfFormat::PerfScript; + } else if (UnsymbolizedProfFilename.getNumOccurrences()) { + File.InputFile = UnsymbolizedProfFilename; + File.Format |= PerfFormat::UnsymbolizedProfile; + } + return File; +} + int main(int argc, const char *argv[]) { InitLLVM X(argc, argv); @@ -113,15 +139,10 @@ if (ShowDisassemblyOnly) return EXIT_SUCCESS; - // Parse perf events and samples - StringRef PerfInputFile; - bool IsPerfData = PerfDataFilename.getNumOccurrences(); - if (IsPerfData) - PerfInputFile = PerfDataFilename; - else - PerfInputFile = PerfTraceFilename; + PerfInputFile PerfFile = getPerfInputFile(); std::unique_ptr Reader = - PerfReaderBase::create(Binary.get(), PerfInputFile, IsPerfData); + PerfReaderBase::create(Binary.get(), PerfFile); + // Parse perf events and samples Reader->parsePerfTraces(); if (SkipSymbolization)