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

Hongtao Yu via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 14 21:56:36 PDT 2021


Author: Hongtao Yu
Date: 2021-09-14T21:56:22-07:00
New Revision: 0057c7185d1cdbcfdaa9385f4b67e677066af8b8

URL: https://github.com/llvm/llvm-project/commit/0057c7185d1cdbcfdaa9385f4b67e677066af8b8
DIFF: https://github.com/llvm/llvm-project/commit/0057c7185d1cdbcfdaa9385f4b67e677066af8b8.diff

LOG: [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

Added: 
    llvm/test/tools/llvm-profgen/Inputs/cs-invalid-ret-addr.perfscript
    llvm/test/tools/llvm-profgen/cs-invalid-ret-addr.test

Modified: 
    llvm/tools/llvm-profgen/PerfReader.cpp
    llvm/tools/llvm-profgen/PerfReader.h
    llvm/tools/llvm-profgen/ProfiledBinary.h

Removed: 
    


################################################################################
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
index 0000000000000..63ba27822e26d
--- /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
index 0000000000000..3eac05e65f8bf
--- /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.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 259b23f33d47b..f892d6bf4a2a4 100644
--- 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<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();
 }
 

diff  --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index 2babcd7246a8b..c281e76c96379 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -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);
@@ -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;
 };
 
 /*

diff  --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h
index 4a3bb561f488e..fabc73d3b5675 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -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) {


        


More information about the llvm-commits mailing list