forked from OSchip/llvm-project
[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
This commit is contained in:
parent
9bbfe0f72c
commit
a5f411b7f8
|
@ -495,28 +495,32 @@ public:
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -58,12 +58,26 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// 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 @@ private:
|
|||
// 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 @@ protected:
|
|||
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 @@ protected:
|
|||
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 @@ protected:
|
|||
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 @@ private:
|
|||
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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue