[llvm] 28fec94 - [llvm-profgen] Fix bogus trace check (#225569)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 18:09:19 PDT 2026


Author: Amir Ayupov
Date: 2026-09-23T18:09:10-07:00
New Revision: 28fec94991905d8dd8a08658e9590e13b7fe1af5

URL: https://github.com/llvm/llvm-project/commit/28fec94991905d8dd8a08658e9590e13b7fe1af5
DIFF: https://github.com/llvm/llvm-project/commit/28fec94991905d8dd8a08658e9590e13b7fe1af5.diff

LOG: [llvm-profgen] Fix bogus trace check (#225569)

Added: 
    llvm/test/tools/llvm-profgen/AArch64/cs-bogus-trace.test

Modified: 
    llvm/test/tools/llvm-profgen/lit.local.cfg
    llvm/tools/llvm-profgen/PerfReader.cpp
    llvm/tools/llvm-profgen/PerfReader.h

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-profgen/AArch64/cs-bogus-trace.test b/llvm/test/tools/llvm-profgen/AArch64/cs-bogus-trace.test
new file mode 100644
index 0000000000000..2b2eb64a861f5
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/AArch64/cs-bogus-trace.test
@@ -0,0 +1,160 @@
+# REQUIRES: lld
+# RUN: split-file %s %t
+# RUN: llvm-mc -triple=aarch64 -filetype=obj %t/a.s -o %t/a.o
+# RUN: ld.lld --image-base=0 -Ttext=0x1000 -e main %t/a.o -o %t/cs-bogus-trace.exe
+# RUN: llvm-profgen --binary=%t/cs-bogus-trace.exe --perfscript=%t/perfscript \
+# RUN:   --skip-symbolization --format=text --output=%t/profile \
+# RUN:   --show-detailed-warning 2>&1 \
+# RUN:   | FileCheck %s --implicit-check-not="Bogus trace"
+# RUN: llvm-profgen --binary=%t/cs-bogus-trace.exe --perfscript=%t/perfscript-clean \
+# RUN:   --skip-symbolization --format=text --output=%t/profile-clean \
+# RUN:   --show-detailed-warning 2>&1 \
+# RUN:   | FileCheck %s --check-prefix=CLEAN --allow-empty
+
+## A hybrid sample is kept only if the callchain leaf (sampled pc) and the
+## newest LBR target lie in the same function, or either is external. The leaf
+## is then canonicalized to the LBR target, so skid within one function is
+## harmless.
+
+## Cross-function skid is rejected.
+# CHECK: Bogus trace: stack tip = 0x00001088, LBR tip = 0x00001000
+# CHECK: Bogus trace: stack tip = 0x00001008, LBR tip = 0x00001094
+# CHECK: Bogus trace: stack tip = 0x00001088, LBR tip = 0x00001060
+# CHECK: 33.33%(3/9) of hybrid samples had a callchain leaf that disagreed with the newest LBR target (bogus trace).
+# CHECK: Bogus trace rate exceeds 1%: the profile has high sample skid and may not be suitable for optimization.
+
+## No warnings when every sample is kept.
+# CLEAN-NOT: Bogus trace
+
+#--- a.s
+.macro func name
+  .globl \name
+  .type \name,%function
+\name:
+.endm
+
+.macro endfunc name
+  .size \name, .-\name
+.endm
+
+  .text
+func g                    // 0x1000
+  nop
+  nop
+  nop
+  nop
+  nop
+  nop
+  nop
+  ret                     // 0x101c
+endfunc g
+
+func r                    // 0x1020
+  nop
+  nop
+  nop
+  bl r                    // 0x102c: r -> r
+  nop
+  nop
+  nop
+  ret
+endfunc r
+
+func main                 // 0x1040
+  nop
+  nop
+  bl f                    // 0x1048: main -> f
+  nop
+  bl r                    // 0x1050: main -> r
+  nop
+  nop
+  ret
+endfunc main
+
+func h                    // 0x1060
+  nop
+  nop
+  nop
+  nop
+  nop
+  nop
+  nop
+  ret
+endfunc h
+
+func f                    // 0x1080
+  nop
+  nop
+  nop
+  nop
+  bl g                    // 0x1090: f -> g
+  nop
+  nop
+  b h                     // 0x109c: f -> h (tail call)
+  .space 0x160            // make f larger than 256 bytes
+endfunc f
+
+// profgen takes function ranges from symbol sizes only in pseudo-probe mode.
+  .section .pseudo_probe_desc,"",%progbits
+  .section .pseudo_probe,"",%progbits
+
+#--- perfscript
+PERF_RECORD_MMAP2 1/1: [0x0(0x2000) @ 0 00:00 0 0]: r-xp /tmp/cs-bogus-trace.exe
+
+; Small positive skid: pc 8 bytes past the LBR target in f. Kept.
+	            1088
+	            104c
+ 0x1048/0x1080/P/-/-/0
+
+; Positive skid of 260 bytes within f, beyond the old 0x100 cap. Kept.
+	            1184
+	            104c
+ 0x1048/0x1080/P/-/-/0
+
+; Negative skid within f: the LBR is newer than the pc (g returned to f).
+; The frames above the leaf are unchanged. Kept.
+	            1088
+	            104c
+ 0x101c/0x1094/P/-/-/0  0x1090/0x1000/P/-/-/0  0x1048/0x1080/P/-/-/0
+
+; Skid across a call: pc in f, newest branch is f -> g. Dropped.
+	            1088
+	            104c
+ 0x1090/0x1000/P/-/-/0  0x1048/0x1080/P/-/-/0
+
+; Skid across a return: pc in g, newest branch is g returning to f. Dropped.
+	            1008
+	            1094
+	            104c
+ 0x101c/0x1094/P/-/-/0  0x1090/0x1000/P/-/-/0  0x1048/0x1080/P/-/-/0
+
+; Skid across a tail call: pc in f, newest branch is f -> h. Dropped.
+	            1088
+	            104c
+ 0x109c/0x1060/P/-/-/0  0x1048/0x1080/P/-/-/0
+
+; Skid across a recursive call: pc in the inner r, but the LBR is older and
+; its newest branch is main -> outer r. Both are in r, so this is not caught.
+	            1028
+	            1030
+	            1054
+ 0x1050/0x1020/P/-/-/0
+
+; External leaf: pc in a library called from f. Not checked. Kept.
+	    7f0000001000
+	            1094
+	            104c
+ 0x1048/0x1080/P/-/-/0
+
+; External LBR target: newest branch leaves the binary. Not checked. Kept.
+	            1088
+	            104c
+ 0x1090/0x7f0000001000/P/-/-/0  0x1048/0x1080/P/-/-/0
+
+#--- perfscript-clean
+PERF_RECORD_MMAP2 1/1: [0x0(0x2000) @ 0 00:00 0 0]: r-xp /tmp/cs-bogus-trace.exe
+
+; Small positive skid within f. Kept.
+	            1088
+	            104c
+ 0x1048/0x1080/P/-/-/0

diff  --git a/llvm/test/tools/llvm-profgen/lit.local.cfg b/llvm/test/tools/llvm-profgen/lit.local.cfg
index 860e1e9557a7f..a0316aa239b40 100644
--- a/llvm/test/tools/llvm-profgen/lit.local.cfg
+++ b/llvm/test/tools/llvm-profgen/lit.local.cfg
@@ -1,4 +1,9 @@
+from lit.llvm import llvm_config
+
 config.suffixes = [".test", ".ll", ".s", ".yaml"]
 
 # Allow tests to prepend a mock directory onto the real PATH.
 config.substitutions.append(("%{PATH}", config.environment["PATH"]))
+
+if llvm_config.use_lld(required=False):
+    config.available_features.add("lld")

diff  --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 18297d5f7665b..4c266b4f3d9aa 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -300,11 +300,6 @@ bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
   // Capture initial state as starting point for unwinding.
   UnwindState State(Sample, Binary);
 
-  // Sanity check - making sure leaf of LBR aligns with leaf of stack sample
-  // Stack sample sometimes can be unreliable, so filter out bogus ones.
-  if (!State.validateInitialState())
-    return false;
-
   NumTotalBranches += State.LBRStack.size();
   // Now process the LBR samples in parrallel with stack sample
   // Note that we do not reverse the LBR entry order so we can
@@ -722,6 +717,14 @@ void HybridPerfReader::unwindSamples() {
                      Unwinder.NumExtCallBranch,
                      "of artificial call branches but doesn't have an external "
                      "frame to match.");
+
+  emitWarningSummary(NumBogusTrace, NumTotalHybridSample,
+                     "of hybrid samples had a callchain leaf that disagreed "
+                     "with the newest LBR target (bogus trace).");
+  if (NumBogusTrace * 100 > NumTotalHybridSample)
+    WithColor::warning() << "Bogus trace rate exceeds 1%: the profile has high "
+                            "sample skid and may not be suitable for "
+                            "optimization.\n";
 }
 
 /// Parse a hex address from \p Str.
@@ -881,6 +884,15 @@ void PerfScriptReader::warnIfMissingMMap() {
   }
 }
 
+// The unwinder requires that LBR tip belong to the leaf frame.
+// External addresses are not checked.
+static bool isValidTrace(ProfiledBinary *Binary, uint64_t StackLeaf,
+                         uint64_t LBRLeaf) {
+  if (StackLeaf == ExternalAddr || LBRLeaf == ExternalAddr)
+    return true;
+  return Binary->findFuncRange(LBRLeaf) == Binary->findFuncRange(StackLeaf);
+}
+
 void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
   // The raw hybird sample started with call stack in FILO order and followed
   // intermediately by LBR sample
@@ -911,6 +923,19 @@ void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
       if (IgnoreStackSamples) {
         Sample->CallStack.clear();
       } else {
+        NumTotalHybridSample++;
+        // Drop samples whose callchain and LBR disagree before the
+        // canonicalization below hides the disagreement.
+        uint64_t StackLeaf = Sample->CallStack.front();
+        uint64_t LBRLeaf = Sample->LBRStack[0].Target;
+        if (!isValidTrace(Binary, StackLeaf, LBRLeaf)) {
+          NumBogusTrace++;
+          if (ShowDetailedWarning)
+            WithColor::warning()
+                << "Bogus trace: stack tip = " << format("%#010x", StackLeaf)
+                << ", LBR tip = " << format("%#010x\n", LBRLeaf);
+          return;
+        }
         // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR
         // ranges
         Sample->CallStack.front() = Sample->LBRStack[0].Target;

diff  --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index b9af0f19cb5d3..b6e47b7c71d9f 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -272,24 +272,6 @@ struct UnwindState {
     initFrameTrie(Sample->CallStack);
   }
 
-  bool validateInitialState() {
-    uint64_t LBRLeaf = LBRStack[LBRIndex].Target;
-    uint64_t LeafAddr = CurrentLeafFrame->Address;
-    assert((LBRLeaf != ExternalAddr || LBRLeaf == LeafAddr) &&
-           "External leading LBR should match the leaf frame.");
-
-    // When we take a stack sample, ideally the sampling distance between the
-    // leaf IP of stack and the last LBR target shouldn't be very large.
-    // Use a heuristic size (0x100) to filter out broken records.
-    if (LeafAddr < LBRLeaf || LeafAddr - LBRLeaf >= 0x100) {
-      WithColor::warning() << "Bogus trace: stack tip = "
-                           << format("%#010x", LeafAddr)
-                           << ", LBR tip = " << format("%#010x\n", LBRLeaf);
-      return false;
-    }
-    return true;
-  }
-
   void checkStateConsistency() {
     assert(InstPtr.Address == CurrentLeafFrame->Address &&
            "IP should align with context leaf");
@@ -716,6 +698,9 @@ class HybridPerfReader : public PerfScriptReader {
 private:
   // Unwind the hybrid samples after aggregration
   void unwindSamples();
+
+  uint64_t NumBogusTrace = 0;
+  uint64_t NumTotalHybridSample = 0;
 };
 
 /*


        


More information about the llvm-commits mailing list