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
