[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:
wlei 2021-10-14 20:18:53 -07:00
parent 9bbfe0f72c
commit a5f411b7f8
7 changed files with 321 additions and 165 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)