diff --git a/llvm/test/tools/llvm-profgen/Inputs/cs-invalid-ret-addr.perfscript b/llvm/test/tools/llvm-profgen/Inputs/cs-invalid-ret-addr.perfscript new file mode 100644 --- /dev/null +++ b/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 diff --git a/llvm/test/tools/llvm-profgen/cs-invalid-ret-addr.test b/llvm/test/tools/llvm-profgen/cs-invalid-ret-addr.test new file mode 100644 --- /dev/null +++ b/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 diff --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h --- a/llvm/tools/llvm-profgen/PerfReader.h +++ b/llvm/tools/llvm-profgen/PerfReader.h @@ -594,6 +594,8 @@ 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 &CallStack); @@ -619,6 +621,8 @@ // 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 InvalidReturnAddresses; }; /* diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp --- a/llvm/tools/llvm-profgen/PerfReader.cpp +++ b/llvm/tools/llvm-profgen/PerfReader.cpp @@ -9,13 +9,15 @@ #include "ProfileGenerator.h" #include "llvm/Support/FileSystem.h" +#define DEBUG_TYPE "perf-reader" + static cl::opt ShowMmapEvents("show-mmap-events", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore, cl::desc("Print binary load events.")); cl::opt 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 @@ 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 @@ 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 &PerfTraceFilenames) { // Parse perf traces and do aggregation. for (auto Filename : PerfTraceFilenames) parseAndAggregateTrace(Filename); + warnTruncatedStack(); generateRawProfile(); } diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h --- a/llvm/tools/llvm-profgen/ProfiledBinary.h +++ b/llvm/tools/llvm-profgen/ProfiledBinary.h @@ -299,7 +299,11 @@ } 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) {