[llvm] a5f411b - [llvm-profgen] Allow unsymbolized profile as perf input
via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 25 23:58:41 PDT 2021
Author: wlei
Date: 2021-10-25T23:58:08-07:00
New Revision: a5f411b7f88caf1ef000a0397eb93d1a39d25690
URL: https://github.com/llvm/llvm-project/commit/a5f411b7f88caf1ef000a0397eb93d1a39d25690
DIFF: https://github.com/llvm/llvm-project/commit/a5f411b7f88caf1ef000a0397eb93d1a39d25690.diff
LOG: [llvm-profgen] Allow unsymbolized profile as perf input
This change allows the unsymbolized profile as input. The unsymbolized profile is created by `llvm-profgen` with `--skip-symbolization` and it's after the sample aggregation but before symbolization , so it has much small file size. It can be used for sample merging and trimming, also is useful for debugging or adding test cases. A switch `--unsymbolized-profile=file-patch` is added for this.
Format of unsymbolized profile:
```
[context stack1] # 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
[context stack2]
......
```
Reviewed By: hoy, wenlei
Differential Revision: https://reviews.llvm.org/D111750
Added:
Modified:
llvm/include/llvm/ProfileData/SampleProf.h
llvm/test/tools/llvm-profgen/inline-noprobe.test
llvm/test/tools/llvm-profgen/inline-noprobe2.test
llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test
llvm/tools/llvm-profgen/PerfReader.cpp
llvm/tools/llvm-profgen/PerfReader.h
llvm/tools/llvm-profgen/llvm-profgen.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index 3e0753ba14dd5..89faa835a15ea 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -495,28 +495,32 @@ class SampleContext {
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
index 7d8a876f46da1..f7a61470f261a 100644
--- 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
index 147c509d3f3d0..3fcc1cb5541c2 100644
--- 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
index 46d358f9a669f..9f4918741d855 100644
--- 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.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 8224c085d6071..e36c707c4fcd8 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -24,7 +24,8 @@ static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::init(false),
static cl::opt<bool>
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<bool>
IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore,
@@ -277,24 +278,29 @@ bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
return true;
}
-std::unique_ptr<PerfReaderBase> 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>
+PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput) {
+ std::unique_ptr<PerfReaderBase> PerfReader;
+
+ if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) {
+ PerfReader.reset(
+ new UnsymbolizedProfileReader(Binary, PerfInput.InputFile));
+ return PerfReader;
}
- PerfScriptType PerfType = checkPerfScriptType(PerfInputFile);
- std::unique_ptr<PerfReaderBase> 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 @@ std::unique_ptr<PerfReaderBase> PerfReaderBase::create(ProfiledBinary *Binary,
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 @@ std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary,
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 @@ void HybridPerfReader::unwindSamples() {
<< format("0x%" PRIx64, Address) << "\n";
}
-bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
- SmallVectorImpl<LBREntry> &LBRStack) {
+bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
+ SmallVectorImpl<LBREntry> &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 @@ bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
return !LBRStack.empty();
}
-bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
- SmallVectorImpl<uint64_t> &CallStack) {
+bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
+ SmallVectorImpl<uint64_t> &CallStack) {
// The raw format of call stack is like:
// 4005dc # leaf frame
// 400634
@@ -609,7 +616,7 @@ bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
!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 HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
}
}
-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<std::string, SampleCounter *>;
-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 @@ void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
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 PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
}
}
-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<StringBasedCtxKey> Key =
+ std::make_shared<StringBasedCtxKey>();
+ 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<ContextKey>(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::parseSample(TraceStream &TraceIt, uint64_t Count) {
}
}
-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 @@ void LBRPerfReader::generateRawProfile() {
}
}
-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 @@ uint64_t PerfReaderBase::parseAggregatedCount(TraceStream &TraceIt) {
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 @@ bool PerfReaderBase::extractMMap2EventForBinary(ProfiledBinary *Binary,
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 @@ void PerfReaderBase::parseAndAggregateTrace() {
// 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<StringRef, 32> Records;
Line.trim().split(Records, " ", 2, false);
@@ -867,7 +946,7 @@ bool PerfReaderBase::isLBRSample(StringRef Line) {
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 @@ bool PerfReaderBase::isMMap2Event(StringRef Line) {
// 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 @@ PerfScriptType PerfReaderBase::checkPerfScriptType(StringRef FileName) {
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::warnTruncatedStack() {
}
}
-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/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index 9870bcc5411aa..c9f74313c166d 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -58,12 +58,26 @@ class TraceStream {
}
};
-// 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 @@ class VirtualUnwinder {
// 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<PerfReaderBase>
- create(ProfiledBinary *Binary, StringRef PerfInputFile, bool IsPerfData);
+ static std::unique_ptr<PerfReaderBase> 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 @@ class PerfReaderBase {
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 @@ class PerfReaderBase {
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<uint64_t> InvalidReturnAddresses;
-
- bool ProfileIsCS = false;
};
/*
@@ -592,17 +617,12 @@ class PerfReaderBase {
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 @@ class LBRPerfReader : public PerfReaderBase {
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<std::string> ContextStrSet;
+};
+
} // end namespace sampleprof
} // end namespace llvm
diff --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp
index b5be250fc12f9..ad7e6dcb029a3 100644
--- 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<std::string> PerfTraceFilename(
+static cl::opt<std::string> 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<std::string> PerfDataFilename(
"perfdata", cl::value_desc("perfdata"), cl::ZeroOrMore,
@@ -34,6 +36,17 @@ static cl::opt<std::string> PerfDataFilename(
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<std::string> 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<std::string> BinaryPath(
"binary", cl::value_desc("binary"), cl::Required,
@@ -49,36 +62,34 @@ using namespace sampleprof;
// 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 void validateCommandLine() {
}
}
+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 @@ int main(int argc, const char *argv[]) {
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<PerfReaderBase> Reader =
- PerfReaderBase::create(Binary.get(), PerfInputFile, IsPerfData);
+ PerfReaderBase::create(Binary.get(), PerfFile);
+ // Parse perf events and samples
Reader->parsePerfTraces();
if (SkipSymbolization)
More information about the llvm-commits
mailing list