Skip to content

Commit 138202a

Browse files
committed
[llvm-profgen] Warn on invalid range and show warning summary
Two things in this diff: 1) Warn on the invalid range, currently three types of checking, see the detailed message in the code. 2) In some situation, llvm-profgen gives lots of warnings on the truncated stacks which is noisy. This change provides a switch to `--show-detailed-warning` to skip the warnings. Alternatively, we use a summary for those warning and show the percentage of cases with those issues. Example of warning summary. ``` warning: 0.05%(1120/2428958) cases with issue: Profile context truncated due to missing probe for call instruction. warning: 0.00%(2/178637) cases with issue: Range does not belong to any functions, likely from external function. ``` Reviewed By: hoy Differential Revision: https://reviews.llvm.org/D111902
1 parent 57e0931 commit 138202a

File tree

5 files changed

+137
-18
lines changed

5 files changed

+137
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
; REQUIRES: x86_64-linux
2-
; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/cs-invalid-ret-addr.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t 2>&1 | FileCheck %s
2+
; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/cs-invalid-ret-addr.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --show-detailed-warning 2>&1 | FileCheck %s
33

44
; CHECK: warning: Truncated stack sample due to invalid return address at 0x400686, likely caused by frame pointer omission

llvm/tools/llvm-profgen/PerfReader.cpp

+113-9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ static cl::opt<bool>
3131
IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore,
3232
cl::desc("Ignore call stack samples for hybrid samples "
3333
"and produce context-insensitive profile."));
34+
static cl::opt<bool>
35+
ShowDetailedWarning("show-detailed-warning", cl::init(false),
36+
cl::ZeroOrMore,
37+
cl::desc("Show detailed warning message."));
3438

3539
extern cl::opt<std::string> PerfTraceFilename;
3640
extern cl::opt<bool> ShowDisassemblyOnly;
@@ -433,10 +437,16 @@ void HybridPerfReader::unwindSamples() {
433437
}
434438

435439
// Warn about untracked frames due to missing probes.
436-
for (auto Address : AllUntrackedCallsites)
437-
WithColor::warning() << "Profile context truncated due to missing probe "
438-
<< "for call instruction at "
439-
<< format("0x%" PRIx64, Address) << "\n";
440+
if (ShowDetailedWarning) {
441+
for (auto Address : AllUntrackedCallsites)
442+
WithColor::warning() << "Profile context truncated due to missing probe "
443+
<< "for call instruction at "
444+
<< format("0x%" PRIx64, Address) << "\n";
445+
}
446+
447+
emitWarningSummary(AllUntrackedCallsites.size(), SampleCounters.size(),
448+
"of profiled contexts are truncated due to missing probe "
449+
"for call instruction.");
440450
}
441451

442452
bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
@@ -1008,12 +1018,105 @@ void HybridPerfReader::generateUnsymbolizedProfile() {
10081018
}
10091019

10101020
void PerfScriptReader::warnTruncatedStack() {
1011-
for (auto Address : InvalidReturnAddresses) {
1012-
WithColor::warning()
1013-
<< "Truncated stack sample due to invalid return address at "
1014-
<< format("0x%" PRIx64, Address)
1015-
<< ", likely caused by frame pointer omission\n";
1021+
if (ShowDetailedWarning) {
1022+
for (auto Address : InvalidReturnAddresses) {
1023+
WithColor::warning()
1024+
<< "Truncated stack sample due to invalid return address at "
1025+
<< format("0x%" PRIx64, Address)
1026+
<< ", likely caused by frame pointer omission\n";
1027+
}
1028+
}
1029+
emitWarningSummary(
1030+
InvalidReturnAddresses.size(), AggregatedSamples.size(),
1031+
"of truncated stack samples due to invalid return address, "
1032+
"likely caused by frame pointer omission.");
1033+
}
1034+
1035+
void PerfScriptReader::emitWarningSummary(uint64_t Num, uint64_t Total,
1036+
StringRef Msg) {
1037+
if (!Total || !Num)
1038+
return;
1039+
WithColor::warning() << format("%.2f", static_cast<double>(Num) * 100 / Total)
1040+
<< "%(" << Num << "/" << Total << ") " << Msg << "\n";
1041+
}
1042+
1043+
void PerfScriptReader::warnInvalidRange() {
1044+
std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t,
1045+
pair_hash<uint64_t, uint64_t>>
1046+
Ranges;
1047+
1048+
for (const auto &Item : AggregatedSamples) {
1049+
const PerfSample *Sample = Item.first.getPtr();
1050+
uint64_t Count = Item.second;
1051+
uint64_t EndOffeset = 0;
1052+
for (const LBREntry &LBR : Sample->LBRStack) {
1053+
uint64_t SourceOffset = Binary->virtualAddrToOffset(LBR.Source);
1054+
uint64_t StartOffset = Binary->virtualAddrToOffset(LBR.Target);
1055+
if (EndOffeset != 0)
1056+
Ranges[{StartOffset, EndOffeset}] += Count;
1057+
EndOffeset = SourceOffset;
1058+
}
10161059
}
1060+
1061+
if (Ranges.empty()) {
1062+
WithColor::warning() << "No samples in perf script!\n";
1063+
return;
1064+
}
1065+
1066+
auto WarnInvalidRange =
1067+
[&](uint64_t StartOffset, uint64_t EndOffset, StringRef Msg) {
1068+
if (!ShowDetailedWarning)
1069+
return;
1070+
WithColor::warning()
1071+
<< "["
1072+
<< format("%8" PRIx64, Binary->offsetToVirtualAddr(StartOffset))
1073+
<< ","
1074+
<< format("%8" PRIx64, Binary->offsetToVirtualAddr(EndOffset))
1075+
<< "]: " << Msg << "\n";
1076+
};
1077+
1078+
const char *EndNotBoundaryMsg = "Range is not on instruction boundary, "
1079+
"likely due to profile and binary mismatch.";
1080+
const char *DanglingRangeMsg = "Range does not belong to any functions, "
1081+
"likely from PLT, .init or .fini section.";
1082+
const char *RangeCrossFuncMsg =
1083+
"Fall through range should not cross function boundaries, likely due to "
1084+
"profile and binary mismatch.";
1085+
1086+
uint64_t InstNotBoundary = 0;
1087+
uint64_t UnmatchedRange = 0;
1088+
uint64_t RangeCrossFunc = 0;
1089+
1090+
for (auto &I : Ranges) {
1091+
uint64_t StartOffset = I.first.first;
1092+
uint64_t EndOffset = I.first.second;
1093+
1094+
if (!Binary->offsetIsCode(StartOffset) ||
1095+
!Binary->offsetIsTransfer(EndOffset)) {
1096+
InstNotBoundary++;
1097+
WarnInvalidRange(StartOffset, EndOffset, EndNotBoundaryMsg);
1098+
}
1099+
1100+
auto *FRange = Binary->findFuncRangeForOffset(StartOffset);
1101+
if (!FRange) {
1102+
UnmatchedRange++;
1103+
WarnInvalidRange(StartOffset, EndOffset, DanglingRangeMsg);
1104+
continue;
1105+
}
1106+
1107+
if (EndOffset >= FRange->EndOffset) {
1108+
RangeCrossFunc++;
1109+
WarnInvalidRange(StartOffset, EndOffset, RangeCrossFuncMsg);
1110+
}
1111+
}
1112+
1113+
uint64_t TotalRangeNum = Ranges.size();
1114+
emitWarningSummary(InstNotBoundary, TotalRangeNum,
1115+
"of profiled ranges are not on instruction boundary.");
1116+
emitWarningSummary(UnmatchedRange, TotalRangeNum,
1117+
"of profiled ranges do not belong to any functions.");
1118+
emitWarningSummary(RangeCrossFunc, TotalRangeNum,
1119+
"of profiled ranges do cross function boundaries.");
10171120
}
10181121

10191122
void PerfScriptReader::parsePerfTraces() {
@@ -1022,6 +1125,7 @@ void PerfScriptReader::parsePerfTraces() {
10221125

10231126
// Generate unsymbolized profile.
10241127
warnTruncatedStack();
1128+
warnInvalidRange();
10251129
generateUnsymbolizedProfile();
10261130

10271131
if (SkipSymbolization)

llvm/tools/llvm-profgen/PerfReader.h

+3
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,13 @@ class PerfScriptReader : public PerfReaderBase {
581581
void parseAndAggregateTrace();
582582
// Parse either an MMAP event or a perf sample
583583
void parseEventOrSample(TraceStream &TraceIt);
584+
void emitWarningSummary(uint64_t Num, uint64_t Total, StringRef Msg);
584585
// Warn if the relevant mmap event is missing.
585586
void warnIfMissingMMap();
586587
// Emit accumulate warnings.
587588
void warnTruncatedStack();
589+
// Warn if range is invalid.
590+
void warnInvalidRange();
588591
// Extract call stack from the perf trace lines
589592
bool extractCallstack(TraceStream &TraceIt,
590593
SmallVectorImpl<uint64_t> &CallStack);

llvm/tools/llvm-profgen/ProfiledBinary.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void ProfiledBinary::load() {
187187

188188
// Use function start and return address to infer prolog and epilog
189189
ProEpilogTracker.inferPrologOffsets(StartOffset2FuncRangeMap);
190-
ProEpilogTracker.inferEpilogOffsets(RetAddrs);
190+
ProEpilogTracker.inferEpilogOffsets(RetOffsets);
191191

192192
// TODO: decode other sections.
193193
}
@@ -397,9 +397,11 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
397397
// Populate address maps.
398398
CodeAddrOffsets.push_back(Offset);
399399
if (MCDesc.isCall())
400-
CallAddrs.insert(Offset);
400+
CallOffsets.insert(Offset);
401401
else if (MCDesc.isReturn())
402-
RetAddrs.insert(Offset);
402+
RetOffsets.insert(Offset);
403+
else if (MCDesc.isBranch())
404+
BranchOffsets.insert(Offset);
403405

404406
if (InvalidInstLength) {
405407
WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1);

llvm/tools/llvm-profgen/ProfiledBinary.h

+15-5
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,11 @@ class ProfiledBinary {
204204
// sorting is needed to fast advance to the next forward/backward instruction.
205205
std::vector<uint64_t> CodeAddrOffsets;
206206
// A set of call instruction offsets. Used by virtual unwinding.
207-
std::unordered_set<uint64_t> CallAddrs;
207+
std::unordered_set<uint64_t> CallOffsets;
208208
// A set of return instruction offsets. Used by virtual unwinding.
209-
std::unordered_set<uint64_t> RetAddrs;
209+
std::unordered_set<uint64_t> RetOffsets;
210+
// A set of branch instruction offsets.
211+
std::unordered_set<uint64_t> BranchOffsets;
210212

211213
// Estimate and track function prolog and epilog ranges.
212214
PrologEpilogTracker ProEpilogTracker;
@@ -305,23 +307,31 @@ class ProfiledBinary {
305307
return TextSegmentOffsets;
306308
}
307309

310+
bool offsetIsCode(uint64_t Offset) const {
311+
return Offset2InstSizeMap.find(Offset) != Offset2InstSizeMap.end();
312+
}
308313
bool addressIsCode(uint64_t Address) const {
309314
uint64_t Offset = virtualAddrToOffset(Address);
310-
return Offset2InstSizeMap.find(Offset) != Offset2InstSizeMap.end();
315+
return offsetIsCode(Offset);
311316
}
312317
bool addressIsCall(uint64_t Address) const {
313318
uint64_t Offset = virtualAddrToOffset(Address);
314-
return CallAddrs.count(Offset);
319+
return CallOffsets.count(Offset);
315320
}
316321
bool addressIsReturn(uint64_t Address) const {
317322
uint64_t Offset = virtualAddrToOffset(Address);
318-
return RetAddrs.count(Offset);
323+
return RetOffsets.count(Offset);
319324
}
320325
bool addressInPrologEpilog(uint64_t Address) const {
321326
uint64_t Offset = virtualAddrToOffset(Address);
322327
return ProEpilogTracker.PrologEpilogSet.count(Offset);
323328
}
324329

330+
bool offsetIsTransfer(uint64_t Offset) {
331+
return BranchOffsets.count(Offset) || RetOffsets.count(Offset) ||
332+
CallOffsets.count(Offset);
333+
}
334+
325335
uint64_t getAddressforIndex(uint64_t Index) const {
326336
return offsetToVirtualAddr(CodeAddrOffsets[Index]);
327337
}

0 commit comments

Comments
 (0)