https://github.com/firmiana402 created 
https://github.com/llvm/llvm-project/pull/219213

`SymbolFileDWARF::CollectCallEdges` only scanned direct children of a concrete 
subprogram DIE. GCC can place a call made from an inlined function under the 
corresponding `DW_TAG_inlined_subroutine`, so LLDB did not record the edge. 
`DW_OP_entry_value` evaluation then failed after skipping the synthetic inline 
frame and looking up the return address in the concrete parent function.

Recursively collect call-site DIEs from the concrete function's child scopes 
while stopping at nested `DW_TAG_subprogram` entries, whose call sites belong 
to another function. The existing parsing of each call-site DIE remains 
unchanged.

Add a DWARF YAML unit test covering both a call site nested in an inline 
instance and the nested-subprogram traversal boundary.

Fixes #219198


>From 3c5307502486c681cf5933857dd60f1e03565a2d Mon Sep 17 00:00:00 2001
From: firmiana402 <[email protected]>
Date: Thu, 27 Aug 2026 20:30:45 +0800
Subject: [PATCH] [lldb] Collect call sites nested in inline instances

---
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 28 +++++---
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |  4 +-
 .../SymbolFile/DWARF/SymbolFileDWARFTests.cpp | 70 +++++++++++++++++++
 3 files changed, 91 insertions(+), 11 deletions(-)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 81cd4444161f7..c26b7d2386ac2 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4194,6 +4194,21 @@ CollectCallSiteParameters(ModuleSP module, DWARFDIE 
call_site_die) {
   return parameters;
 }
 
+static void CollectCallSiteDIEs(DWARFDIE parent,
+                                std::vector<DWARFDIE> &call_site_dies) {
+  for (DWARFDIE child : parent.children()) {
+    if (child.Tag() == DW_TAG_call_site ||
+        child.Tag() == DW_TAG_GNU_call_site) {
+      call_site_dies.push_back(child);
+      continue;
+    }
+
+    // A nested subprogram owns its call sites independently of this function.
+    if (child.Tag() != DW_TAG_subprogram)
+      CollectCallSiteDIEs(child, call_site_dies);
+  }
+}
+
 /// Collect call graph edges present in a function DIE.
 std::vector<std::unique_ptr<lldb_private::CallEdge>>
 SymbolFileDWARF::CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
@@ -4209,16 +4224,11 @@ SymbolFileDWARF::CollectCallEdges(ModuleSP module, 
DWARFDIE function_die) {
   LLDB_LOG(log, "CollectCallEdges: Found call site info in {0}",
            function_die.GetPubname());
 
-  // Scan the DIE for TAG_call_site entries.
-  // TODO: A recursive scan of all blocks in the subprogram is needed in order
-  // to be DWARF5-compliant. This may need to be done lazily to be performant.
-  // For now, assume that all entries are nested directly under the subprogram
-  // (this is the kind of DWARF LLVM produces) and parse them eagerly.
-  std::vector<std::unique_ptr<CallEdge>> call_edges;
-  for (DWARFDIE child : function_die.children()) {
-    if (child.Tag() != DW_TAG_call_site && child.Tag() != DW_TAG_GNU_call_site)
-      continue;
+  std::vector<DWARFDIE> call_site_dies;
+  CollectCallSiteDIEs(function_die, call_site_dies);
 
+  std::vector<std::unique_ptr<CallEdge>> call_edges;
+  for (DWARFDIE child : call_site_dies) {
     std::optional<DWARFDIE> call_origin;
     std::optional<DWARFExpressionList> call_target;
     addr_t return_pc = LLDB_INVALID_ADDRESS;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 9879fc4fe922c..1d8e24d272e86 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -486,8 +486,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   bool ClassContainsSelector(const DWARFDIE &class_die, ConstString selector);
 
-  /// Parse call site entries (DW_TAG_call_site), including any nested call 
site
-  /// parameters (DW_TAG_call_site_parameter).
+  /// Parse call site entries (DW_TAG_call_site), including entries in nested
+  /// scopes and their call site parameters (DW_TAG_call_site_parameter).
   std::vector<std::unique_ptr<CallEdge>>
   CollectCallEdges(lldb::ModuleSP module, DWARFDIE function_die);
 
diff --git a/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp 
b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
index 7f477f1913f9c..d26a78d743bbf 100644
--- a/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
@@ -22,6 +22,7 @@
 #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/Symbol/YAMLModuleTester.h"
 #include "TestingSupport/TestUtilities.h"
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Module.h"
@@ -29,6 +30,7 @@
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/LineTable.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/DataEncoder.h"
@@ -69,6 +71,74 @@ TEST_F(SymbolFileDWARFTests, TestAbilitiesForDWARF) {
   EXPECT_EQ(expected_abilities, symfile->CalculateAbilities());
 }
 
+TEST(SymbolFileDWARFCallEdgeTests, ParseCallSiteNestedInInline) {
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+DWARF:
+  debug_abbrev:
+    - Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+        - Code:            0x2
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_call_all_calls
+              Form:            DW_FORM_flag_present
+        - Code:            0x3
+          Tag:             DW_TAG_inlined_subroutine
+          Children:        DW_CHILDREN_yes
+        - Code:            0x4
+          Tag:             DW_TAG_call_site
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_call_target
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_call_return_pc
+              Form:            DW_FORM_addr
+  debug_info:
+    - Version:         5
+      UnitType:        DW_UT_compile
+      AddrSize:        8
+      Entries:
+        - AbbrCode:        0x1
+        - AbbrCode:        0x2
+        - AbbrCode:        0x3
+        - AbbrCode:        0x4
+          Values:
+            - Value:           0x1
+              BlockData:
+                - 0x50 # DW_OP_reg0
+            - Value:           0x1234
+        - AbbrCode:        0x0
+        - AbbrCode:        0x2
+        - AbbrCode:        0x4
+          Values:
+            - Value:           0x1
+              BlockData:
+                - 0x50 # DW_OP_reg0
+            - Value:           0x5678
+        - AbbrCode:        0x0
+        - AbbrCode:        0x0
+        - AbbrCode:        0x0
+)";
+
+  YAMLModuleTester t(yamldata);
+  auto *symbol_file =
+      llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile());
+  DWARFDIE function_die = t.GetDwarfUnit()->DIE().GetFirstChild();
+
+  auto call_edges = 
symbol_file->ParseCallEdgesInFunction(function_die.GetID());
+  ASSERT_EQ(call_edges.size(), 1u);
+  EXPECT_EQ(call_edges.front()->GetSortKey().second, 0x1234u);
+}
+
 TEST_F(SymbolFileDWARFTests, ParseArangesNonzeroSegmentSize) {
   // This `.debug_aranges` table header is a valid 32bit big-endian section
   // according to the DWARFv5 spec:6.2.1, but contains segment selectors which

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

Reply via email to