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 ; 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,26 @@ } }; -// 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 { + UnknownFormat = 0, + PerfData = 1, // Raw linux perf.data. + PerfScript = 2, // Perf script create by `perf script` command. + UnsymbolizedProfile = 3, // Unsymbolized profile generated by llvm-profgen. + +}; + +// The type of perfscript content. +enum PerfContent { + UnknownContent = 0, + LBR = 1, // Only LBR sample. + LBRStack = 2, // Hybrid sample including call stack and LBR stack. +}; + +struct PerfInputFile { + std::string InputFile; + PerfFormat Format = PerfFormat::UnknownFormat; + PerfContent Content = PerfContent::UnknownContent; }; // The parsed LBR sample entry. @@ -503,24 +517,44 @@ // 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() = 0; const ContextSampleCounterMap &getSampleCounters() const { return SampleCounters; } bool profileIsCS() { return ProfileIsCS; } +protected: + ProfiledBinary *Binary = nullptr; + StringRef PerfTraceFile; + + ContextSampleCounterMap SampleCounters; + bool ProfileIsCS = false; +}; + +// Read perf script to parse the events and samples. +class PerfScriptReader : public PerfReaderBase { +public: + PerfScriptReader(ProfiledBinary *B, StringRef PerfTrace) + : PerfReaderBase(B, PerfTrace){}; + + // Entry of the reader to parse multiple perf traces + virtual void parsePerfTraces() override; + // Generate perf script from perf data + static PerfInputFile convertPerfDataToTrace(ProfiledBinary *Binary, + PerfInputFile &File); + // Extract perf script type by peaking at the input + static PerfContent checkPerfScriptType(StringRef FileName); + protected: // The parsed MMap event struct MMapEvent { @@ -530,15 +564,11 @@ uint64_t Offset = 0; StringRef BinaryPath; }; - // Generate perf script from perf data - static std::string convertPerfDataToTrace(ProfiledBinary *Binary, - StringRef PerfData); + // 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); // 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,23 +597,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){}; + void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat); // 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; - - bool ProfileIsCS = false; }; /* @@ -592,17 +617,12 @@ 40062f 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... ... 0x4005c8/0x4005dc/P/-/-/0 */ -class LBRPerfReader : public PerfReaderBase { +class LBRPerfReader : public PerfScriptReader { public: - LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace, - PerfScriptType Type = PERF_LBR) - : PerfReaderBase(Binary, PerfTrace, Type){}; + LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace) + : PerfScriptReader(Binary, PerfTrace){}; // Parse the LBR only sample. virtual void parseSample(TraceStream &TraceIt, uint64_t Count) override; - virtual void generateRawProfile() override; - -private: - void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat); }; /* @@ -614,19 +634,51 @@ 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries */ -class HybridPerfReader : public LBRPerfReader { +class HybridPerfReader : public PerfScriptReader { public: HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace) - : LBRPerfReader(Binary, PerfTrace, PERF_LBR_STACK){}; + : PerfScriptReader(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: + + [frame1 @ frame2 @ ...] # If it's a CS profile + 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 + [frame1 @ frame2 @ ...] # Next context + ...... + +Note that non-CS profile doesn't have the empty `[]` context. +*/ +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,29 @@ 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 = PerfScriptReader::convertPerfDataToTrace(Binary, PerfInput); + + assert((PerfInput.Format == PerfFormat::PerfScript) && + "Should be a perfscript!"); + + PerfInput.Content = + PerfScriptReader::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 +308,9 @@ return PerfReader; } -std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary, - StringRef PerfData) { +PerfInputFile PerfScriptReader::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,10 +355,10 @@ PIDs, "-i", PerfData}; sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, llvm::None, Redirects); - return PerfTraceFile; + return {PerfTraceFile, PerfFormat::PerfScript, PerfContent::UnknownContent}; } -void PerfReaderBase::updateBinaryAddress(const MMapEvent &Event) { +void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) { // Drop the event which doesn't belong to user-provided binary StringRef BinaryName = llvm::sys::path::filename(Event.BinaryPath); if (Binary->getName() != BinaryName) @@ -432,8 +439,8 @@ << format("0x%" PRIx64, Address) << "\n"; } -bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt, - SmallVectorImpl &LBRStack) { +bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, + SmallVectorImpl &LBRStack) { // The raw format of LBR stack is like: // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... // ... 0x4005c8/0x4005dc/P/-/-/0 @@ -551,8 +558,8 @@ return !LBRStack.empty(); } -bool PerfReaderBase::extractCallstack(TraceStream &TraceIt, - SmallVectorImpl &CallStack) { +bool PerfScriptReader::extractCallstack(TraceStream &TraceIt, + SmallVectorImpl &CallStack) { // The raw format of call stack is like: // 4005dc # leaf frame // 400634 @@ -609,7 +616,7 @@ !Binary->addressInPrologEpilog(CallStack.front()); } -void PerfReaderBase::warnIfMissingMMap() { +void PerfScriptReader::warnIfMissingMMap() { if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) { WithColor::warning() << "No relevant mmap event is matched for " << Binary->getName() @@ -663,43 +670,28 @@ } } -void PerfReaderBase::writeRawProfile(StringRef Filename) { +void PerfScriptReader::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 PerfScriptReader::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 +704,7 @@ for (auto &CI : OrderedCounters) { uint32_t Indent = 0; - if (!CI.first.empty()) { + if (ProfileIsCS) { // Context string key OS << "[" << CI.first << "]\n"; Indent = 2; @@ -724,8 +716,95 @@ } } -void LBRPerfReader::computeCounterFromLBR(const PerfSample *Sample, - uint64_t Repeat) { +// 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(); + // Read context stack for CS profile. + 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); +} + +void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample, + uint64_t Repeat) { SampleCounter &Counter = SampleCounters.begin()->second; uint64_t EndOffeset = 0; for (const LBREntry &LBR : Sample->LBRStack) { @@ -755,7 +834,7 @@ } } -void LBRPerfReader::generateRawProfile() { +void PerfScriptReader::generateUnsymbolizedProfile() { // There is no context for LBR only sample, so initialize one entry with // fake "empty" context key. assert(SampleCounters.empty() && @@ -770,7 +849,7 @@ } } -uint64_t PerfReaderBase::parseAggregatedCount(TraceStream &TraceIt) { +uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) { // The aggregated count is optional, so do not skip the line and return 1 if // it's unmatched uint64_t Count = 1; @@ -779,15 +858,15 @@ return Count; } -void PerfReaderBase::parseSample(TraceStream &TraceIt) { +void PerfScriptReader::parseSample(TraceStream &TraceIt) { uint64_t Count = parseAggregatedCount(TraceIt); assert(Count >= 1 && "Aggregated count should be >= 1!"); parseSample(TraceIt, Count); } -bool PerfReaderBase::extractMMap2EventForBinary(ProfiledBinary *Binary, - StringRef Line, - MMapEvent &MMap) { +bool PerfScriptReader::extractMMap2EventForBinary(ProfiledBinary *Binary, + StringRef Line, + MMapEvent &MMap) { // Parse a line like: // PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0 // 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so @@ -831,21 +910,21 @@ return Binary->getName() == BinaryName; } -void PerfReaderBase::parseMMap2Event(TraceStream &TraceIt) { +void PerfScriptReader::parseMMap2Event(TraceStream &TraceIt) { MMapEvent MMap; if (extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap)) updateBinaryAddress(MMap); TraceIt.advance(); } -void PerfReaderBase::parseEventOrSample(TraceStream &TraceIt) { +void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) { if (isMMap2Event(TraceIt.getCurrentLine())) parseMMap2Event(TraceIt); else parseSample(TraceIt); } -void PerfReaderBase::parseAndAggregateTrace() { +void PerfScriptReader::parseAndAggregateTrace() { // Trace line iterator TraceStream TraceIt(PerfTraceFile); while (!TraceIt.isAtEoF()) @@ -856,7 +935,7 @@ // 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ... // A heuristic for fast detection by checking whether a // leading " 0x" and the '/' exist. -bool PerfReaderBase::isLBRSample(StringRef Line) { +bool PerfScriptReader::isLBRSample(StringRef Line) { // Skip the leading instruction pointer SmallVector Records; Line.trim().split(Records, " ", 2, false); @@ -867,7 +946,7 @@ return false; } -bool PerfReaderBase::isMMap2Event(StringRef Line) { +bool PerfScriptReader::isMMap2Event(StringRef Line) { // Short cut to avoid string find is possible. if (Line.empty() || Line.size() < 50) return false; @@ -890,7 +969,7 @@ // 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) { +PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) { TraceStream TraceIt(FileName); uint64_t FrameAddr = 0; while (!TraceIt.isAtEoF()) { @@ -908,27 +987,27 @@ if (!TraceIt.isAtEoF()) { if (isLBRSample(TraceIt.getCurrentLine())) { if (Count > 0) - return PERF_LBR_STACK; + return PerfContent::LBRStack; else - return PERF_LBR; + return PerfContent::LBR; } TraceIt.advance(); } } exitWithError("Invalid perf script input!"); - return PERF_INVALID; + return PerfContent::UnknownContent; } -void HybridPerfReader::generateRawProfile() { +void HybridPerfReader::generateUnsymbolizedProfile() { ProfileIsCS = !IgnoreStackSamples; if (ProfileIsCS) unwindSamples(); else - LBRPerfReader::generateRawProfile(); + PerfScriptReader::generateUnsymbolizedProfile(); } -void PerfReaderBase::warnTruncatedStack() { +void PerfScriptReader::warnTruncatedStack() { for (auto Address : InvalidReturnAddresses) { WithColor::warning() << "Truncated stack sample due to invalid return address at " @@ -937,16 +1016,16 @@ } } -void PerfReaderBase::parsePerfTraces() { +void PerfScriptReader::parsePerfTraces() { // Parse perf traces and do aggregation. parseAndAggregateTrace(); // 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 unsymbolized 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 `--unsymbolized-profile` " + "cannot be used together." + : "Perf input file is missing, please use one of `--perfscript`, " + "`--perfdata` and `--unsymbolized-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)