[CSSPGO][llvm-profgen] Truncate stack samples with invalid return address.

Invalid frame addresses exist in call stack samples due to bad unwinding. This could happen to frame-pointer-based unwinding and the callee functions that do not have the frame pointer chain set up. It isn't common when the program is built with the frame pointer omission disabled, but can still happen with third-party static libs built with frame pointer omitted.

Reviewed By: wenlei

Differential Revision: https://reviews.llvm.org/D109638
This commit is contained in:
Hongtao Yu 2021-09-14 16:44:41 -07:00
parent 0dc461441e
commit 0057c7185d
5 changed files with 48 additions and 5 deletions

View File

@ -0,0 +1,12 @@
PERF_RECORD_MMAP2 2854748/2854748: [0x400000(0x1000) @ 0 00:1d 123291722 526021]: r-xp /home/noinline-cs-noprobe.perfbin
// test for invalid return address
4005b0
400686
7f68c5788793
0x40062f/0x4005b0/P/-/-/0 0x400645/0x4005ff/P/-/-/0 0x400637/0x400645/P/-/-/0 0x4005e9/0x400634/P/-/-/0 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 0x400645/0x4005ff/P/-/-/0 0x400637/0x400645/P/-/-/0 0x4005e9/0x400634/P/-/-/0 0x4005d7/0x4005e5/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 0x400645/0x4005ff/P/-/-/0 0x400637/0x400645/P/-/-/0 0x4005e9/0x400634/P/-/-/0 0x4005d7/0x4005e5/P/-/-/0 0x40062f/0x4005b0/P/-/-/0
4005b2
400686
7f68c5788793
0x40062f/0x4005b0/P/-/-/0 0x400645/0x4005ff/P/-/-/0 0x400637/0x400645/P/-/-/0 0x4005e9/0x400634/P/-/-/0 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 0x400645/0x4005ff/P/-/-/0 0x400637/0x400645/P/-/-/0 0x4005e9/0x400634/P/-/-/0 0x4005d7/0x4005e5/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 0x400645/0x4005ff/P/-/-/0 0x400637/0x400645/P/-/-/0 0x4005e9/0x400634/P/-/-/0 0x4005d7/0x4005e5/P/-/-/0 0x40062f/0x4005b0/P/-/-/0

View File

@ -0,0 +1,4 @@
; REQUIRES: x86_64-linux
; 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
; CHECK: warning: Truncated stack sample due to invalid return address at 0x400686, likely caused by frame pointer omission

View File

@ -9,13 +9,15 @@
#include "ProfileGenerator.h"
#include "llvm/Support/FileSystem.h"
#define DEBUG_TYPE "perf-reader"
static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::ReallyHidden,
cl::init(false), cl::ZeroOrMore,
cl::desc("Print binary load events."));
cl::opt<bool> SkipSymbolization("skip-symbolization", cl::ReallyHidden,
cl::init(false), cl::ZeroOrMore,
cl::desc("Dump the unsumbolized profile to the "
cl::desc("Dump the unsymbolized profile to the "
"output file. It will show unwinder "
"output for CS profile generation."));
@ -517,10 +519,17 @@ bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
if (!Binary->addressIsCode(FrameAddr))
break;
// We need to translate return address to call address
// for non-leaf frames
// We need to translate return address to call address for non-leaf frames.
if (!CallStack.empty()) {
FrameAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
auto CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
if (!CallAddr) {
// Stop at an invalid return address caused by bad unwinding. This could
// happen to frame-pointer-based unwinding and the callee functions that
// do not have the frame pointer chain set up.
InvalidReturnAddresses.insert(FrameAddr);
break;
}
FrameAddr = CallAddr;
}
CallStack.emplace_back(FrameAddr);
@ -760,12 +769,22 @@ PerfReaderBase::extractPerfType(cl::list<std::string> &PerfTraceFilenames) {
void HybridPerfReader::generateRawProfile() { unwindSamples(); }
void PerfReaderBase::warnTruncatedStack() {
for (auto Address : InvalidReturnAddresses) {
WithColor::warning()
<< "Truncated stack sample due to invalid return address at "
<< format("0x%" PRIx64, Address)
<< ", likely caused by frame pointer omission\n";
}
}
void PerfReaderBase::parsePerfTraces(
cl::list<std::string> &PerfTraceFilenames) {
// Parse perf traces and do aggregation.
for (auto Filename : PerfTraceFilenames)
parseAndAggregateTrace(Filename);
warnTruncatedStack();
generateRawProfile();
}

View File

@ -594,6 +594,8 @@ protected:
void parseEventOrSample(TraceStream &TraceIt);
// Warn if the relevant mmap event is missing.
void warnIfMissingMMap();
// Emit accumulate warnings.
void warnTruncatedStack();
// Extract call stack from the perf trace lines
bool extractCallstack(TraceStream &TraceIt,
SmallVectorImpl<uint64_t> &CallStack);
@ -619,6 +621,8 @@ protected:
// 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;
};
/*

View File

@ -299,7 +299,11 @@ public:
}
uint64_t getCallAddrFromFrameAddr(uint64_t FrameAddr) const {
return getAddressforIndex(getIndexForAddr(FrameAddr) - 1);
auto I = getIndexForAddr(FrameAddr);
FrameAddr = I ? getAddressforIndex(I - 1) : 0;
if (FrameAddr && addressIsCall(FrameAddr))
return FrameAddr;
return 0;
}
StringRef getFuncFromStartOffset(uint64_t Offset) {