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;
|
State = UnknownContext;
|
||||||
Name = ContextStr;
|
Name = ContextStr;
|
||||||
} else {
|
} else {
|
||||||
// Remove encapsulating '[' and ']' if any
|
|
||||||
ContextStr = ContextStr.substr(1, ContextStr.size() - 2);
|
|
||||||
CSNameTable.emplace_back();
|
CSNameTable.emplace_back();
|
||||||
SampleContextFrameVector &Context = CSNameTable.back();
|
SampleContextFrameVector &Context = CSNameTable.back();
|
||||||
/// Create a context vector from a given context string and save it in
|
createCtxVectorFromStr(ContextStr, Context);
|
||||||
/// `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);
|
|
||||||
}
|
|
||||||
|
|
||||||
setContext(Context, CState);
|
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
|
// Promote context by removing top frames with the length of
|
||||||
// `ContextFramesToRemove`. Note that with array representation of context,
|
// `ContextFramesToRemove`. Note that with array representation of context,
|
||||||
// the promotion is effectively a slice operation with first
|
// 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: 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: 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: 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: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/inline-noprobe.perfbin --output=%t1
|
||||||
; RUN: FileCheck %s --input-file %t --check-prefix=CHECK
|
; 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: 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
|
; 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: 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: 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: 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: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t1 --use-offset=0
|
||||||
; RUN: FileCheck %s --input-file %t --check-prefix=CHECK
|
; 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-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
|
; 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
|
; 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: 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: 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: 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 %t
|
; 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: 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: 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
|
; 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>
|
static cl::opt<bool>
|
||||||
UseOffset("use-offset", cl::init(true), cl::ZeroOrMore,
|
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."));
|
"offset instead of virtual address."));
|
||||||
static cl::opt<bool>
|
static cl::opt<bool>
|
||||||
IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore,
|
IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore,
|
||||||
|
@ -277,24 +278,29 @@ bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PerfReaderBase> PerfReaderBase::create(ProfiledBinary *Binary,
|
std::unique_ptr<PerfReaderBase>
|
||||||
StringRef PerfInputFile,
|
PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput) {
|
||||||
bool IsPerfData) {
|
std::unique_ptr<PerfReaderBase> PerfReader;
|
||||||
// For perf data input, we need to convert them into perf script first.
|
|
||||||
if (IsPerfData) {
|
if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) {
|
||||||
std::string ConvertedPerfScript =
|
PerfReader.reset(
|
||||||
convertPerfDataToTrace(Binary, PerfInputFile);
|
new UnsymbolizedProfileReader(Binary, PerfInput.InputFile));
|
||||||
// Let commoand opt own the string for converted perf trace file name
|
return PerfReader;
|
||||||
PerfTraceFilename = ConvertedPerfScript;
|
|
||||||
PerfInputFile = PerfTraceFilename;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfScriptType PerfType = checkPerfScriptType(PerfInputFile);
|
// For perf data input, we need to convert them into perf script first.
|
||||||
std::unique_ptr<PerfReaderBase> PerfReader;
|
if (PerfInput.Format == PerfFormat::PerfData)
|
||||||
if (PerfType == PERF_LBR_STACK) {
|
PerfInput = PerfScriptReader::convertPerfDataToTrace(Binary, PerfInput);
|
||||||
PerfReader.reset(new HybridPerfReader(Binary, PerfInputFile));
|
|
||||||
} else if (PerfType == PERF_LBR) {
|
assert((PerfInput.Format == PerfFormat::PerfScript) &&
|
||||||
PerfReader.reset(new LBRPerfReader(Binary, PerfInputFile));
|
"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 {
|
} else {
|
||||||
exitWithError("Unsupported perfscript!");
|
exitWithError("Unsupported perfscript!");
|
||||||
}
|
}
|
||||||
|
@ -302,8 +308,9 @@ std::unique_ptr<PerfReaderBase> PerfReaderBase::create(ProfiledBinary *Binary,
|
||||||
return PerfReader;
|
return PerfReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary,
|
PerfInputFile PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary,
|
||||||
StringRef PerfData) {
|
PerfInputFile &File) {
|
||||||
|
StringRef PerfData = File.InputFile;
|
||||||
// Run perf script to retrieve PIDs matching binary we're interested in.
|
// Run perf script to retrieve PIDs matching binary we're interested in.
|
||||||
auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
|
auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
|
||||||
if (!PerfExecutable) {
|
if (!PerfExecutable) {
|
||||||
|
@ -348,10 +355,10 @@ std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary,
|
||||||
PIDs, "-i", PerfData};
|
PIDs, "-i", PerfData};
|
||||||
sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, llvm::None, Redirects);
|
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
|
// Drop the event which doesn't belong to user-provided binary
|
||||||
StringRef BinaryName = llvm::sys::path::filename(Event.BinaryPath);
|
StringRef BinaryName = llvm::sys::path::filename(Event.BinaryPath);
|
||||||
if (Binary->getName() != BinaryName)
|
if (Binary->getName() != BinaryName)
|
||||||
|
@ -432,8 +439,8 @@ void HybridPerfReader::unwindSamples() {
|
||||||
<< format("0x%" PRIx64, Address) << "\n";
|
<< format("0x%" PRIx64, Address) << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
|
bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
|
||||||
SmallVectorImpl<LBREntry> &LBRStack) {
|
SmallVectorImpl<LBREntry> &LBRStack) {
|
||||||
// The raw format of LBR stack is like:
|
// The raw format of LBR stack is like:
|
||||||
// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
|
// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
|
||||||
// ... 0x4005c8/0x4005dc/P/-/-/0
|
// ... 0x4005c8/0x4005dc/P/-/-/0
|
||||||
|
@ -551,8 +558,8 @@ bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
|
||||||
return !LBRStack.empty();
|
return !LBRStack.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
|
bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
|
||||||
SmallVectorImpl<uint64_t> &CallStack) {
|
SmallVectorImpl<uint64_t> &CallStack) {
|
||||||
// The raw format of call stack is like:
|
// The raw format of call stack is like:
|
||||||
// 4005dc # leaf frame
|
// 4005dc # leaf frame
|
||||||
// 400634
|
// 400634
|
||||||
|
@ -609,7 +616,7 @@ bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
|
||||||
!Binary->addressInPrologEpilog(CallStack.front());
|
!Binary->addressInPrologEpilog(CallStack.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfReaderBase::warnIfMissingMMap() {
|
void PerfScriptReader::warnIfMissingMMap() {
|
||||||
if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
|
if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
|
||||||
WithColor::warning() << "No relevant mmap event is matched for "
|
WithColor::warning() << "No relevant mmap event is matched for "
|
||||||
<< Binary->getName()
|
<< 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;
|
std::error_code EC;
|
||||||
raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
|
raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
|
||||||
if (EC)
|
if (EC)
|
||||||
exitWithError(EC, Filename);
|
exitWithError(EC, Filename);
|
||||||
writeRawProfile(OS);
|
writeUnsymbolizedProfile(OS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use ordered map to make the output deterministic
|
// Use ordered map to make the output deterministic
|
||||||
using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
|
using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
|
||||||
|
|
||||||
void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
|
void PerfScriptReader::writeUnsymbolizedProfile(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
|
|
||||||
*/
|
|
||||||
|
|
||||||
OrderedCounterForPrint OrderedCounters;
|
OrderedCounterForPrint OrderedCounters;
|
||||||
for (auto &CI : SampleCounters) {
|
for (auto &CI : SampleCounters) {
|
||||||
OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second;
|
OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SCounterPrinter = [&](RangeSample Counter, StringRef Separator,
|
auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator,
|
||||||
uint32_t Indent) {
|
uint32_t Indent) {
|
||||||
OS.indent(Indent);
|
OS.indent(Indent);
|
||||||
OS << Counter.size() << "\n";
|
OS << Counter.size() << "\n";
|
||||||
for (auto I : Counter) {
|
for (auto &I : Counter) {
|
||||||
uint64_t Start = UseOffset ? I.first.first
|
uint64_t Start = UseOffset ? I.first.first
|
||||||
: Binary->offsetToVirtualAddr(I.first.first);
|
: Binary->offsetToVirtualAddr(I.first.first);
|
||||||
uint64_t End = UseOffset ? I.first.second
|
uint64_t End = UseOffset ? I.first.second
|
||||||
|
@ -712,7 +704,7 @@ void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
|
||||||
|
|
||||||
for (auto &CI : OrderedCounters) {
|
for (auto &CI : OrderedCounters) {
|
||||||
uint32_t Indent = 0;
|
uint32_t Indent = 0;
|
||||||
if (!CI.first.empty()) {
|
if (ProfileIsCS) {
|
||||||
// Context string key
|
// Context string key
|
||||||
OS << "[" << CI.first << "]\n";
|
OS << "[" << CI.first << "]\n";
|
||||||
Indent = 2;
|
Indent = 2;
|
||||||
|
@ -724,8 +716,95 @@ void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LBRPerfReader::computeCounterFromLBR(const PerfSample *Sample,
|
// Format of input:
|
||||||
uint64_t Repeat) {
|
// 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;
|
SampleCounter &Counter = SampleCounters.begin()->second;
|
||||||
uint64_t EndOffeset = 0;
|
uint64_t EndOffeset = 0;
|
||||||
for (const LBREntry &LBR : Sample->LBRStack) {
|
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
|
// There is no context for LBR only sample, so initialize one entry with
|
||||||
// fake "empty" context key.
|
// fake "empty" context key.
|
||||||
assert(SampleCounters.empty() &&
|
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
|
// The aggregated count is optional, so do not skip the line and return 1 if
|
||||||
// it's unmatched
|
// it's unmatched
|
||||||
uint64_t Count = 1;
|
uint64_t Count = 1;
|
||||||
|
@ -779,15 +858,15 @@ uint64_t PerfReaderBase::parseAggregatedCount(TraceStream &TraceIt) {
|
||||||
return Count;
|
return Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfReaderBase::parseSample(TraceStream &TraceIt) {
|
void PerfScriptReader::parseSample(TraceStream &TraceIt) {
|
||||||
uint64_t Count = parseAggregatedCount(TraceIt);
|
uint64_t Count = parseAggregatedCount(TraceIt);
|
||||||
assert(Count >= 1 && "Aggregated count should be >= 1!");
|
assert(Count >= 1 && "Aggregated count should be >= 1!");
|
||||||
parseSample(TraceIt, Count);
|
parseSample(TraceIt, Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PerfReaderBase::extractMMap2EventForBinary(ProfiledBinary *Binary,
|
bool PerfScriptReader::extractMMap2EventForBinary(ProfiledBinary *Binary,
|
||||||
StringRef Line,
|
StringRef Line,
|
||||||
MMapEvent &MMap) {
|
MMapEvent &MMap) {
|
||||||
// Parse a line like:
|
// Parse a line like:
|
||||||
// PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
|
// PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
|
||||||
// 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
|
// 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;
|
return Binary->getName() == BinaryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfReaderBase::parseMMap2Event(TraceStream &TraceIt) {
|
void PerfScriptReader::parseMMap2Event(TraceStream &TraceIt) {
|
||||||
MMapEvent MMap;
|
MMapEvent MMap;
|
||||||
if (extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap))
|
if (extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap))
|
||||||
updateBinaryAddress(MMap);
|
updateBinaryAddress(MMap);
|
||||||
TraceIt.advance();
|
TraceIt.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfReaderBase::parseEventOrSample(TraceStream &TraceIt) {
|
void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) {
|
||||||
if (isMMap2Event(TraceIt.getCurrentLine()))
|
if (isMMap2Event(TraceIt.getCurrentLine()))
|
||||||
parseMMap2Event(TraceIt);
|
parseMMap2Event(TraceIt);
|
||||||
else
|
else
|
||||||
parseSample(TraceIt);
|
parseSample(TraceIt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfReaderBase::parseAndAggregateTrace() {
|
void PerfScriptReader::parseAndAggregateTrace() {
|
||||||
// Trace line iterator
|
// Trace line iterator
|
||||||
TraceStream TraceIt(PerfTraceFile);
|
TraceStream TraceIt(PerfTraceFile);
|
||||||
while (!TraceIt.isAtEoF())
|
while (!TraceIt.isAtEoF())
|
||||||
|
@ -856,7 +935,7 @@ void PerfReaderBase::parseAndAggregateTrace() {
|
||||||
// 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
|
// 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
|
||||||
// A heuristic for fast detection by checking whether a
|
// A heuristic for fast detection by checking whether a
|
||||||
// leading " 0x" and the '/' exist.
|
// leading " 0x" and the '/' exist.
|
||||||
bool PerfReaderBase::isLBRSample(StringRef Line) {
|
bool PerfScriptReader::isLBRSample(StringRef Line) {
|
||||||
// Skip the leading instruction pointer
|
// Skip the leading instruction pointer
|
||||||
SmallVector<StringRef, 32> Records;
|
SmallVector<StringRef, 32> Records;
|
||||||
Line.trim().split(Records, " ", 2, false);
|
Line.trim().split(Records, " ", 2, false);
|
||||||
|
@ -867,7 +946,7 @@ bool PerfReaderBase::isLBRSample(StringRef Line) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PerfReaderBase::isMMap2Event(StringRef Line) {
|
bool PerfScriptReader::isMMap2Event(StringRef Line) {
|
||||||
// Short cut to avoid string find is possible.
|
// Short cut to avoid string find is possible.
|
||||||
if (Line.empty() || Line.size() < 50)
|
if (Line.empty() || Line.size() < 50)
|
||||||
return false;
|
return false;
|
||||||
|
@ -890,7 +969,7 @@ bool PerfReaderBase::isMMap2Event(StringRef Line) {
|
||||||
// Determine the perfscript contains hybrid samples(call stack + LBRs) by
|
// Determine the perfscript contains hybrid samples(call stack + LBRs) by
|
||||||
// checking whether there is a non-empty call stack immediately followed by
|
// checking whether there is a non-empty call stack immediately followed by
|
||||||
// a LBR sample
|
// a LBR sample
|
||||||
PerfScriptType PerfReaderBase::checkPerfScriptType(StringRef FileName) {
|
PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) {
|
||||||
TraceStream TraceIt(FileName);
|
TraceStream TraceIt(FileName);
|
||||||
uint64_t FrameAddr = 0;
|
uint64_t FrameAddr = 0;
|
||||||
while (!TraceIt.isAtEoF()) {
|
while (!TraceIt.isAtEoF()) {
|
||||||
|
@ -908,27 +987,27 @@ PerfScriptType PerfReaderBase::checkPerfScriptType(StringRef FileName) {
|
||||||
if (!TraceIt.isAtEoF()) {
|
if (!TraceIt.isAtEoF()) {
|
||||||
if (isLBRSample(TraceIt.getCurrentLine())) {
|
if (isLBRSample(TraceIt.getCurrentLine())) {
|
||||||
if (Count > 0)
|
if (Count > 0)
|
||||||
return PERF_LBR_STACK;
|
return PerfContent::LBRStack;
|
||||||
else
|
else
|
||||||
return PERF_LBR;
|
return PerfContent::LBR;
|
||||||
}
|
}
|
||||||
TraceIt.advance();
|
TraceIt.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exitWithError("Invalid perf script input!");
|
exitWithError("Invalid perf script input!");
|
||||||
return PERF_INVALID;
|
return PerfContent::UnknownContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HybridPerfReader::generateRawProfile() {
|
void HybridPerfReader::generateUnsymbolizedProfile() {
|
||||||
ProfileIsCS = !IgnoreStackSamples;
|
ProfileIsCS = !IgnoreStackSamples;
|
||||||
if (ProfileIsCS)
|
if (ProfileIsCS)
|
||||||
unwindSamples();
|
unwindSamples();
|
||||||
else
|
else
|
||||||
LBRPerfReader::generateRawProfile();
|
PerfScriptReader::generateUnsymbolizedProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfReaderBase::warnTruncatedStack() {
|
void PerfScriptReader::warnTruncatedStack() {
|
||||||
for (auto Address : InvalidReturnAddresses) {
|
for (auto Address : InvalidReturnAddresses) {
|
||||||
WithColor::warning()
|
WithColor::warning()
|
||||||
<< "Truncated stack sample due to invalid return address at "
|
<< "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.
|
// Parse perf traces and do aggregation.
|
||||||
parseAndAggregateTrace();
|
parseAndAggregateTrace();
|
||||||
|
|
||||||
// Generate unsymbolized profile.
|
// Generate unsymbolized profile.
|
||||||
warnTruncatedStack();
|
warnTruncatedStack();
|
||||||
generateRawProfile();
|
generateUnsymbolizedProfile();
|
||||||
|
|
||||||
if (SkipSymbolization)
|
if (SkipSymbolization)
|
||||||
writeRawProfile(OutputFilename);
|
writeUnsymbolizedProfile(OutputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace sampleprof
|
} // end namespace sampleprof
|
||||||
|
|
|
@ -58,12 +58,26 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The type of perfscript
|
// The type of input format.
|
||||||
enum PerfScriptType {
|
enum PerfFormat {
|
||||||
PERF_UNKNOWN = 0,
|
UnknownFormat = 0,
|
||||||
PERF_INVALID = 1,
|
PerfData = 1, // Raw linux perf.data.
|
||||||
PERF_LBR = 2, // Only LBR sample
|
PerfScript = 2, // Perf script create by `perf script` command.
|
||||||
PERF_LBR_STACK = 3, // Hybrid sample including call stack and LBR stack.
|
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.
|
// The parsed LBR sample entry.
|
||||||
|
@ -503,24 +517,44 @@ private:
|
||||||
// Read perf trace to parse the events and samples.
|
// Read perf trace to parse the events and samples.
|
||||||
class PerfReaderBase {
|
class PerfReaderBase {
|
||||||
public:
|
public:
|
||||||
PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace,
|
PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace)
|
||||||
PerfScriptType Type = PERF_UNKNOWN)
|
: Binary(B), PerfTraceFile(PerfTrace) {
|
||||||
: Binary(B), PerfTraceFile(PerfTrace), PerfType(Type) {
|
|
||||||
// Initialize the base address to preferred address.
|
// Initialize the base address to preferred address.
|
||||||
Binary->setBaseAddress(Binary->getPreferredBaseAddress());
|
Binary->setBaseAddress(Binary->getPreferredBaseAddress());
|
||||||
};
|
};
|
||||||
virtual ~PerfReaderBase() = default;
|
virtual ~PerfReaderBase() = default;
|
||||||
static std::unique_ptr<PerfReaderBase>
|
static std::unique_ptr<PerfReaderBase> create(ProfiledBinary *Binary,
|
||||||
create(ProfiledBinary *Binary, StringRef PerfInputFile, bool IsPerfData);
|
PerfInputFile &PerfInput);
|
||||||
|
|
||||||
PerfScriptType getPerfScriptType() const { return PerfType; }
|
|
||||||
// Entry of the reader to parse multiple perf traces
|
// Entry of the reader to parse multiple perf traces
|
||||||
void parsePerfTraces();
|
virtual void parsePerfTraces() = 0;
|
||||||
const ContextSampleCounterMap &getSampleCounters() const {
|
const ContextSampleCounterMap &getSampleCounters() const {
|
||||||
return SampleCounters;
|
return SampleCounters;
|
||||||
}
|
}
|
||||||
bool profileIsCS() { return ProfileIsCS; }
|
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:
|
protected:
|
||||||
// The parsed MMap event
|
// The parsed MMap event
|
||||||
struct MMapEvent {
|
struct MMapEvent {
|
||||||
|
@ -530,15 +564,11 @@ protected:
|
||||||
uint64_t Offset = 0;
|
uint64_t Offset = 0;
|
||||||
StringRef BinaryPath;
|
StringRef BinaryPath;
|
||||||
};
|
};
|
||||||
// Generate perf script from perf data
|
|
||||||
static std::string convertPerfDataToTrace(ProfiledBinary *Binary,
|
|
||||||
StringRef PerfData);
|
|
||||||
// Check whether a given line is LBR sample
|
// Check whether a given line is LBR sample
|
||||||
static bool isLBRSample(StringRef Line);
|
static bool isLBRSample(StringRef Line);
|
||||||
// Check whether a given line is MMAP event
|
// Check whether a given line is MMAP event
|
||||||
static bool isMMap2Event(StringRef Line);
|
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
|
// Parse a single line of a PERF_RECORD_MMAP2 event looking for a
|
||||||
// mapping between the binary name and its memory layout.
|
// mapping between the binary name and its memory layout.
|
||||||
static bool extractMMap2EventForBinary(ProfiledBinary *Binary, StringRef Line,
|
static bool extractMMap2EventForBinary(ProfiledBinary *Binary, StringRef Line,
|
||||||
|
@ -567,23 +597,18 @@ protected:
|
||||||
void parseSample(TraceStream &TraceIt);
|
void parseSample(TraceStream &TraceIt);
|
||||||
// An aggregated count is given to indicate how many times the sample is
|
// An aggregated count is given to indicate how many times the sample is
|
||||||
// repeated.
|
// 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
|
// Post process the profile after trace aggregation, we will do simple range
|
||||||
// overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample).
|
// overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample).
|
||||||
virtual void generateRawProfile() = 0;
|
virtual void generateUnsymbolizedProfile();
|
||||||
void writeRawProfile(StringRef Filename);
|
void writeUnsymbolizedProfile(StringRef Filename);
|
||||||
void writeRawProfile(raw_fd_ostream &OS);
|
void writeUnsymbolizedProfile(raw_fd_ostream &OS);
|
||||||
|
|
||||||
ProfiledBinary *Binary = nullptr;
|
|
||||||
StringRef PerfTraceFile;
|
|
||||||
ContextSampleCounterMap SampleCounters;
|
|
||||||
// Samples with the repeating time generated by the perf reader
|
// Samples with the repeating time generated by the perf reader
|
||||||
AggregatedCounter AggregatedSamples;
|
AggregatedCounter AggregatedSamples;
|
||||||
PerfScriptType PerfType = PERF_UNKNOWN;
|
|
||||||
// Keep track of all invalid return addresses
|
// Keep track of all invalid return addresses
|
||||||
std::set<uint64_t> InvalidReturnAddresses;
|
std::set<uint64_t> InvalidReturnAddresses;
|
||||||
|
|
||||||
bool ProfileIsCS = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -592,17 +617,12 @@ protected:
|
||||||
40062f 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
|
40062f 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
|
||||||
... 0x4005c8/0x4005dc/P/-/-/0
|
... 0x4005c8/0x4005dc/P/-/-/0
|
||||||
*/
|
*/
|
||||||
class LBRPerfReader : public PerfReaderBase {
|
class LBRPerfReader : public PerfScriptReader {
|
||||||
public:
|
public:
|
||||||
LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,
|
LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
|
||||||
PerfScriptType Type = PERF_LBR)
|
: PerfScriptReader(Binary, PerfTrace){};
|
||||||
: PerfReaderBase(Binary, PerfTrace, Type){};
|
|
||||||
// Parse the LBR only sample.
|
// Parse the LBR only sample.
|
||||||
virtual void parseSample(TraceStream &TraceIt, uint64_t Count) override;
|
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 0x40062f/0x4005b0/P/-/-/0 ...
|
||||||
... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
|
... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
|
||||||
*/
|
*/
|
||||||
class HybridPerfReader : public LBRPerfReader {
|
class HybridPerfReader : public PerfScriptReader {
|
||||||
public:
|
public:
|
||||||
HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
|
HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
|
||||||
: LBRPerfReader(Binary, PerfTrace, PERF_LBR_STACK){};
|
: PerfScriptReader(Binary, PerfTrace){};
|
||||||
// Parse the hybrid sample including the call and LBR line
|
// Parse the hybrid sample including the call and LBR line
|
||||||
void parseSample(TraceStream &TraceIt, uint64_t Count) override;
|
void parseSample(TraceStream &TraceIt, uint64_t Count) override;
|
||||||
void generateRawProfile() override;
|
void generateUnsymbolizedProfile() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Unwind the hybrid samples after aggregration
|
// Unwind the hybrid samples after aggregration
|
||||||
void unwindSamples();
|
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 sampleprof
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,14 @@
|
||||||
|
|
||||||
static cl::OptionCategory ProfGenCategory("ProfGen Options");
|
static cl::OptionCategory ProfGenCategory("ProfGen Options");
|
||||||
|
|
||||||
cl::opt<std::string> PerfTraceFilename(
|
static cl::opt<std::string> PerfScriptFilename(
|
||||||
"perfscript", cl::value_desc("perfscript"), cl::ZeroOrMore,
|
"perfscript", cl::value_desc("perfscript"), cl::ZeroOrMore,
|
||||||
llvm::cl::MiscFlags::CommaSeparated,
|
llvm::cl::MiscFlags::CommaSeparated,
|
||||||
cl::desc("Path of perf-script trace created by Linux perf tool with "
|
cl::desc("Path of perf-script trace created by Linux perf tool with "
|
||||||
"`script` command(the raw perf.data should be profiled with -b)"),
|
"`script` command(the raw perf.data should be profiled with -b)"),
|
||||||
cl::cat(ProfGenCategory));
|
cl::cat(ProfGenCategory));
|
||||||
|
static cl::alias PSA("ps", cl::desc("Alias for --perfscript"),
|
||||||
|
cl::aliasopt(PerfScriptFilename));
|
||||||
|
|
||||||
static cl::opt<std::string> PerfDataFilename(
|
static cl::opt<std::string> PerfDataFilename(
|
||||||
"perfdata", cl::value_desc("perfdata"), cl::ZeroOrMore,
|
"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 "
|
cl::desc("Path of raw perf data created by Linux perf tool (it should be "
|
||||||
"profiled with -b)"),
|
"profiled with -b)"),
|
||||||
cl::cat(ProfGenCategory));
|
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(
|
static cl::opt<std::string> BinaryPath(
|
||||||
"binary", cl::value_desc("binary"), cl::Required,
|
"binary", cl::value_desc("binary"), cl::Required,
|
||||||
|
@ -49,36 +62,34 @@ using namespace sampleprof;
|
||||||
|
|
||||||
// Validate the command line input.
|
// Validate the command line input.
|
||||||
static void validateCommandLine() {
|
static void validateCommandLine() {
|
||||||
// Validate input profile is provided only once
|
// Allow the missing perfscript if we only use to show binary disassembly.
|
||||||
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.
|
|
||||||
if (!ShowDisassemblyOnly) {
|
if (!ShowDisassemblyOnly) {
|
||||||
if (PerfTraceFilename.getNumOccurrences() &&
|
// Validate input profile is provided only once
|
||||||
!llvm::sys::fs::exists(PerfTraceFilename)) {
|
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 =
|
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);
|
exitWithError(Msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PerfDataFilename.getNumOccurrences() &&
|
auto CheckFileExists = [](bool H, StringRef File) {
|
||||||
!llvm::sys::fs::exists(PerfDataFilename)) {
|
if (H && !llvm::sys::fs::exists(File)) {
|
||||||
std::string Msg =
|
std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";
|
||||||
"Input perf data(" + PerfDataFilename + ") doesn't exist.";
|
exitWithError(Msg);
|
||||||
exitWithError(Msg);
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
CheckFileExists(HasPerfData, PerfDataFilename);
|
||||||
|
CheckFileExists(HasPerfScript, PerfScriptFilename);
|
||||||
|
CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!llvm::sys::fs::exists(BinaryPath)) {
|
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[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
InitLLVM X(argc, argv);
|
InitLLVM X(argc, argv);
|
||||||
|
|
||||||
|
@ -113,15 +139,10 @@ int main(int argc, const char *argv[]) {
|
||||||
if (ShowDisassemblyOnly)
|
if (ShowDisassemblyOnly)
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
// Parse perf events and samples
|
PerfInputFile PerfFile = getPerfInputFile();
|
||||||
StringRef PerfInputFile;
|
|
||||||
bool IsPerfData = PerfDataFilename.getNumOccurrences();
|
|
||||||
if (IsPerfData)
|
|
||||||
PerfInputFile = PerfDataFilename;
|
|
||||||
else
|
|
||||||
PerfInputFile = PerfTraceFilename;
|
|
||||||
std::unique_ptr<PerfReaderBase> Reader =
|
std::unique_ptr<PerfReaderBase> Reader =
|
||||||
PerfReaderBase::create(Binary.get(), PerfInputFile, IsPerfData);
|
PerfReaderBase::create(Binary.get(), PerfFile);
|
||||||
|
// Parse perf events and samples
|
||||||
Reader->parsePerfTraces();
|
Reader->parsePerfTraces();
|
||||||
|
|
||||||
if (SkipSymbolization)
|
if (SkipSymbolization)
|
||||||
|
|
Loading…
Reference in New Issue