Skip to content

Commit

Permalink
[CSSPGO][llvm-profgen] Truncate stack samples with invalid return add…
Browse files Browse the repository at this point in the history
…ress.

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
  • Loading branch information
htyu committed Sep 15, 2021
1 parent 0dc4614 commit 0057c71
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 5 deletions.
12 changes: 12 additions & 0 deletions llvm/test/tools/llvm-profgen/Inputs/cs-invalid-ret-addr.perfscript
@@ -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
4 changes: 4 additions & 0 deletions llvm/test/tools/llvm-profgen/cs-invalid-ret-addr.test
@@ -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
27 changes: 23 additions & 4 deletions llvm/tools/llvm-profgen/PerfReader.cpp
Expand Up @@ -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."));

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
}

Expand Down
4 changes: 4 additions & 0 deletions llvm/tools/llvm-profgen/PerfReader.h
Expand Up @@ -594,6 +594,8 @@ class PerfReaderBase {
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);
Expand All @@ -619,6 +621,8 @@ class PerfReaderBase {
// 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;
};

/*
Expand Down
6 changes: 5 additions & 1 deletion llvm/tools/llvm-profgen/ProfiledBinary.h
Expand Up @@ -299,7 +299,11 @@ class ProfiledBinary {
}

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) {
Expand Down

0 comments on commit 0057c71

Please sign in to comment.