1 //===- DwarfTransformer.cpp -----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 #include <unordered_set>
12 #include "llvm/DebugInfo/DIContext.h"
13 #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
14 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/ThreadPool.h"
17 #include "llvm/Support/raw_ostream.h"
19 #include "llvm/DebugInfo/GSYM/DwarfTransformer.h"
20 #include "llvm/DebugInfo/GSYM/FunctionInfo.h"
21 #include "llvm/DebugInfo/GSYM/GsymCreator.h"
22 #include "llvm/DebugInfo/GSYM/GsymReader.h"
23 #include "llvm/DebugInfo/GSYM/InlineInfo.h"
29 struct llvm::gsym::CUInfo
{
30 const DWARFDebugLine::LineTable
*LineTable
;
32 std::vector
<uint32_t> FileCache
;
33 uint64_t Language
= 0;
36 CUInfo(DWARFContext
&DICtx
, DWARFCompileUnit
*CU
) {
37 LineTable
= DICtx
.getLineTableForUnit(CU
);
38 CompDir
= CU
->getCompilationDir();
41 FileCache
.assign(LineTable
->Prologue
.FileNames
.size() + 1, UINT32_MAX
);
42 DWARFDie Die
= CU
->getUnitDIE();
43 Language
= dwarf::toUnsigned(Die
.find(dwarf::DW_AT_language
), 0);
44 AddrSize
= CU
->getAddressByteSize();
47 /// Return true if Addr is the highest address for a given compile unit. The
48 /// highest address is encoded as -1, of all ones in the address. These high
49 /// addresses are used by some linkers to indicate that a function has been
50 /// dead stripped or didn't end up in the linked executable.
51 bool isHighestAddress(uint64_t Addr
) const {
53 return Addr
== UINT32_MAX
;
54 else if (AddrSize
== 8)
55 return Addr
== UINT64_MAX
;
59 /// Convert a DWARF compile unit file index into a GSYM global file index.
61 /// Each compile unit in DWARF has its own file table in the line table
62 /// prologue. GSYM has a single large file table that applies to all files
63 /// from all of the info in a GSYM file. This function converts between the
64 /// two and caches and DWARF CU file index that has already been converted so
65 /// the first client that asks for a compile unit file index will end up
66 /// doing the conversion, and subsequent clients will get the cached GSYM
68 uint32_t DWARFToGSYMFileIndex(GsymCreator
&Gsym
, uint32_t DwarfFileIdx
) {
71 assert(DwarfFileIdx
< FileCache
.size());
72 uint32_t &GsymFileIdx
= FileCache
[DwarfFileIdx
];
73 if (GsymFileIdx
!= UINT32_MAX
)
76 if (LineTable
->getFileNameByIndex(
77 DwarfFileIdx
, CompDir
,
78 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
, File
))
79 GsymFileIdx
= Gsym
.insertFile(File
);
87 static DWARFDie
GetParentDeclContextDIE(DWARFDie
&Die
) {
88 if (DWARFDie SpecDie
=
89 Die
.getAttributeValueAsReferencedDie(dwarf::DW_AT_specification
)) {
90 if (DWARFDie SpecParent
= GetParentDeclContextDIE(SpecDie
))
93 if (DWARFDie AbstDie
=
94 Die
.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin
)) {
95 if (DWARFDie AbstParent
= GetParentDeclContextDIE(AbstDie
))
99 // We never want to follow parent for inlined subroutine - that would
100 // give us information about where the function is inlined, not what
101 // function is inlined
102 if (Die
.getTag() == dwarf::DW_TAG_inlined_subroutine
)
105 DWARFDie ParentDie
= Die
.getParent();
109 switch (ParentDie
.getTag()) {
110 case dwarf::DW_TAG_namespace
:
111 case dwarf::DW_TAG_structure_type
:
112 case dwarf::DW_TAG_union_type
:
113 case dwarf::DW_TAG_class_type
:
114 case dwarf::DW_TAG_subprogram
:
115 return ParentDie
; // Found parent decl context DIE
116 case dwarf::DW_TAG_lexical_block
:
117 return GetParentDeclContextDIE(ParentDie
);
125 /// Get the GsymCreator string table offset for the qualified name for the
126 /// DIE passed in. This function will avoid making copies of any strings in
127 /// the GsymCreator when possible. We don't need to copy a string when the
128 /// string comes from our .debug_str section or is an inlined string in the
129 /// .debug_info. If we create a qualified name string in this function by
130 /// combining multiple strings in the DWARF string table or info, we will make
131 /// a copy of the string when we add it to the string table.
132 static std::optional
<uint32_t>
133 getQualifiedNameIndex(DWARFDie
&Die
, uint64_t Language
, GsymCreator
&Gsym
) {
134 // If the dwarf has mangled name, use mangled name
135 if (auto LinkageName
=
136 dwarf::toString(Die
.findRecursively({dwarf::DW_AT_MIPS_linkage_name
,
137 dwarf::DW_AT_linkage_name
}),
139 return Gsym
.insertString(LinkageName
, /* Copy */ false);
141 StringRef
ShortName(Die
.getName(DINameKind::ShortName
));
142 if (ShortName
.empty())
145 // For C++ and ObjC, prepend names of all parent declaration contexts
146 if (!(Language
== dwarf::DW_LANG_C_plus_plus
||
147 Language
== dwarf::DW_LANG_C_plus_plus_03
||
148 Language
== dwarf::DW_LANG_C_plus_plus_11
||
149 Language
== dwarf::DW_LANG_C_plus_plus_14
||
150 Language
== dwarf::DW_LANG_ObjC_plus_plus
||
151 // This should not be needed for C, but we see C++ code marked as C
152 // in some binaries. This should hurt, so let's do it for C as well
153 Language
== dwarf::DW_LANG_C
))
154 return Gsym
.insertString(ShortName
, /* Copy */ false);
156 // Some GCC optimizations create functions with names ending with .isra.<num>
157 // or .part.<num> and those names are just DW_AT_name, not DW_AT_linkage_name
158 // If it looks like it could be the case, don't add any prefix
159 if (ShortName
.startswith("_Z") &&
160 (ShortName
.contains(".isra.") || ShortName
.contains(".part.")))
161 return Gsym
.insertString(ShortName
, /* Copy */ false);
163 DWARFDie ParentDeclCtxDie
= GetParentDeclContextDIE(Die
);
164 if (ParentDeclCtxDie
) {
165 std::string Name
= ShortName
.str();
166 while (ParentDeclCtxDie
) {
167 StringRef
ParentName(ParentDeclCtxDie
.getName(DINameKind::ShortName
));
168 if (!ParentName
.empty()) {
169 // "lambda" names are wrapped in < >. Replace with { }
170 // to be consistent with demangled names and not to confuse with
172 if (ParentName
.front() == '<' && ParentName
.back() == '>')
173 Name
= "{" + ParentName
.substr(1, ParentName
.size() - 2).str() + "}" +
176 Name
= ParentName
.str() + "::" + Name
;
178 ParentDeclCtxDie
= GetParentDeclContextDIE(ParentDeclCtxDie
);
180 // Copy the name since we created a new name in a std::string.
181 return Gsym
.insertString(Name
, /* Copy */ true);
183 // Don't copy the name since it exists in the DWARF object file.
184 return Gsym
.insertString(ShortName
, /* Copy */ false);
187 static bool hasInlineInfo(DWARFDie Die
, uint32_t Depth
) {
188 bool CheckChildren
= true;
189 switch (Die
.getTag()) {
190 case dwarf::DW_TAG_subprogram
:
191 // Don't look into functions within functions.
192 CheckChildren
= Depth
== 0;
194 case dwarf::DW_TAG_inlined_subroutine
:
201 for (DWARFDie ChildDie
: Die
.children()) {
202 if (hasInlineInfo(ChildDie
, Depth
+ 1))
208 static void parseInlineInfo(GsymCreator
&Gsym
, CUInfo
&CUI
, DWARFDie Die
,
209 uint32_t Depth
, FunctionInfo
&FI
,
210 InlineInfo
&parent
) {
211 if (!hasInlineInfo(Die
, Depth
))
214 dwarf::Tag Tag
= Die
.getTag();
215 if (Tag
== dwarf::DW_TAG_inlined_subroutine
) {
216 // create new InlineInfo and append to parent.children
218 DWARFAddressRange FuncRange
=
219 DWARFAddressRange(FI
.startAddress(), FI
.endAddress());
220 Expected
<DWARFAddressRangesVector
> RangesOrError
= Die
.getAddressRanges();
222 for (const DWARFAddressRange
&Range
: RangesOrError
.get()) {
223 // Check that the inlined function is within the range of the function
224 // info, it might not be in case of split functions
225 if (FuncRange
.LowPC
<= Range
.LowPC
&& Range
.HighPC
<= FuncRange
.HighPC
)
226 II
.Ranges
.insert(AddressRange(Range
.LowPC
, Range
.HighPC
));
229 if (II
.Ranges
.empty())
232 if (auto NameIndex
= getQualifiedNameIndex(Die
, CUI
.Language
, Gsym
))
233 II
.Name
= *NameIndex
;
234 II
.CallFile
= CUI
.DWARFToGSYMFileIndex(
235 Gsym
, dwarf::toUnsigned(Die
.find(dwarf::DW_AT_call_file
), 0));
236 II
.CallLine
= dwarf::toUnsigned(Die
.find(dwarf::DW_AT_call_line
), 0);
237 // parse all children and append to parent
238 for (DWARFDie ChildDie
: Die
.children())
239 parseInlineInfo(Gsym
, CUI
, ChildDie
, Depth
+ 1, FI
, II
);
240 parent
.Children
.emplace_back(std::move(II
));
243 if (Tag
== dwarf::DW_TAG_subprogram
|| Tag
== dwarf::DW_TAG_lexical_block
) {
244 // skip this Die and just recurse down
245 for (DWARFDie ChildDie
: Die
.children())
246 parseInlineInfo(Gsym
, CUI
, ChildDie
, Depth
+ 1, FI
, parent
);
250 static void convertFunctionLineTable(raw_ostream
&Log
, CUInfo
&CUI
,
251 DWARFDie Die
, GsymCreator
&Gsym
,
253 std::vector
<uint32_t> RowVector
;
254 const uint64_t StartAddress
= FI
.startAddress();
255 const uint64_t EndAddress
= FI
.endAddress();
256 const uint64_t RangeSize
= EndAddress
- StartAddress
;
257 const object::SectionedAddress SecAddress
{
258 StartAddress
, object::SectionedAddress::UndefSection
};
261 if (!CUI
.LineTable
->lookupAddressRange(SecAddress
, RangeSize
, RowVector
)) {
262 // If we have a DW_TAG_subprogram but no line entries, fall back to using
263 // the DW_AT_decl_file an d DW_AT_decl_line if we have both attributes.
264 std::string FilePath
= Die
.getDeclFile(
265 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
);
266 if (FilePath
.empty())
269 dwarf::toUnsigned(Die
.findRecursively({dwarf::DW_AT_decl_line
}))) {
270 LineEntry
LE(StartAddress
, Gsym
.insertFile(FilePath
), *Line
);
271 FI
.OptLineTable
= LineTable();
272 FI
.OptLineTable
->push(LE
);
277 FI
.OptLineTable
= LineTable();
278 DWARFDebugLine::Row PrevRow
;
279 for (uint32_t RowIndex
: RowVector
) {
280 // Take file number and line/column from the row.
281 const DWARFDebugLine::Row
&Row
= CUI
.LineTable
->Rows
[RowIndex
];
282 const uint32_t FileIdx
= CUI
.DWARFToGSYMFileIndex(Gsym
, Row
.File
);
283 uint64_t RowAddress
= Row
.Address
.Address
;
284 // Watch out for a RowAddress that is in the middle of a line table entry
285 // in the DWARF. If we pass an address in between two line table entries
286 // we will get a RowIndex for the previous valid line table row which won't
287 // be contained in our function. This is usually a bug in the DWARF due to
288 // linker problems or LTO or other DWARF re-linking so it is worth emitting
289 // an error, but not worth stopping the creation of the GSYM.
290 if (!FI
.Range
.contains(RowAddress
)) {
291 if (RowAddress
< FI
.Range
.start()) {
292 Log
<< "error: DIE has a start address whose LowPC is between the "
293 "line table Row[" << RowIndex
<< "] with address "
294 << HEX64(RowAddress
) << " and the next one.\n";
295 Die
.dump(Log
, 0, DIDumpOptions::getForSingleDIE());
296 RowAddress
= FI
.Range
.start();
302 LineEntry
LE(RowAddress
, FileIdx
, Row
.Line
);
303 if (RowIndex
!= RowVector
[0] && Row
.Address
< PrevRow
.Address
) {
304 // We have seen full duplicate line tables for functions in some
305 // DWARF files. Watch for those here by checking the the last
306 // row was the function's end address (HighPC) and that the
307 // current line table entry's address is the same as the first
308 // line entry we already have in our "function_info.Lines". If
309 // so break out after printing a warning.
310 auto FirstLE
= FI
.OptLineTable
->first();
311 if (FirstLE
&& *FirstLE
== LE
) {
312 if (!Gsym
.isQuiet()) {
313 Log
<< "warning: duplicate line table detected for DIE:\n";
314 Die
.dump(Log
, 0, DIDumpOptions::getForSingleDIE());
317 // Print out (ignore if os == nulls as this is expensive)
318 Log
<< "error: line table has addresses that do not "
319 << "monotonically increase:\n";
320 for (uint32_t RowIndex2
: RowVector
) {
321 CUI
.LineTable
->Rows
[RowIndex2
].dump(Log
);
323 Die
.dump(Log
, 0, DIDumpOptions::getForSingleDIE());
328 // Skip multiple line entries for the same file and line.
329 auto LastLE
= FI
.OptLineTable
->last();
330 if (LastLE
&& LastLE
->File
== FileIdx
&& LastLE
->Line
== Row
.Line
)
332 // Only push a row if it isn't an end sequence. End sequence markers are
333 // included for the last address in a function or the last contiguous
334 // address in a sequence.
335 if (Row
.EndSequence
) {
336 // End sequence means that the next line entry could have a lower address
337 // that the previous entries. So we clear the previous row so we don't
338 // trigger the line table error about address that do not monotonically
340 PrevRow
= DWARFDebugLine::Row();
342 FI
.OptLineTable
->push(LE
);
346 // If not line table rows were added, clear the line table so we don't encode
347 // on in the GSYM file.
348 if (FI
.OptLineTable
->empty())
349 FI
.OptLineTable
= std::nullopt
;
352 void DwarfTransformer::handleDie(raw_ostream
&OS
, CUInfo
&CUI
, DWARFDie Die
) {
353 switch (Die
.getTag()) {
354 case dwarf::DW_TAG_subprogram
: {
355 Expected
<DWARFAddressRangesVector
> RangesOrError
= Die
.getAddressRanges();
356 if (!RangesOrError
) {
357 consumeError(RangesOrError
.takeError());
360 const DWARFAddressRangesVector
&Ranges
= RangesOrError
.get();
363 auto NameIndex
= getQualifiedNameIndex(Die
, CUI
.Language
, Gsym
);
365 OS
<< "error: function at " << HEX64(Die
.getOffset())
366 << " has no name\n ";
367 Die
.dump(OS
, 0, DIDumpOptions::getForSingleDIE());
371 // Create a function_info for each range
372 for (const DWARFAddressRange
&Range
: Ranges
) {
373 // The low PC must be less than the high PC. Many linkers don't remove
374 // DWARF for functions that don't get linked into the final executable.
375 // If both the high and low pc have relocations, linkers will often set
376 // the address values for both to the same value to indicate the function
377 // has been remove. Other linkers have been known to set the one or both
378 // PC values to a UINT32_MAX for 4 byte addresses and UINT64_MAX for 8
379 // byte addresses to indicate the function isn't valid. The check below
380 // tries to watch for these cases and abort if it runs into them.
381 if (Range
.LowPC
>= Range
.HighPC
|| CUI
.isHighestAddress(Range
.LowPC
))
384 // Many linkers can't remove DWARF and might set the LowPC to zero. Since
385 // high PC can be an offset from the low PC in more recent DWARF versions
386 // we need to watch for a zero'ed low pc which we do using
387 // ValidTextRanges below.
388 if (!Gsym
.IsValidTextAddress(Range
.LowPC
)) {
389 // We expect zero and -1 to be invalid addresses in DWARF depending
390 // on the linker of the DWARF. This indicates a function was stripped
391 // and the debug info wasn't able to be stripped from the DWARF. If
392 // the LowPC isn't zero or -1, then we should emit an error.
393 if (Range
.LowPC
!= 0) {
394 if (!Gsym
.isQuiet()) {
395 // Unexpected invalid address, emit a warning
396 OS
<< "warning: DIE has an address range whose start address is "
397 "not in any executable sections ("
398 << *Gsym
.GetValidTextRanges()
399 << ") and will not be processed:\n";
400 Die
.dump(OS
, 0, DIDumpOptions::getForSingleDIE());
407 FI
.Range
= {Range
.LowPC
, Range
.HighPC
};
408 FI
.Name
= *NameIndex
;
410 convertFunctionLineTable(OS
, CUI
, Die
, Gsym
, FI
);
412 if (hasInlineInfo(Die
, 0)) {
413 FI
.Inline
= InlineInfo();
414 FI
.Inline
->Name
= *NameIndex
;
415 FI
.Inline
->Ranges
.insert(FI
.Range
);
416 parseInlineInfo(Gsym
, CUI
, Die
, 0, FI
, *FI
.Inline
);
418 Gsym
.addFunctionInfo(std::move(FI
));
424 for (DWARFDie ChildDie
: Die
.children())
425 handleDie(OS
, CUI
, ChildDie
);
428 Error
DwarfTransformer::convert(uint32_t NumThreads
) {
429 size_t NumBefore
= Gsym
.getNumFunctionInfos();
430 auto getDie
= [&](DWARFUnit
&DwarfUnit
) -> DWARFDie
{
431 DWARFDie ReturnDie
= DwarfUnit
.getUnitDIE(false);
432 if (std::optional
<uint64_t> DWOId
= DwarfUnit
.getDWOId()) {
433 DWARFUnit
*DWOCU
= DwarfUnit
.getNonSkeletonUnitDIE(false).getDwarfUnit();
434 if (!DWOCU
->isDWOUnit()) {
435 std::string DWOName
= dwarf::toString(
436 DwarfUnit
.getUnitDIE().find(
437 {dwarf::DW_AT_dwo_name
, dwarf::DW_AT_GNU_dwo_name
}),
439 Log
<< "warning: Unable to retrieve DWO .debug_info section for "
442 ReturnDie
= DWOCU
->getUnitDIE(false);
447 if (NumThreads
== 1) {
448 // Parse all DWARF data from this thread, use the same string/file table
450 for (const auto &CU
: DICtx
.compile_units()) {
451 DWARFDie Die
= getDie(*CU
);
452 CUInfo
CUI(DICtx
, dyn_cast
<DWARFCompileUnit
>(CU
.get()));
453 handleDie(Log
, CUI
, Die
);
456 // LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
457 // front before we start accessing any DIEs since there might be
458 // cross compile unit references in the DWARF. If we don't do this we can
461 // We need to call getAbbreviations sequentially first so that getUnitDIE()
462 // only works with its local data.
463 for (const auto &CU
: DICtx
.compile_units())
464 CU
->getAbbreviations();
466 // Now parse all DIEs in case we have cross compile unit references in a
468 ThreadPool
pool(hardware_concurrency(NumThreads
));
469 for (const auto &CU
: DICtx
.compile_units())
470 pool
.async([&CU
]() { CU
->getUnitDIE(false /*CUDieOnly*/); });
473 // Now convert all DWARF to GSYM in a thread pool.
475 for (const auto &CU
: DICtx
.compile_units()) {
476 DWARFDie Die
= getDie(*CU
);
478 CUInfo
CUI(DICtx
, dyn_cast
<DWARFCompileUnit
>(CU
.get()));
479 pool
.async([this, CUI
, &LogMutex
, Die
]() mutable {
480 std::string ThreadLogStorage
;
481 raw_string_ostream
ThreadOS(ThreadLogStorage
);
482 handleDie(ThreadOS
, CUI
, Die
);
484 if (!ThreadLogStorage
.empty()) {
485 // Print ThreadLogStorage lines into an actual stream under a lock
486 std::lock_guard
<std::mutex
> guard(LogMutex
);
487 Log
<< ThreadLogStorage
;
494 size_t FunctionsAddedCount
= Gsym
.getNumFunctionInfos() - NumBefore
;
495 Log
<< "Loaded " << FunctionsAddedCount
<< " functions from DWARF.\n";
496 return Error::success();
499 llvm::Error
DwarfTransformer::verify(StringRef GsymPath
) {
500 Log
<< "Verifying GSYM file \"" << GsymPath
<< "\":\n";
502 auto Gsym
= GsymReader::openFile(GsymPath
);
504 return Gsym
.takeError();
506 auto NumAddrs
= Gsym
->getNumAddresses();
507 DILineInfoSpecifier
DLIS(
508 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
,
509 DILineInfoSpecifier::FunctionNameKind::LinkageName
);
510 std::string gsymFilename
;
511 for (uint32_t I
= 0; I
< NumAddrs
; ++I
) {
512 auto FuncAddr
= Gsym
->getAddress(I
);
514 return createStringError(std::errc::invalid_argument
,
515 "failed to extract address[%i]", I
);
517 auto FI
= Gsym
->getFunctionInfo(*FuncAddr
);
519 return createStringError(std::errc::invalid_argument
,
520 "failed to extract function info for address 0x%"
523 for (auto Addr
= *FuncAddr
; Addr
< *FuncAddr
+ FI
->size(); ++Addr
) {
524 const object::SectionedAddress SectAddr
{
525 Addr
, object::SectionedAddress::UndefSection
};
526 auto LR
= Gsym
->lookup(Addr
);
528 return LR
.takeError();
530 auto DwarfInlineInfos
=
531 DICtx
.getInliningInfoForAddress(SectAddr
, DLIS
);
532 uint32_t NumDwarfInlineInfos
= DwarfInlineInfos
.getNumberOfFrames();
533 if (NumDwarfInlineInfos
== 0) {
534 DwarfInlineInfos
.addFrame(
535 DICtx
.getLineInfoForAddress(SectAddr
, DLIS
));
538 // Check for 1 entry that has no file and line info
539 if (NumDwarfInlineInfos
== 1 &&
540 DwarfInlineInfos
.getFrame(0).FileName
== "<invalid>") {
541 DwarfInlineInfos
= DIInliningInfo();
542 NumDwarfInlineInfos
= 0;
544 if (NumDwarfInlineInfos
> 0 &&
545 NumDwarfInlineInfos
!= LR
->Locations
.size()) {
546 Log
<< "error: address " << HEX64(Addr
) << " has "
547 << NumDwarfInlineInfos
<< " DWARF inline frames and GSYM has "
548 << LR
->Locations
.size() << "\n";
549 Log
<< " " << NumDwarfInlineInfos
<< " DWARF frames:\n";
550 for (size_t Idx
= 0; Idx
< NumDwarfInlineInfos
; ++Idx
) {
551 const auto &dii
= DwarfInlineInfos
.getFrame(Idx
);
552 Log
<< " [" << Idx
<< "]: " << dii
.FunctionName
<< " @ "
553 << dii
.FileName
<< ':' << dii
.Line
<< '\n';
555 Log
<< " " << LR
->Locations
.size() << " GSYM frames:\n";
556 for (size_t Idx
= 0, count
= LR
->Locations
.size();
557 Idx
< count
; ++Idx
) {
558 const auto &gii
= LR
->Locations
[Idx
];
559 Log
<< " [" << Idx
<< "]: " << gii
.Name
<< " @ " << gii
.Dir
560 << '/' << gii
.Base
<< ':' << gii
.Line
<< '\n';
562 DwarfInlineInfos
= DICtx
.getInliningInfoForAddress(SectAddr
, DLIS
);
563 Gsym
->dump(Log
, *FI
);
567 for (size_t Idx
= 0, count
= LR
->Locations
.size(); Idx
< count
;
569 const auto &gii
= LR
->Locations
[Idx
];
570 if (Idx
< NumDwarfInlineInfos
) {
571 const auto &dii
= DwarfInlineInfos
.getFrame(Idx
);
572 gsymFilename
= LR
->getSourceFile(Idx
);
573 // Verify function name
574 if (dii
.FunctionName
.find(gii
.Name
.str()) != 0)
575 Log
<< "error: address " << HEX64(Addr
) << " DWARF function \""
576 << dii
.FunctionName
.c_str()
577 << "\" doesn't match GSYM function \"" << gii
.Name
<< "\"\n";
578 // Verify source file path
579 if (dii
.FileName
!= gsymFilename
)
580 Log
<< "error: address " << HEX64(Addr
) << " DWARF path \""
581 << dii
.FileName
.c_str() << "\" doesn't match GSYM path \""
582 << gsymFilename
.c_str() << "\"\n";
583 // Verify source file line
584 if (dii
.Line
!= gii
.Line
)
585 Log
<< "error: address " << HEX64(Addr
) << " DWARF line "
586 << dii
.Line
<< " != GSYM line " << gii
.Line
<< "\n";
591 return Error::success();