Author: Felipe de Azevedo Piovezan
Date: 2026-08-20T08:51:42+01:00
New Revision: 020fe197085a730753a781c8127e838bd22e6643

URL: 
https://github.com/llvm/llvm-project/commit/020fe197085a730753a781c8127e838bd22e6643
DIFF: 
https://github.com/llvm/llvm-project/commit/020fe197085a730753a781c8127e838bd22e6643.diff

LOG: [lldb] Inherit behaves_like_zeroth_frame in synthesized inline frames 
(#217377)

Inline frames share the concrete frame's PC, so they share its address
convention. Hardcoding false made GetFrameCodeAddressForSymbolication
look up PC-1, resolving sc.symbol to the preceding symbol when the PC is
a function's first instruction.

StackFrameListTest checks that every frame sharing the concrete frame's
PC reports the same address from GetFrameCodeAddressForSymbolication.
Frame 1 reports PC-1 without this change.

The yaml object file was written with help of Claude, and it creates a
backtrace like this:

```
frame #0: 0x000000000000000d inner(x=<unavailable>) at inl.cpp:2:13 [inlined]
frame #1: 0x000000000000000d outer(x=<unavailable>) at inl.cpp:6:27
```

Added: 
    lldb/unittests/Target/Inputs/inlined-function.yaml
    lldb/unittests/Target/StackFrameListTest.cpp

Modified: 
    lldb/source/Target/StackFrameList.cpp
    lldb/unittests/Target/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/source/Target/StackFrameList.cpp 
b/lldb/source/Target/StackFrameList.cpp
index e55ed4c736b9c..b11fdc6ddbf91 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -425,13 +425,15 @@ uint32_t 
StackFrameList::SynthesizeInlineFrames(StackFrameSP frame_sp,
   Address next_frame_address;
   uint32_t num_inlined_frames = 0;
 
+  const bool behaves_like_zeroth_frame = frame_sp->m_behaves_like_zeroth_frame;
+
   while (unwind_sc.GetParentOfInlinedScope(curr_frame_address, next_frame_sc,
                                            next_frame_address)) {
     next_frame_sc.line_entry.ApplyFileMappings(target_sp);
     StackFrameSP inline_frame_sp = std::make_shared<StackFrame>(
         m_thread.shared_from_this(), m_frames.size(), concrete_frame_idx,
         frame_sp->GetRegisterContextSP(), cfa, next_frame_address,
-        /*behaves_like_zeroth_frame=*/false, &next_frame_sc);
+        behaves_like_zeroth_frame, &next_frame_sc);
 
     inline_frame_sp->m_frame_list_id = GetIdentifier();
     m_frames.push_back(inline_frame_sp);

diff  --git a/lldb/unittests/Target/CMakeLists.txt 
b/lldb/unittests/Target/CMakeLists.txt
index fab0b7353d4b7..cd7abf50590ea 100644
--- a/lldb/unittests/Target/CMakeLists.txt
+++ b/lldb/unittests/Target/CMakeLists.txt
@@ -12,6 +12,7 @@ add_lldb_unittest(TargetTests
   RegisterTypeBuilderClangTest.cpp
   RemoteAwarePlatformTest.cpp
   ScratchTypeSystemTest.cpp
+  StackFrameListTest.cpp
   StackFrameRecognizerTest.cpp
   SummaryStatisticsTest.cpp
   TargetAPIMutexTest.cpp
@@ -28,8 +29,11 @@ add_lldb_unittest(TargetTests
       lldbPluginPlatformLinux
       lldbPluginPlatformMacOSX
       lldbPluginPlatformAndroid
+      lldbPluginProcessUtility
       lldbPluginRegisterTypeBuilderClang
+      lldbPluginScriptInterpreterNone
       lldbPluginSymbolFileBreakpad
+      lldbPluginSymbolFileDWARF
       lldbPluginSymbolFileSymtab
       lldbPluginTypeSystemClang
       lldbTarget
@@ -44,5 +48,6 @@ set(test_inputs
   AndroidModule.so.sym
   AndroidModule.unstripped.so
   TestModule.so
+  inlined-function.yaml
   )
 add_unittest_inputs(TargetTests "${test_inputs}")

diff  --git a/lldb/unittests/Target/Inputs/inlined-function.yaml 
b/lldb/unittests/Target/Inputs/inlined-function.yaml
new file mode 100644
index 0000000000000..e766e57716269
--- /dev/null
+++ b/lldb/unittests/Target/Inputs/inlined-function.yaml
@@ -0,0 +1,161 @@
+# Generated from:
+#
+#   __attribute__((always_inline)) static int inner(int x) {
+#     int y = x * 3;
+#     return y + 7;
+#   }
+#
+#   int outer(int x) { return inner(x) + 1; }
+#
+#   clang --target=x86_64-pc-linux -gdwarf-4 -O0 -c inl.cpp -o inl.o
+#   obj2yaml inl.o
+#
+# -O0 keeps the inlined body from being folded away, so the CU contains exactly
+# one DW_TAG_inlined_subroutine. Sections not needed to parse blocks (.comment,
+# .note.GNU-stack, .eh_frame, .llvm_addrsig) have been dropped by hand.
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+  SectionHeaderStringTable: .strtab
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x10
+    Content:         
554889E5897DF48B45F48945FC6B45FC038945F88B45F883C00783C0015DC3
+  - Name:            .debug_abbrev
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x1
+    Content:         
011101250E1305030E10171B0E110112060000022E016E0E030E3A0B3B0B4913200B0000030500030E3A0B3B0B49130000043400030E3A0B3B0B49130000052400030E3E0B0B0B0000062E011101120640186E0E030E3A0B3B0B49133F1900000705000218030E3A0B3B0B49130000081D01311311011206580B590B570B00000905000218311300000A340002183113000000
+  - Name:            .debug_info
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x1
+    Content:         
A6000000040000000000080100000000210000000000000000000000000000000000000000001F000000020000000000000000010151000000010300000000010151000000040000000001025100000000050000000005040600000000000000001F000000015600000000000000000106510000000702917400000000010651000000082A00000000000000000000000D00000001061B0902917C3A0000000A02917845000000000000
+  - Name:            .debug_line
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x1
+    Content:         
5200000004001F000000010101FB0E0D00010101010000000100000100696E6C2E637070000000000000090200000000000000001705210A74050D620507064A050A063D050C063C0524063F0514060B3C0202000101
+  - Name:            .rela.debug_info
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .debug_info
+    Relocations:
+      - Offset:          0x6
+        Symbol:          .debug_abbrev
+        Type:            R_X86_64_32
+      - Offset:          0xC
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+      - Offset:          0x12
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          102
+      - Offset:          0x16
+        Symbol:          .debug_line
+        Type:            R_X86_64_32
+      - Offset:          0x1A
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          110
+      - Offset:          0x1E
+        Symbol:          .text
+        Type:            R_X86_64_64
+      - Offset:          0x2B
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          115
+      - Offset:          0x2F
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          126
+      - Offset:          0x3B
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          136
+      - Offset:          0x46
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          138
+      - Offset:          0x52
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          132
+      - Offset:          0x59
+        Symbol:          .text
+        Type:            R_X86_64_64
+      - Offset:          0x67
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          140
+      - Offset:          0x6B
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          150
+      - Offset:          0x79
+        Symbol:          .debug_str
+        Type:            R_X86_64_32
+        Addend:          136
+      - Offset:          0x88
+        Symbol:          .text
+        Type:            R_X86_64_64
+        Addend:          13
+  - Name:            .rela.debug_line
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .debug_line
+    Relocations:
+      - Offset:          0x2C
+        Symbol:          .text
+        Type:            R_X86_64_64
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .strtab
+      - Name:            .text
+      - Name:            .debug_abbrev
+      - Name:            .debug_info
+      - Name:            .rela.debug_info
+      - Name:            .debug_str
+      - Name:            .debug_line
+      - Name:            .rela.debug_line
+      - Name:            .symtab
+Symbols:
+  - Name:            inl.cpp
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+  - Name:            .debug_abbrev
+    Type:            STT_SECTION
+    Section:         .debug_abbrev
+  - Name:            .debug_str
+    Type:            STT_SECTION
+    Section:         .debug_str
+  - Name:            .debug_line
+    Type:            STT_SECTION
+    Section:         .debug_line
+  - Name:            _Z5outeri
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Size:            0x1F
+DWARF:
+  debug_str:
+    - 'clang version 21.0.0 ([email protected]:apple/llvm-project.git 
93469d2c8423636a21cf023a0994c63f526b6432)'
+    - inl.cpp
+    - '/tmp'
+    - _ZL5inneri
+    - inner
+    - int
+    - x
+    - y
+    - _Z5outeri
+    - outer
+...

diff  --git a/lldb/unittests/Target/StackFrameListTest.cpp 
b/lldb/unittests/Target/StackFrameListTest.cpp
new file mode 100644
index 0000000000000..e9bdf32472516
--- /dev/null
+++ b/lldb/unittests/Target/StackFrameListTest.cpp
@@ -0,0 +1,121 @@
+//===-- StackFrameListTest.cpp 
--------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "gtest/gtest.h"
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class DummyProcess : public Process {
+public:
+  DummyProcess(TargetSP target_sp, ListenerSP listener_sp)
+      : Process(target_sp, listener_sp) {}
+  bool CanDebug(TargetSP, bool) override { return true; }
+  Status DoDestroy() override { return {}; }
+  void RefreshStateAfterStop() override {}
+  size_t DoReadMemory(const ProcessAddress &, void *, size_t,
+                      Status &) override {
+    return 0;
+  }
+  bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; 
}
+  llvm::StringRef GetPluginName() override { return "Dummy"; }
+};
+
+class StackFrameListTest : public ::testing::Test {
+  SubsystemRAII<FileSystem, HostInfo, TypeSystemClang, ObjectFileELF,
+                plugin::dwarf::SymbolFileDWARF, platform_linux::PlatformLinux,
+                ScriptInterpreterNone>
+      subsystems;
+
+public:
+  void SetUp() override {
+    std::call_once(TestUtilities::g_debugger_initialize_flag,
+                   []() { Debugger::Initialize(nullptr); });
+    ArchSpec arch("x86_64-pc-linux");
+    PlatformSP platform_sp =
+        platform_linux::PlatformLinux::CreateInstance(true, &arch);
+    ASSERT_TRUE(platform_sp);
+    Platform::SetHostPlatform(platform_sp);
+
+    DebuggerSP debugger_sp = Debugger::CreateInstance();
+    ASSERT_TRUE(debugger_sp);
+    PlatformSP target_platform_sp;
+    ASSERT_TRUE(debugger_sp->GetTargetList()
+                    .CreateTarget(*debugger_sp, "", arch, eLoadDependentsNo,
+                                  target_platform_sp, m_target_sp)
+                    .Success());
+    ASSERT_TRUE(m_target_sp);
+
+    llvm::Expected<TestFile> file =
+        TestFile::fromYamlFile("inlined-function.yaml");
+    ASSERT_THAT_EXPECTED(file, llvm::Succeeded());
+    m_file.emplace(std::move(*file));
+    ModuleSP module_sp = std::make_shared<Module>(m_file->moduleSpec());
+    ASSERT_TRUE(module_sp);
+    m_target_sp->GetImages().Append(module_sp);
+    bool changed = false;
+    ASSERT_TRUE(module_sp->SetLoadAddress(*m_target_sp, 0,
+                                          /*value_is_offset=*/true, changed));
+  }
+
+protected:
+  std::optional<TestFile> m_file;
+  TargetSP m_target_sp;
+};
+
+// Frames synthesized for an inlined scope share the concrete frame's PC, they
+// should also share its "behaves like frame zero" behavior.
+TEST_F(StackFrameListTest, InlineFramesInheritZerothFrameSymbolication) {
+  // outer() inlines inner() starting at this offset into .text; see the
+  // generation recipe in Inputs/inlined-function.yaml.
+  const addr_t pc = 0xd;
+
+  ListenerSP listener_sp(Listener::MakeListener("dummy"));
+  ProcessSP process_sp =
+      std::make_shared<DummyProcess>(m_target_sp, listener_sp);
+  ASSERT_TRUE(process_sp);
+  // HistoryPCType::Returns makes frame zero behave like the zeroth frame.
+  ThreadSP thread_sp = std::make_shared<HistoryThread>(
+      *process_sp, /*tid=*/0x1234, std::vector<addr_t>{pc});
+
+  // An inline chain requires more than the concrete frame.
+  ASSERT_GT(thread_sp->GetStackFrameCount(), 1u)
+      << "no inline frames synthesized at pc " << pc;
+
+  // The YAML object file creates a backtrace like this:
+  //   frame #0: 0x000000000000000d inner() at inl.cpp:2:13 [inlined]
+  //   frame #1: 0x000000000000000d outer() at inl.cpp:6:27
+
+  for (uint32_t i = 0; i < thread_sp->GetStackFrameCount(); ++i) {
+    StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(i);
+    ASSERT_TRUE(frame_sp);
+    if (frame_sp->GetFrameCodeAddress().GetFileAddress() != pc)
+      continue;
+    EXPECT_EQ(frame_sp->GetFrameCodeAddressForSymbolication().GetFileAddress(),
+              frame_sp->GetFrameCodeAddress().GetFileAddress())
+        << "frame " << i << " symbolicates at an adjusted address";
+  }
+}
+} // namespace


        
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to