Skip to content

Commit 658a02c

Browse files
committed
[DebugInfo] Add option to use DW_AT_LLVM_stmt_sequence in line table lookups
1 parent 4564ac9 commit 658a02c

File tree

3 files changed

+201
-12
lines changed

3 files changed

+201
-12
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ class DWARFDebugLine {
209209
unsigned LastRowIndex;
210210
bool Empty;
211211

212+
/// The offset into the line table where this sequence begins
213+
uint64_t Offset = 0;
214+
212215
void reset();
213216

214217
static bool orderByHighPC(const Sequence &LHS, const Sequence &RHS) {
@@ -243,8 +246,20 @@ class DWARFDebugLine {
243246
uint32_t lookupAddress(object::SectionedAddress Address,
244247
bool *IsApproximateLine = nullptr) const;
245248

249+
/// Fills the Result argument with the indices of the rows that correspond
250+
/// to the address range specified by \p Address and \p Size.
251+
///
252+
/// \param Address - The starting address of the range.
253+
/// \param Size - The size of the address range.
254+
/// \param Result - The vector to fill with row indices.
255+
/// \param Die - if provided, the function will check for a
256+
/// DW_AT_LLVM_stmt_sequence attribute. If present, only rows from the
257+
/// sequence starting at the matching offset will be added to the result.
258+
///
259+
/// Returns true if any rows were found.
246260
bool lookupAddressRange(object::SectionedAddress Address, uint64_t Size,
247-
std::vector<uint32_t> &Result) const;
261+
std::vector<uint32_t> &Result,
262+
const DWARFDie *Die = nullptr) const;
248263

249264
bool hasFileAtIndex(uint64_t FileIndex) const {
250265
return Prologue.hasFileAtIndex(FileIndex);
@@ -305,8 +320,20 @@ class DWARFDebugLine {
305320
uint32_t lookupAddressImpl(object::SectionedAddress Address,
306321
bool *IsApproximateLine = nullptr) const;
307322

308-
bool lookupAddressRangeImpl(object::SectionedAddress Address, uint64_t Size,
309-
std::vector<uint32_t> &Result) const;
323+
/// Fills the Result argument with the indices of the rows that correspond
324+
/// to the address range specified by \p Address and \p Size.
325+
///
326+
/// \param Address - The starting address of the range.
327+
/// \param Size - The size of the address range.
328+
/// \param Result - The vector to fill with row indices.
329+
/// \param StmtSequenceOffset - if provided, only rows from the sequence
330+
/// starting at the matching offset will be added to the result.
331+
///
332+
/// Returns true if any rows were found.
333+
bool
334+
lookupAddressRangeImpl(object::SectionedAddress Address, uint64_t Size,
335+
std::vector<uint32_t> &Result,
336+
std::optional<uint64_t> StmtSequenceOffset) const;
310337
};
311338

312339
const LineTable *getLineTable(uint64_t Offset) const;
@@ -377,7 +404,15 @@ class DWARFDebugLine {
377404
function_ref<void(Error)> ErrorHandler);
378405

379406
void resetRowAndSequence();
380-
void appendRowToMatrix();
407+
408+
/// Append the current Row to the LineTable's matrix of rows and update the
409+
/// current Sequence information.
410+
///
411+
/// \param LineTableOffset - the offset into the line table where the
412+
/// current sequence of rows begins. This offset is stored in the Sequence
413+
/// to allow filtering rows based on their originating sequence when a
414+
/// DW_AT_LLVM_stmt_sequence attribute is present.
415+
void appendRowToMatrix(uint64_t LineTableOffset);
381416

382417
struct AddrOpIndexDelta {
383418
uint64_t AddrOffset;

llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,9 @@ void DWARFDebugLine::ParsingState::resetRowAndSequence() {
570570
Sequence.reset();
571571
}
572572

573-
void DWARFDebugLine::ParsingState::appendRowToMatrix() {
573+
void DWARFDebugLine::ParsingState::appendRowToMatrix(uint64_t LineTableOffset) {
574574
unsigned RowNumber = LineTable->Rows.size();
575+
Sequence.Offset = LineTableOffset;
575576
if (Sequence.Empty) {
576577
// Record the beginning of instruction sequence.
577578
Sequence.Empty = false;
@@ -848,6 +849,8 @@ Error DWARFDebugLine::LineTable::parse(
848849
*OS << '\n';
849850
Row::dumpTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0);
850851
}
852+
uint64_t LineTableSeqOffset = *OffsetPtr;
853+
851854
bool TombstonedAddress = false;
852855
auto EmitRow = [&] {
853856
if (!TombstonedAddress) {
@@ -857,7 +860,7 @@ Error DWARFDebugLine::LineTable::parse(
857860
}
858861
if (OS)
859862
State.Row.dump(*OS);
860-
State.appendRowToMatrix();
863+
State.appendRowToMatrix(LineTableSeqOffset);
861864
}
862865
};
863866
while (*OffsetPtr < EndOffset) {
@@ -913,6 +916,9 @@ Error DWARFDebugLine::LineTable::parse(
913916
// followed.
914917
EmitRow();
915918
State.resetRowAndSequence();
919+
// Cursor now points to right after the end_sequence opcode - so points
920+
// to the start of the next sequence - if one exists.
921+
LineTableSeqOffset = Cursor.tell();
916922
break;
917923

918924
case DW_LNE_set_address:
@@ -1364,23 +1370,32 @@ DWARFDebugLine::LineTable::lookupAddressImpl(object::SectionedAddress Address,
13641370

13651371
bool DWARFDebugLine::LineTable::lookupAddressRange(
13661372
object::SectionedAddress Address, uint64_t Size,
1367-
std::vector<uint32_t> &Result) const {
1373+
std::vector<uint32_t> &Result, const DWARFDie *Die) const {
1374+
1375+
// If DIE is provided, check for DW_AT_LLVM_stmt_sequence
1376+
std::optional<uint64_t> StmtSequenceOffset;
1377+
if (Die) {
1378+
if (auto StmtSeqAttr = Die->find(dwarf::DW_AT_LLVM_stmt_sequence)) {
1379+
StmtSequenceOffset = toSectionOffset(StmtSeqAttr);
1380+
}
1381+
}
13681382

13691383
// Search for relocatable addresses
1370-
if (lookupAddressRangeImpl(Address, Size, Result))
1384+
if (lookupAddressRangeImpl(Address, Size, Result, StmtSequenceOffset))
13711385
return true;
13721386

13731387
if (Address.SectionIndex == object::SectionedAddress::UndefSection)
13741388
return false;
13751389

13761390
// Search for absolute addresses
13771391
Address.SectionIndex = object::SectionedAddress::UndefSection;
1378-
return lookupAddressRangeImpl(Address, Size, Result);
1392+
return lookupAddressRangeImpl(Address, Size, Result, StmtSequenceOffset);
13791393
}
13801394

13811395
bool DWARFDebugLine::LineTable::lookupAddressRangeImpl(
13821396
object::SectionedAddress Address, uint64_t Size,
1383-
std::vector<uint32_t> &Result) const {
1397+
std::vector<uint32_t> &Result,
1398+
std::optional<uint64_t> StmtSequenceOffset) const {
13841399
if (Sequences.empty())
13851400
return false;
13861401
uint64_t EndAddr = Address.Address + Size;
@@ -1401,6 +1416,14 @@ bool DWARFDebugLine::LineTable::lookupAddressRangeImpl(
14011416

14021417
while (SeqPos != LastSeq && SeqPos->LowPC < EndAddr) {
14031418
const DWARFDebugLine::Sequence &CurSeq = *SeqPos;
1419+
1420+
// Skip sequences that don't match our stmt_sequence offset if one was
1421+
// provided
1422+
if (StmtSequenceOffset && CurSeq.Offset != *StmtSequenceOffset) {
1423+
++SeqPos;
1424+
continue;
1425+
}
1426+
14041427
// For the first sequence, we need to find which row in the sequence is the
14051428
// first in our range.
14061429
uint32_t FirstRowIndex = CurSeq.FirstRowIndex;
@@ -1423,7 +1446,7 @@ bool DWARFDebugLine::LineTable::lookupAddressRangeImpl(
14231446
++SeqPos;
14241447
}
14251448

1426-
return true;
1449+
return !Result.empty();
14271450
}
14281451

14291452
std::optional<StringRef>

llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
910
#include "DwarfGenerator.h"
1011
#include "DwarfUtils.h"
1112
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
12-
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
1313
#include "llvm/Object/ObjectFile.h"
1414
#include "llvm/Testing/Support/Error.h"
1515
#include "gtest/gtest.h"
@@ -2035,4 +2035,135 @@ TEST_F(DebugLineBasicFixture, PrintPathsProperly) {
20352035
EXPECT_THAT(Result.c_str(), MatchesRegex("a dir.b dir.b file"));
20362036
}
20372037

2038+
/// Test that lookupAddressRange correctly filters rows based on
2039+
/// DW_AT_LLVM_stmt_sequence.
2040+
///
2041+
/// This test verifies that:
2042+
/// 1. When a DIE has a DW_AT_LLVM_stmt_sequence attribute, lookupAddressRange
2043+
/// only returns rows from the sequence starting at the specified offset
2044+
/// 2. When a DIE has an invalid DW_AT_LLVM_stmt_sequence offset, no rows are
2045+
/// returned
2046+
/// 3. When no DW_AT_LLVM_stmt_sequence is present, all matching rows are
2047+
/// returned
2048+
///
2049+
/// The test creates a line table with two sequences at the same address range
2050+
/// but different line numbers. It then creates three subprogram DIEs:
2051+
/// - One with DW_AT_LLVM_stmt_sequence pointing to the first sequence
2052+
/// - One with DW_AT_LLVM_stmt_sequence pointing to the second sequence
2053+
/// - One with an invalid DW_AT_LLVM_stmt_sequence offset
2054+
TEST_F(DebugLineBasicFixture, LookupAddressRangeWithStmtSequenceOffset) {
2055+
if (!setupGenerator())
2056+
GTEST_SKIP();
2057+
2058+
// Create new DWARF with the subprogram DIE
2059+
dwarfgen::CompileUnit &CU = Gen->addCompileUnit();
2060+
dwarfgen::DIE CUDie = CU.getUnitDIE();
2061+
2062+
CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c");
2063+
CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);
2064+
2065+
dwarfgen::DIE SD1 = CUDie.addChild(DW_TAG_subprogram);
2066+
SD1.addAttribute(DW_AT_name, DW_FORM_strp, "sub1");
2067+
SD1.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U);
2068+
SD1.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x1032U);
2069+
// DW_AT_LLVM_stmt_sequence points to the first sequence
2070+
SD1.addAttribute(DW_AT_LLVM_stmt_sequence, DW_FORM_sec_offset, 0x2e);
2071+
2072+
dwarfgen::DIE SD2 = CUDie.addChild(DW_TAG_subprogram);
2073+
SD2.addAttribute(DW_AT_name, DW_FORM_strp, "sub2");
2074+
SD2.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U);
2075+
SD2.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x1032U);
2076+
// DW_AT_LLVM_stmt_sequence points to the second sequence
2077+
SD2.addAttribute(DW_AT_LLVM_stmt_sequence, DW_FORM_sec_offset, 0x42);
2078+
2079+
dwarfgen::DIE SD3 = CUDie.addChild(DW_TAG_subprogram);
2080+
SD3.addAttribute(DW_AT_name, DW_FORM_strp, "sub3");
2081+
SD3.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U);
2082+
SD3.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x1032U);
2083+
// Invalid DW_AT_LLVM_stmt_sequence
2084+
SD3.addAttribute(DW_AT_LLVM_stmt_sequence, DW_FORM_sec_offset, 0x66);
2085+
2086+
// Create a line table with multiple sequences
2087+
LineTable &LT = Gen->addLineTable();
2088+
2089+
// First sequence with addresses 0x1000(Ln100), 0x1004(Ln101)
2090+
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0x1000U, LineTable::Quad}});
2091+
LT.addStandardOpcode(DW_LNS_set_prologue_end, {});
2092+
LT.addStandardOpcode(DW_LNS_advance_line, {{99, LineTable::SLEB}});
2093+
LT.addStandardOpcode(DW_LNS_copy, {});
2094+
LT.addByte(0x4b); // Special opcode: address += 4, line += 1
2095+
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
2096+
2097+
// Second sequence with addresses 0x1000(Ln200), 0x1004(Ln201)
2098+
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0x1000U, LineTable::Quad}});
2099+
LT.addStandardOpcode(DW_LNS_set_prologue_end, {});
2100+
LT.addStandardOpcode(DW_LNS_advance_line, {{199, LineTable::SLEB}});
2101+
LT.addStandardOpcode(DW_LNS_copy, {});
2102+
LT.addByte(0x4b); // Special opcode: address += 4, line += 1
2103+
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
2104+
2105+
// Generate the DWARF
2106+
generate();
2107+
2108+
// Parse the line table to get the sequence offset
2109+
auto ExpectedLineTable = Line.getOrParseLineTable(
2110+
LineData, /*Offset=*/0, *Context, nullptr, RecordRecoverable);
2111+
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
2112+
const auto *Table = *ExpectedLineTable;
2113+
2114+
uint32_t NumCUs = Context->getNumCompileUnits();
2115+
ASSERT_EQ(NumCUs, 1u);
2116+
DWARFUnit *Unit = Context->getUnitAtIndex(0);
2117+
auto DwarfCUDie = Unit->getUnitDIE(false);
2118+
2119+
auto Sub1Die = DwarfCUDie.getFirstChild();
2120+
auto Sub2Die = Sub1Die.getSibling();
2121+
auto Sub3Die = Sub2Die.getSibling();
2122+
2123+
// Verify Sub1Die is the DIE generated from SD1
2124+
auto NameAttr1 = Sub1Die.find(DW_AT_name);
2125+
EXPECT_STREQ(*dwarf::toString(*NameAttr1), "sub1");
2126+
2127+
// Verify Sub2Die is the DIE generated from SD2
2128+
auto NameAttr2 = Sub2Die.find(DW_AT_name);
2129+
EXPECT_STREQ(*dwarf::toString(*NameAttr2), "sub2");
2130+
2131+
// Verify Sub2Die is the DIE generated from SD3
2132+
auto NameAttr3 = Sub3Die.find(DW_AT_name);
2133+
EXPECT_STREQ(*dwarf::toString(*NameAttr3), "sub3");
2134+
2135+
// Ensure there are two sequences
2136+
ASSERT_EQ(Table->Sequences.size(), 2u);
2137+
2138+
// Lookup addresses in the first sequence with the second sequence's filter
2139+
{
2140+
std::vector<uint32_t> Rows;
2141+
bool Found;
2142+
2143+
// Look up using Sub3Die, which has an invalid DW_AT_LLVM_stmt_sequence
2144+
Found = Table->lookupAddressRange(
2145+
{0x1000, object::SectionedAddress::UndefSection}, /*Size=*/1, Rows,
2146+
&Sub3Die);
2147+
EXPECT_FALSE(Found);
2148+
2149+
// Look up using Sub1Die, which has a valid DW_AT_LLVM_stmt_sequence and
2150+
// should return row 0
2151+
Found = Table->lookupAddressRange(
2152+
{0x1000, object::SectionedAddress::UndefSection}, /*Size=*/1, Rows,
2153+
&Sub1Die);
2154+
EXPECT_TRUE(Found);
2155+
ASSERT_EQ(Rows.size(), 1u);
2156+
EXPECT_EQ(Rows[0], 0);
2157+
2158+
// Look up using Sub2Die, which has a valid DW_AT_LLVM_stmt_sequence and
2159+
// should return row 3
2160+
Rows.clear();
2161+
Found = Table->lookupAddressRange(
2162+
{0x1000, object::SectionedAddress::UndefSection}, /*Size=*/1, Rows,
2163+
&Sub2Die);
2164+
EXPECT_TRUE(Found);
2165+
ASSERT_EQ(Rows.size(), 1u);
2166+
EXPECT_EQ(Rows[0], 3);
2167+
}
2168+
}
20382169
} // end anonymous namespace

0 commit comments

Comments
 (0)