[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;
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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