Skip to content

Commit 0886440

Browse files
[Symbolizer] Support for Missing Line Numbers. (#82240)
LLVM Symbolizer attempt to symbolize addresses of optimized binaries reports missing line numbers for some cases. It maybe due to compiler which sometimes cannot map an instruction to line number due to optimizations. Symbolizer should handle those cases gracefully. Adding an option '--skip-line-zero' to symbolizer so as to report the nearest non-zero line number. --------- Co-authored-by: Amit Pandey <[email protected]>
1 parent fc83e97 commit 0886440

File tree

12 files changed

+260
-38
lines changed

12 files changed

+260
-38
lines changed

lld/Common/DWARF.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ std::optional<DILineInfo> DWARFCache::getDILineInfo(uint64_t offset,
9393
DILineInfo info;
9494
for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
9595
if (lt->getFileLineInfoForAddress(
96-
{offset, sectionIndex}, nullptr,
96+
{offset, sectionIndex}, false, nullptr,
9797
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
9898
return info;
9999
}

llvm/docs/CommandGuide/llvm-symbolizer.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,33 @@ Example 7 - Addresses as symbol names:
207207
foz
208208
/tmp/test.h:1:0
209209
210+
Example 8 - :option:`--skip-line-zero` output for an address with no line correspondence (an address associated with line zero):
211+
212+
.. code-block:: c
213+
214+
// test.c
215+
int foo = 0;
216+
int x = 1234;
217+
int main() {
218+
if (x)
219+
return foo;
220+
else
221+
return x;
222+
}
223+
224+
These files are built as follows:
225+
226+
.. code-block:: console
227+
228+
$ clang -g -O2 -S test.c -o test.s
229+
$ llvm-mc -filetype=obj -triple=x86_64-unknown-linux test.s -o test.o
230+
231+
.. code-block:: console
232+
233+
$ llvm-symbolizer --obj=test.o --skip-line-zero 0xa
234+
main
235+
/tmp/test.c:5:7 (approximate)
236+
210237
OPTIONS
211238
-------
212239

@@ -216,6 +243,12 @@ OPTIONS
216243
This can be used to perform lookups as if the object were relocated by the
217244
offset.
218245

246+
.. option:: --skip-line-zero
247+
248+
If an address does not have an associated line number, use the last line
249+
number from the current sequence in the line-table. Such lines are labeled
250+
as "approximate" in the output as they may be misleading.
251+
219252
.. option:: --basenames, -s
220253

221254
Print just the file's name without any directories, instead of the

llvm/include/llvm/DebugInfo/DIContext.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace llvm {
3030

3131
/// A format-neutral container for source line information.
3232
struct DILineInfo {
33+
static constexpr const char *const ApproxString = "(approximate)";
3334
// DILineInfo contains "<invalid>" for function/filename it cannot fetch.
3435
static constexpr const char *const BadString = "<invalid>";
3536
// Use "??" instead of "<invalid>" to make our output closer to addr2line.
@@ -50,6 +51,7 @@ struct DILineInfo {
5051
// DWARF-specific.
5152
uint32_t Discriminator = 0;
5253

54+
bool IsApproximateLine = false;
5355
DILineInfo()
5456
: FileName(BadString), FunctionName(BadString), StartFileName(BadString) {
5557
}
@@ -153,13 +155,14 @@ struct DILineInfoSpecifier {
153155
AbsoluteFilePath
154156
};
155157
using FunctionNameKind = DINameKind;
156-
157158
FileLineInfoKind FLIKind;
158159
FunctionNameKind FNKind;
160+
bool ApproximateLine;
159161

160162
DILineInfoSpecifier(FileLineInfoKind FLIKind = FileLineInfoKind::RawValue,
161-
FunctionNameKind FNKind = FunctionNameKind::None)
162-
: FLIKind(FLIKind), FNKind(FNKind) {}
163+
FunctionNameKind FNKind = FunctionNameKind::None,
164+
bool ApproximateLine = false)
165+
: FLIKind(FLIKind), FNKind(FNKind), ApproximateLine(ApproximateLine) {}
163166

164167
inline bool operator==(const DILineInfoSpecifier &RHS) const {
165168
return FLIKind == RHS.FLIKind && FNKind == RHS.FNKind;

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ class DWARFDebugLine {
240240

241241
/// Returns the index of the row with file/line info for a given address,
242242
/// or UnknownRowIndex if there is no such row.
243-
uint32_t lookupAddress(object::SectionedAddress Address) const;
243+
uint32_t lookupAddress(object::SectionedAddress Address,
244+
bool *IsApproximateLine = nullptr) const;
244245

245246
bool lookupAddressRange(object::SectionedAddress Address, uint64_t Size,
246247
std::vector<uint32_t> &Result) const;
@@ -267,7 +268,7 @@ class DWARFDebugLine {
267268
/// Fills the Result argument with the file and line information
268269
/// corresponding to Address. Returns true on success.
269270
bool getFileLineInfoForAddress(object::SectionedAddress Address,
270-
const char *CompDir,
271+
bool Approximate, const char *CompDir,
271272
DILineInfoSpecifier::FileLineInfoKind Kind,
272273
DILineInfo &Result) const;
273274

@@ -301,7 +302,8 @@ class DWARFDebugLine {
301302
getSourceByIndex(uint64_t FileIndex,
302303
DILineInfoSpecifier::FileLineInfoKind Kind) const;
303304

304-
uint32_t lookupAddressImpl(object::SectionedAddress Address) const;
305+
uint32_t lookupAddressImpl(object::SectionedAddress Address,
306+
bool *IsApproximateLine = nullptr) const;
305307

306308
bool lookupAddressRangeImpl(object::SectionedAddress Address, uint64_t Size,
307309
std::vector<uint32_t> &Result) const;

llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class LLVMSymbolizer {
5252
struct Options {
5353
FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName;
5454
FileLineInfoKind PathStyle = FileLineInfoKind::AbsoluteFilePath;
55+
bool SkipLineZero = false;
5556
bool UseSymbolTable = true;
5657
bool Demangle = true;
5758
bool RelativeAddresses = false;

llvm/lib/DebugInfo/DWARF/DWARFContext.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,8 +1743,8 @@ DILineInfo DWARFContext::getLineInfoForAddress(object::SectionedAddress Address,
17431743
if (Spec.FLIKind != FileLineInfoKind::None) {
17441744
if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) {
17451745
LineTable->getFileLineInfoForAddress(
1746-
{Address.Address, Address.SectionIndex}, CU->getCompilationDir(),
1747-
Spec.FLIKind, Result);
1746+
{Address.Address, Address.SectionIndex}, Spec.ApproximateLine,
1747+
CU->getCompilationDir(), Spec.FLIKind, Result);
17481748
}
17491749
}
17501750

@@ -1838,9 +1838,10 @@ DWARFContext::getInliningInfoForAddress(object::SectionedAddress Address,
18381838
if (Spec.FLIKind != FileLineInfoKind::None) {
18391839
DILineInfo Frame;
18401840
LineTable = getLineTableForUnit(CU);
1841-
if (LineTable && LineTable->getFileLineInfoForAddress(
1842-
{Address.Address, Address.SectionIndex},
1843-
CU->getCompilationDir(), Spec.FLIKind, Frame))
1841+
if (LineTable &&
1842+
LineTable->getFileLineInfoForAddress(
1843+
{Address.Address, Address.SectionIndex}, Spec.ApproximateLine,
1844+
CU->getCompilationDir(), Spec.FLIKind, Frame))
18441845
InliningInfo.addFrame(Frame);
18451846
}
18461847
return InliningInfo;
@@ -1866,8 +1867,8 @@ DWARFContext::getInliningInfoForAddress(object::SectionedAddress Address,
18661867
// For the topmost routine, get file/line info from line table.
18671868
if (LineTable)
18681869
LineTable->getFileLineInfoForAddress(
1869-
{Address.Address, Address.SectionIndex}, CU->getCompilationDir(),
1870-
Spec.FLIKind, Frame);
1870+
{Address.Address, Address.SectionIndex}, Spec.ApproximateLine,
1871+
CU->getCompilationDir(), Spec.FLIKind, Frame);
18711872
} else {
18721873
// Otherwise, use call file, call line and call column from
18731874
// previous DIE in inlined chain.

llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,12 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS,
158158
uint32_t FileBase = getVersion() >= 5 ? 0 : 1;
159159
for (uint32_t I = 0; I != FileNames.size(); ++I) {
160160
const FileNameEntry &FileEntry = FileNames[I];
161-
OS << format("file_names[%3u]:\n", I + FileBase);
162-
OS << " name: ";
161+
OS << format("file_names[%3u]:\n", I + FileBase);
162+
OS << " name: ";
163163
FileEntry.Name.dump(OS, DumpOptions);
164-
OS << '\n'
165-
<< format(" dir_index: %" PRIu64 "\n", FileEntry.DirIdx);
164+
OS << '\n' << format(" dir_index: %" PRIu64 "\n", FileEntry.DirIdx);
166165
if (ContentTypes.HasMD5)
167-
OS << " md5_checksum: " << FileEntry.Checksum.digest() << '\n';
166+
OS << " md5_checksum: " << FileEntry.Checksum.digest() << '\n';
168167
if (ContentTypes.HasModTime)
169168
OS << format(" mod_time: 0x%8.8" PRIx64 "\n", FileEntry.ModTime);
170169
if (ContentTypes.HasLength)
@@ -604,9 +603,10 @@ Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable(
604603
DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx,
605604
const DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) {
606605
if (!DebugLineData.isValidOffset(Offset))
607-
return createStringError(errc::invalid_argument, "offset 0x%8.8" PRIx64
608-
" is not a valid debug line section offset",
609-
Offset);
606+
return createStringError(errc::invalid_argument,
607+
"offset 0x%8.8" PRIx64
608+
" is not a valid debug line section offset",
609+
Offset);
610610

611611
std::pair<LineTableIter, bool> Pos =
612612
LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable()));
@@ -966,7 +966,8 @@ Error DWARFDebugLine::LineTable::parse(
966966

967967
if (Cursor && Verbose) {
968968
*OS << " (";
969-
DWARFFormValue::dumpAddress(*OS, OpcodeAddressSize, State.Row.Address.Address);
969+
DWARFFormValue::dumpAddress(*OS, OpcodeAddressSize,
970+
State.Row.Address.Address);
970971
*OS << ')';
971972
}
972973
}
@@ -1159,8 +1160,7 @@ Error DWARFDebugLine::LineTable::parse(
11591160
// DW_LNS_advance_pc. Such assemblers, however, can use
11601161
// DW_LNS_fixed_advance_pc instead, sacrificing compression.
11611162
{
1162-
uint16_t PCOffset =
1163-
TableData.getRelocatedValue(Cursor, 2);
1163+
uint16_t PCOffset = TableData.getRelocatedValue(Cursor, 2);
11641164
if (Cursor) {
11651165
State.Row.Address.Address += PCOffset;
11661166
State.Row.OpIndex = 0;
@@ -1312,23 +1312,28 @@ uint32_t DWARFDebugLine::LineTable::findRowInSeq(
13121312
return RowPos - Rows.begin();
13131313
}
13141314

1315-
uint32_t DWARFDebugLine::LineTable::lookupAddress(
1316-
object::SectionedAddress Address) const {
1315+
uint32_t
1316+
DWARFDebugLine::LineTable::lookupAddress(object::SectionedAddress Address,
1317+
bool *IsApproximateLine) const {
13171318

13181319
// Search for relocatable addresses
1319-
uint32_t Result = lookupAddressImpl(Address);
1320+
uint32_t Result = lookupAddressImpl(Address, IsApproximateLine);
13201321

13211322
if (Result != UnknownRowIndex ||
13221323
Address.SectionIndex == object::SectionedAddress::UndefSection)
13231324
return Result;
13241325

13251326
// Search for absolute addresses
13261327
Address.SectionIndex = object::SectionedAddress::UndefSection;
1327-
return lookupAddressImpl(Address);
1328+
return lookupAddressImpl(Address, IsApproximateLine);
13281329
}
13291330

1330-
uint32_t DWARFDebugLine::LineTable::lookupAddressImpl(
1331-
object::SectionedAddress Address) const {
1331+
uint32_t
1332+
DWARFDebugLine::LineTable::lookupAddressImpl(object::SectionedAddress Address,
1333+
bool *IsApproximateLine) const {
1334+
assert(!IsApproximateLine ||
1335+
!*IsApproximateLine && "Make sure IsApproximateLine is appropriately "
1336+
"initialized, if provided");
13321337
// First, find an instruction sequence containing the given address.
13331338
DWARFDebugLine::Sequence Sequence;
13341339
Sequence.SectionIndex = Address.SectionIndex;
@@ -1337,7 +1342,24 @@ uint32_t DWARFDebugLine::LineTable::lookupAddressImpl(
13371342
DWARFDebugLine::Sequence::orderByHighPC);
13381343
if (It == Sequences.end() || It->SectionIndex != Address.SectionIndex)
13391344
return UnknownRowIndex;
1340-
return findRowInSeq(*It, Address);
1345+
1346+
uint32_t RowIndex = findRowInSeq(*It, Address);
1347+
if (RowIndex == UnknownRowIndex || !IsApproximateLine)
1348+
return RowIndex;
1349+
1350+
// Approximation will only be attempted if a valid RowIndex exists.
1351+
uint32_t ApproxRowIndex = RowIndex;
1352+
// Approximation Loop
1353+
for (; ApproxRowIndex >= It->FirstRowIndex; --ApproxRowIndex) {
1354+
if (Rows[ApproxRowIndex].Line)
1355+
return ApproxRowIndex;
1356+
*IsApproximateLine = true;
1357+
}
1358+
// Approximation Loop fails to find the valid ApproxRowIndex
1359+
if (ApproxRowIndex < It->FirstRowIndex)
1360+
*IsApproximateLine = false;
1361+
1362+
return RowIndex;
13411363
}
13421364

13431365
bool DWARFDebugLine::LineTable::lookupAddressRange(
@@ -1477,10 +1499,11 @@ bool DWARFDebugLine::Prologue::getFileNameByIndex(
14771499
}
14781500

14791501
bool DWARFDebugLine::LineTable::getFileLineInfoForAddress(
1480-
object::SectionedAddress Address, const char *CompDir,
1502+
object::SectionedAddress Address, bool Approximate, const char *CompDir,
14811503
FileLineInfoKind Kind, DILineInfo &Result) const {
14821504
// Get the index of row we're looking for in the line table.
1483-
uint32_t RowIndex = lookupAddress(Address);
1505+
uint32_t RowIndex =
1506+
lookupAddress(Address, Approximate ? &Result.IsApproximateLine : nullptr);
14841507
if (RowIndex == -1U)
14851508
return false;
14861509
// Take file number and line/column from the row.

llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,19 @@ void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
131131

132132
void LLVMPrinter::printSimpleLocation(StringRef Filename,
133133
const DILineInfo &Info) {
134-
OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n';
134+
OS << Filename << ':' << Info.Line << ':' << Info.Column;
135+
if (Info.IsApproximateLine)
136+
OS << " " << Info.ApproxString;
137+
OS << "\n";
135138
printContext(
136139
SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
137140
}
138141

139142
void GNUPrinter::printSimpleLocation(StringRef Filename,
140143
const DILineInfo &Info) {
141144
OS << Filename << ':' << Info.Line;
145+
if (Info.IsApproximateLine)
146+
OS << " " << Info.ApproxString;
142147
if (Info.Discriminator)
143148
OS << " (discriminator " << Info.Discriminator << ')';
144149
OS << '\n';
@@ -158,6 +163,8 @@ void PlainPrinterBase::printVerbose(StringRef Filename,
158163
OS << " Column: " << Info.Column << '\n';
159164
if (Info.Discriminator)
160165
OS << " Discriminator: " << Info.Discriminator << '\n';
166+
if (Info.IsApproximateLine)
167+
OS << " Approximate: true" << '\n';
161168
}
162169

163170
void LLVMPrinter::printStartAddress(const DILineInfo &Info) {
@@ -294,7 +301,7 @@ static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") {
294301
}
295302

296303
static json::Object toJSON(const DILineInfo &LineInfo) {
297-
return json::Object(
304+
json::Object Obj = json::Object(
298305
{{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString
299306
? LineInfo.FunctionName
300307
: ""},
@@ -309,6 +316,9 @@ static json::Object toJSON(const DILineInfo &LineInfo) {
309316
{"Line", LineInfo.Line},
310317
{"Column", LineInfo.Column},
311318
{"Discriminator", LineInfo.Discriminator}});
319+
if (LineInfo.IsApproximateLine)
320+
Obj.insert({"Approximate", LineInfo.IsApproximateLine});
321+
return Obj;
312322
}
313323

314324
void JSONPrinter::print(const Request &Request, const DILineInfo &Info) {

llvm/lib/DebugInfo/Symbolize/Symbolize.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ LLVMSymbolizer::symbolizeCodeCommon(const T &ModuleSpecifier,
7171
ModuleOffset.Address += Info->getModulePreferredBase();
7272

7373
DILineInfo LineInfo = Info->symbolizeCode(
74-
ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),
74+
ModuleOffset,
75+
DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions,
76+
Opts.SkipLineZero),
7577
Opts.UseSymbolTable);
7678
if (Opts.Demangle)
7779
LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info);
@@ -116,7 +118,9 @@ Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCodeCommon(
116118
ModuleOffset.Address += Info->getModulePreferredBase();
117119

118120
DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(
119-
ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),
121+
ModuleOffset,
122+
DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions,
123+
Opts.SkipLineZero),
120124
Opts.UseSymbolTable);
121125
if (Opts.Demangle) {
122126
for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {

0 commit comments

Comments
 (0)