[Lldb-commits] [lldb] 020fe19 - [lldb] Inherit behaves_like_zeroth_frame in synthesized inline frames (#217377)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Aug 20 00:51:47 PDT 2026
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 (git at github.com: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
More information about the lldb-commits
mailing list