[llvm] 4f902d2 - [llvm-dwarfdump] Make --verify for .debug_names multithreaded. (#127281)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 3 14:02:31 PDT 2025
Author: Alexander Yermolovich
Date: 2025-04-03T14:02:27-07:00
New Revision: 4f902d2425e59bd182390702de23d5cec3467fc2
URL: https://github.com/llvm/llvm-project/commit/4f902d2425e59bd182390702de23d5cec3467fc2
DIFF: https://github.com/llvm/llvm-project/commit/4f902d2425e59bd182390702de23d5cec3467fc2.diff
LOG: [llvm-dwarfdump] Make --verify for .debug_names multithreaded. (#127281)
This PR makes verification of .debug_names acceleration table
multithreaded. In local testing it improves verification of clang
.debug_names from four minutes to under a minute.
This PR relies on a current mechanism of extracting DIEs into a vector.
Future improvements can include creating API to extract one DIE at a
time, or grouping Entires into buckets by CUs and extracting before
parallel step.
Single Thread
4:12.37 real, 246.88 user, 3.54 sys, 0 amem,10232004 mmem
Multi Thread
0:49.40 real, 612.84 user, 515.73 sys, 0 amem, 11226292 mmem
Added:
Modified:
llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness.s
llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
index 9d7ac12cefdc8..cef5fa1f2ee53 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
@@ -770,6 +770,7 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
}
public:
+ using size_type = size_t;
using iterator_category = std::input_iterator_tag;
using value_type = NameTableEntry;
using
diff erence_type = uint32_t;
@@ -793,6 +794,16 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
next();
return I;
}
+ /// Accesses entry at specific index (1-based internally, 0-based
+ /// externally). For example how this is used in parallelForEach.
+ reference operator[](size_type idx) {
+ return CurrentIndex->getNameTableEntry(idx + 1);
+ }
+ /// Computes
diff erence between iterators (used in parallelForEach).
+
diff erence_type operator-(const NameIterator &other) const {
+ assert(CurrentIndex == other.CurrentIndex);
+ return this->CurrentName - other.CurrentName;
+ }
friend bool operator==(const NameIterator &A, const NameIterator &B) {
return A.CurrentIndex == B.CurrentIndex && A.CurrentName == B.CurrentName;
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
index 595215ba35dd5..717f9cedc4ee3 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
@@ -9,6 +9,7 @@
#ifndef LLVM_DEBUGINFO_DWARF_DWARFVERIFIER_H
#define LLVM_DEBUGINFO_DWARF_DWARFVERIFIER_H
+#include "llvm/ADT/StringMap.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
@@ -16,6 +17,7 @@
#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
#include <cstdint>
#include <map>
+#include <mutex>
#include <set>
namespace llvm {
@@ -37,7 +39,9 @@ struct AggregationData {
class OutputCategoryAggregator {
private:
+ std::mutex WriteMutex;
std::map<std::string, AggregationData, std::less<>> Aggregation;
+ uint64_t NumErrors = 0;
bool IncludeDetail;
public:
@@ -52,6 +56,8 @@ class OutputCategoryAggregator {
void EnumerateDetailedResultsFor(
StringRef category,
std::function<void(StringRef, unsigned)> handleCounts);
+ /// Return the number of errors that have been reported.
+ uint64_t GetNumErrors() const { return NumErrors; }
};
/// A class that verifies DWARF debug information given a DWARF Context.
@@ -114,6 +120,7 @@ class DWARFVerifier {
bool IsObjectFile;
bool IsMachOObject;
using ReferenceMap = std::map<uint64_t, std::set<uint64_t>>;
+ std::mutex AccessMutex;
raw_ostream &error() const;
raw_ostream &warn() const;
@@ -274,21 +281,23 @@ class DWARFVerifier {
/// \param SectionName the name of the table we're verifying
///
/// \returns The number of errors occurred during verification
- unsigned verifyAppleAccelTable(const DWARFSection *AccelSection,
- DataExtractor *StrData,
- const char *SectionName);
-
- unsigned verifyDebugNamesCULists(const DWARFDebugNames &AccelTable);
- unsigned verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
- const DataExtractor &StrData);
- unsigned verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI);
- unsigned verifyNameIndexAttribute(const DWARFDebugNames::NameIndex &NI,
- const DWARFDebugNames::Abbrev &Abbr,
- DWARFDebugNames::AttributeEncoding AttrEnc);
- unsigned verifyNameIndexEntries(const DWARFDebugNames::NameIndex &NI,
- const DWARFDebugNames::NameTableEntry &NTE);
- unsigned verifyNameIndexCompleteness(const DWARFDie &Die,
- const DWARFDebugNames::NameIndex &NI);
+ void verifyAppleAccelTable(const DWARFSection *AccelSection,
+ DataExtractor *StrData, const char *SectionName);
+
+ void verifyDebugNamesCULists(const DWARFDebugNames &AccelTable);
+ void verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
+ const DataExtractor &StrData);
+ void verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI);
+ void verifyNameIndexAttribute(const DWARFDebugNames::NameIndex &NI,
+ const DWARFDebugNames::Abbrev &Abbr,
+ DWARFDebugNames::AttributeEncoding AttrEnc);
+ void verifyNameIndexEntries(
+ const DWARFDebugNames::NameIndex &NI,
+ const DWARFDebugNames::NameTableEntry &NTE,
+ const DenseMap<uint64_t, DWARFUnit *> &CUOffsetsToDUMap);
+ void verifyNameIndexCompleteness(
+ const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI,
+ const StringMap<DenseSet<uint64_t>> &NamesToDieOffsets);
/// Verify that the DWARF v5 accelerator table is valid.
///
@@ -307,8 +316,8 @@ class DWARFVerifier {
/// \param StrData string section
///
/// \returns The number of errors occurred during verification
- unsigned verifyDebugNames(const DWARFSection &AccelSection,
- const DataExtractor &StrData);
+ void verifyDebugNames(const DWARFSection &AccelSection,
+ const DataExtractor &StrData);
public:
DWARFVerifier(raw_ostream &S, DWARFContext &D,
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
index 69027500ab51d..690a2e5fee9de 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -33,6 +33,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
@@ -1106,10 +1107,9 @@ bool DWARFVerifier::handleDebugLine() {
return NumDebugLineErrors == 0;
}
-unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
- DataExtractor *StrData,
- const char *SectionName) {
- unsigned NumErrors = 0;
+void DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
+ DataExtractor *StrData,
+ const char *SectionName) {
DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection,
DCtx.isLittleEndian(), 0);
AppleAcceleratorTable AccelTable(AccelSectionData, *StrData);
@@ -1121,7 +1121,7 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
ErrorCategory.Report("Section is too small to fit a section header", [&]() {
error() << "Section is too small to fit a section header.\n";
});
- return 1;
+ return;
}
// Verify that the section is not too short.
@@ -1129,7 +1129,7 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
std::string Msg = toString(std::move(E));
ErrorCategory.Report("Section is too small to fit a section header",
[&]() { error() << Msg << '\n'; });
- return 1;
+ return;
}
// Verify that all buckets have a valid hash index or are empty.
@@ -1147,7 +1147,6 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx,
HashIdx);
});
- ++NumErrors;
}
}
uint32_t NumAtoms = AccelTable.getAtomsDesc().size();
@@ -1155,13 +1154,13 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
ErrorCategory.Report("No atoms", [&]() {
error() << "No atoms: failed to read HashData.\n";
});
- return 1;
+ return;
}
if (!AccelTable.validateForms()) {
ErrorCategory.Report("Unsupported form", [&]() {
error() << "Unsupported form: failed to read HashData.\n";
});
- return 1;
+ return;
}
for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) {
@@ -1176,7 +1175,6 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
"0x%08" PRIx64 ".\n",
HashIdx, HashDataOffset);
});
- ++NumErrors;
}
uint64_t StrpOffset;
@@ -1207,8 +1205,6 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset,
HashDataIdx, Offset, Name);
});
-
- ++NumErrors;
continue;
}
if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) {
@@ -1218,74 +1214,70 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
<< dwarf::TagString(Die.getTag()) << " of DIE["
<< HashDataIdx << "].\n";
});
- ++NumErrors;
}
}
- ++StringCount;
}
}
- return NumErrors;
}
-unsigned
-DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {
+void DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {
// A map from CU offset to the (first) Name Index offset which claims to index
// this CU.
DenseMap<uint64_t, uint64_t> CUMap;
- const uint64_t NotIndexed = std::numeric_limits<uint64_t>::max();
-
CUMap.reserve(DCtx.getNumCompileUnits());
+
+ DenseSet<uint64_t> CUOffsets;
for (const auto &CU : DCtx.compile_units())
- CUMap[CU->getOffset()] = NotIndexed;
+ CUOffsets.insert(CU->getOffset());
- unsigned NumErrors = 0;
- for (const DWARFDebugNames::NameIndex &NI : AccelTable) {
+ parallelForEach(AccelTable, [&](const DWARFDebugNames::NameIndex &NI) {
if (NI.getCUCount() == 0) {
ErrorCategory.Report("Name Index doesn't index any CU", [&]() {
error() << formatv("Name Index @ {0:x} does not index any CU\n",
NI.getUnitOffset());
});
- ++NumErrors;
- continue;
+ return;
}
for (uint32_t CU = 0, End = NI.getCUCount(); CU < End; ++CU) {
uint64_t Offset = NI.getCUOffset(CU);
- auto Iter = CUMap.find(Offset);
-
- if (Iter == CUMap.end()) {
+ if (!CUOffsets.count(Offset)) {
ErrorCategory.Report("Name Index references non-existing CU", [&]() {
error() << formatv(
"Name Index @ {0:x} references a non-existing CU @ {1:x}\n",
NI.getUnitOffset(), Offset);
});
- ++NumErrors;
continue;
}
-
- if (Iter->second != NotIndexed) {
+ uint64_t DuplicateCUOffset = 0;
+ {
+ std::lock_guard<std::mutex> Lock(AccessMutex);
+ auto Iter = CUMap.find(Offset);
+ if (Iter != CUMap.end())
+ DuplicateCUOffset = Iter->second;
+ else
+ CUMap[Offset] = NI.getUnitOffset();
+ }
+ if (DuplicateCUOffset) {
ErrorCategory.Report("Duplicate Name Index", [&]() {
error() << formatv(
"Name Index @ {0:x} references a CU @ {1:x}, but "
"this CU is already indexed by Name Index @ {2:x}\n",
- NI.getUnitOffset(), Offset, Iter->second);
+ NI.getUnitOffset(), Offset, DuplicateCUOffset);
});
continue;
}
- Iter->second = NI.getUnitOffset();
}
- }
+ });
- for (const auto &KV : CUMap) {
- if (KV.second == NotIndexed)
- warn() << formatv("CU @ {0:x} not covered by any Name Index\n", KV.first);
+ for (const auto &CU : DCtx.compile_units()) {
+ if (CUMap.count(CU->getOffset()) == 0)
+ warn() << formatv("CU @ {0:x} not covered by any Name Index\n",
+ CU->getOffset());
}
-
- return NumErrors;
}
-unsigned
-DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
- const DataExtractor &StrData) {
+void DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
+ const DataExtractor &StrData) {
struct BucketInfo {
uint32_t Bucket;
uint32_t Index;
@@ -1295,17 +1287,17 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; }
};
- uint32_t NumErrors = 0;
if (NI.getBucketCount() == 0) {
warn() << formatv("Name Index @ {0:x} does not contain a hash table.\n",
NI.getUnitOffset());
- return NumErrors;
+ return;
}
// Build up a list of (Bucket, Index) pairs. We use this later to verify that
// each Name is reachable from the appropriate bucket.
std::vector<BucketInfo> BucketStarts;
BucketStarts.reserve(NI.getBucketCount() + 1);
+ const uint64_t OrigNumberOfErrors = ErrorCategory.GetNumErrors();
for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) {
uint32_t Index = NI.getBucketArrayEntry(Bucket);
if (Index > NI.getNameCount()) {
@@ -1315,7 +1307,6 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
Bucket, NI.getUnitOffset(), Index,
NI.getNameCount());
});
- ++NumErrors;
continue;
}
if (Index > 0)
@@ -1325,8 +1316,8 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
// If there were any buckets with invalid values, skip further checks as they
// will likely produce many errors which will only confuse the actual root
// problem.
- if (NumErrors > 0)
- return NumErrors;
+ if (OrigNumberOfErrors != ErrorCategory.GetNumErrors())
+ return;
// Sort the list in the order of increasing "Index" entries.
array_pod_sort(BucketStarts.begin(), BucketStarts.end());
@@ -1352,7 +1343,6 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
"are not covered by the hash table.\n",
NI.getUnitOffset(), NextUncovered, B.Index - 1);
});
- ++NumErrors;
}
uint32_t Idx = B.Index;
@@ -1374,7 +1364,6 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
NI.getUnitOffset(), B.Bucket, FirstHash,
FirstHash % NI.getBucketCount());
});
- ++NumErrors;
}
// This find the end of this bucket and also verifies that all the hashes in
@@ -1395,17 +1384,15 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
"the Name Index hash is {4:x}\n",
NI.getUnitOffset(), Str, Idx, caseFoldingDjbHash(Str), Hash);
});
- ++NumErrors;
}
-
++Idx;
}
NextUncovered = std::max(NextUncovered, Idx);
}
- return NumErrors;
+ return;
}
-unsigned DWARFVerifier::verifyNameIndexAttribute(
+void DWARFVerifier::verifyNameIndexAttribute(
const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr,
DWARFDebugNames::AttributeEncoding AttrEnc) {
StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form);
@@ -1416,7 +1403,7 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,
AttrEnc.Form);
});
- return 1;
+ return;
}
if (AttrEnc.Index == DW_IDX_type_hash) {
@@ -1427,9 +1414,9 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
"uses an unexpected form {2} (should be {3}).\n",
NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8);
});
- return 1;
+ return;
}
- return 0;
+ return;
}
if (AttrEnc.Index == dwarf::DW_IDX_parent) {
@@ -1443,9 +1430,9 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
"DW_FORM_ref4 or DW_FORM_flag_present).\n",
NI.getUnitOffset(), Abbr.Code, AttrEnc.Form);
});
- return 1;
+ return;
}
- return 0;
+ return;
}
// A list of known index attributes and their expected form classes.
@@ -1470,7 +1457,7 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an "
"unknown index attribute: {2}.\n",
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index);
- return 0;
+ return;
}
if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) {
@@ -1480,14 +1467,13 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,
AttrEnc.Form, Iter->ClassName);
});
- return 1;
+ return;
}
- return 0;
+ return;
}
-unsigned
-DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
- unsigned NumErrors = 0;
+void DWARFVerifier::verifyNameIndexAbbrevs(
+ const DWARFDebugNames::NameIndex &NI) {
for (const auto &Abbrev : NI.getAbbrevs()) {
StringRef TagName = dwarf::TagString(Abbrev.Tag);
if (TagName.empty()) {
@@ -1505,10 +1491,9 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
"multiple {2} attributes.\n",
NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index);
});
- ++NumErrors;
continue;
}
- NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc);
+ verifyNameIndexAttribute(NI, Abbrev, AttrEnc);
}
if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit) &&
@@ -1519,7 +1504,6 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
"or DW_IDX_type_unit attribute.\n",
NI.getUnitOffset(), Abbrev.Code);
});
- ++NumErrors;
}
if (!Attributes.count(dwarf::DW_IDX_die_offset)) {
ErrorCategory.Report("Abbreviate in NameIndex missing attribute", [&]() {
@@ -1527,12 +1511,12 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
"NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n",
NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset);
});
- ++NumErrors;
}
}
- return NumErrors;
}
+/// Constructs a full name for a DIE. Potentially it does recursive lookup on
+/// DIEs. This can lead to extraction of DIEs in a
diff erent CU or TU.
static SmallVector<std::string, 3> getNames(const DWARFDie &DIE,
bool IncludeStrippedTemplateNames,
bool IncludeObjCNames = true,
@@ -1571,9 +1555,10 @@ static SmallVector<std::string, 3> getNames(const DWARFDie &DIE,
return Result;
}
-unsigned DWARFVerifier::verifyNameIndexEntries(
+void DWARFVerifier::verifyNameIndexEntries(
const DWARFDebugNames::NameIndex &NI,
- const DWARFDebugNames::NameTableEntry &NTE) {
+ const DWARFDebugNames::NameTableEntry &NTE,
+ const DenseMap<uint64_t, DWARFUnit *> &CUOffsetsToDUMap) {
const char *CStr = NTE.getString();
if (!CStr) {
ErrorCategory.Report("Unable to get string associated with name", [&]() {
@@ -1581,11 +1566,9 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
"with name {1}.\n",
NI.getUnitOffset(), NTE.getIndex());
});
- return 1;
+ return;
}
StringRef Str(CStr);
-
- unsigned NumErrors = 0;
unsigned NumEntries = 0;
uint64_t EntryID = NTE.getEntryOffset();
uint64_t NextEntryID = EntryID;
@@ -1601,7 +1584,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
"invalid CU index ({2}).\n",
NI.getUnitOffset(), EntryID, *CUIndex);
});
- ++NumErrors;
continue;
}
const uint32_t NumLocalTUs = NI.getLocalTUCount();
@@ -1612,7 +1594,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
"invalid TU index ({2}).\n",
NI.getUnitOffset(), EntryID, *TUIndex);
});
- ++NumErrors;
continue;
}
std::optional<uint64_t> UnitOffset;
@@ -1640,7 +1621,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
"foreign TU index ({2}) with no CU index.\n",
NI.getUnitOffset(), EntryID, *TUIndex);
});
- ++NumErrors;
continue;
}
} else {
@@ -1668,7 +1648,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
"invalid CU or TU offset {2:x}.\n",
NI.getUnitOffset(), EntryID, *UnitOffset);
});
- ++NumErrors;
continue;
}
// This function will try to get the non skeleton unit DIE, but if it is
@@ -1682,9 +1661,15 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
// call to properly deal with it. It isn't clear that getNonSkeletonUnitDIE
// will return the unit DIE of DU if we aren't able to get the .dwo file,
// but that is what the function currently does.
+ DWARFUnit *NonSkeletonUnit = nullptr;
+ if (DU->getDWOId()) {
+ auto Iter = CUOffsetsToDUMap.find(DU->getOffset());
+ NonSkeletonUnit = Iter->second;
+ } else {
+ NonSkeletonUnit = DU;
+ }
DWARFDie UnitDie = DU->getUnitDIE();
- DWARFDie NonSkeletonUnitDie = DU->getNonSkeletonUnitDIE();
- if (DU->getDWOId() && UnitDie == NonSkeletonUnitDie) {
+ if (DU->getDWOId() && !NonSkeletonUnit->isDWOUnit()) {
ErrorCategory.Report("Unable to get load .dwo file", [&]() {
error() << formatv(
"Name Index @ {0:x}: Entry @ {1:x} unable to load "
@@ -1693,10 +1678,9 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
dwarf::toString(UnitDie.find({DW_AT_dwo_name, DW_AT_GNU_dwo_name})),
*UnitOffset);
});
- ++NumErrors;
continue;
}
- DWARFUnit *NonSkeletonUnit = nullptr;
+
if (TUIndex && *TUIndex >= NumLocalTUs) {
// We have a foreign TU index, which either means we have a .dwo file
// that has one or more type units, or we have a .dwp file with one or
@@ -1707,17 +1691,16 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
// above, so we know we have the right file.
const uint32_t ForeignTUIdx = *TUIndex - NumLocalTUs;
const uint64_t TypeSig = NI.getForeignTUSignature(ForeignTUIdx);
- llvm::DWARFContext &SkeletonDCtx =
- NonSkeletonUnitDie.getDwarfUnit()->getContext();
+ llvm::DWARFContext &NonSkeletonDCtx = NonSkeletonUnit->getContext();
// Now find the type unit from the type signature and then update the
// NonSkeletonUnitDie to point to the actual type unit in the .dwo/.dwp.
NonSkeletonUnit =
- SkeletonDCtx.getTypeUnitForHash(TypeSig, /*IsDWO=*/true);
- NonSkeletonUnitDie = NonSkeletonUnit->getUnitDIE(true);
+ NonSkeletonDCtx.getTypeUnitForHash(TypeSig, /*IsDWO=*/true);
// If we have foreign type unit in a DWP file, then we need to ignore
// any entries from type units that don't match the one that made it into
// the .dwp file.
- if (SkeletonDCtx.isDWP()) {
+ if (NonSkeletonDCtx.isDWP()) {
+ DWARFDie NonSkeletonUnitDie = NonSkeletonUnit->getUnitDIE(true);
StringRef DUDwoName = dwarf::toStringRef(
UnitDie.find({DW_AT_dwo_name, DW_AT_GNU_dwo_name}));
StringRef TUDwoName = dwarf::toStringRef(
@@ -1725,8 +1708,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
if (DUDwoName != TUDwoName)
continue; // Skip this TU, it isn't the one in the .dwp file.
}
- } else {
- NonSkeletonUnit = NonSkeletonUnitDie.getDwarfUnit();
}
uint64_t DIEOffset =
NonSkeletonUnit->getOffset() + *EntryOr->getDIEUnitOffset();
@@ -1743,14 +1724,12 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
continue;
}
DWARFDie DIE = NonSkeletonUnit->getDIEForOffset(DIEOffset);
-
if (!DIE) {
ErrorCategory.Report("NameIndex references nonexistent DIE", [&]() {
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a "
"non-existing DIE @ {2:x}.\n",
NI.getUnitOffset(), EntryID, DIEOffset);
});
- ++NumErrors;
continue;
}
// Only compare the DIE we found's DWARFUnit offset if the DIE lives in
@@ -1766,7 +1745,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
NI.getUnitOffset(), EntryID, DIEOffset, *UnitOffset,
DIE.getDwarfUnit()->getOffset());
});
- ++NumErrors;
}
if (DIE.getTag() != EntryOr->tag()) {
ErrorCategory.Report("Name Index contains mismatched Tag of DIE", [&]() {
@@ -1776,7 +1754,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(),
DIE.getTag());
});
- ++NumErrors;
}
// We allow an extra name for functions: their name without any template
@@ -1792,7 +1769,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
NI.getUnitOffset(), EntryID, DIEOffset, Str,
make_range(EntryNames.begin(), EntryNames.end()));
});
- ++NumErrors;
}
}
handleAllErrors(
@@ -1806,7 +1782,6 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
"not associated with any entries.\n",
NI.getUnitOffset(), NTE.getIndex(), Str);
});
- ++NumErrors;
},
[&](const ErrorInfoBase &Info) {
ErrorCategory.Report("Uncategorized NameIndex error", [&]() {
@@ -1814,9 +1789,7 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
NI.getUnitOffset(), NTE.getIndex(), Str,
Info.message());
});
- ++NumErrors;
});
- return NumErrors;
}
static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) {
@@ -1844,8 +1817,9 @@ static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) {
return false;
}
-unsigned DWARFVerifier::verifyNameIndexCompleteness(
- const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) {
+void DWARFVerifier::verifyNameIndexCompleteness(
+ const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI,
+ const StringMap<DenseSet<uint64_t>> &NamesToDieOffsets) {
// First check, if the Die should be indexed. The code follows the DWARF v5
// wording as closely as possible.
@@ -1853,7 +1827,7 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
// "All non-defining declarations (that is, debugging information entries
// with a DW_AT_declaration attribute) are excluded."
if (Die.find(DW_AT_declaration))
- return 0;
+ return;
// "DW_TAG_namespace debugging information entries without a DW_AT_name
// attribute are included with the name “(anonymous namespace)”.
@@ -1871,7 +1845,7 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
auto EntryNames = getNames(Die, IncludeStrippedTemplateNames,
IncludeObjCNames, IncludeLinkageName);
if (EntryNames.empty())
- return 0;
+ return;
// We deviate from the specification here, which says:
// "The name index must contain an entry for each debugging information entry
@@ -1882,7 +1856,7 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
// Compile units and modules have names but shouldn't be indexed.
case DW_TAG_compile_unit:
case DW_TAG_module:
- return 0;
+ return;
// Function and template parameters are not globally visible, so we shouldn't
// index them.
@@ -1891,22 +1865,22 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
case DW_TAG_template_type_parameter:
case DW_TAG_GNU_template_parameter_pack:
case DW_TAG_GNU_template_template_param:
- return 0;
+ return;
// Object members aren't globally visible.
case DW_TAG_member:
- return 0;
+ return;
// According to a strict reading of the specification, enumerators should not
// be indexed (and LLVM currently does not do that). However, this causes
// problems for the debuggers, so we may need to reconsider this.
case DW_TAG_enumerator:
- return 0;
+ return;
// Imported declarations should not be indexed according to the specification
// and LLVM currently does not do that.
case DW_TAG_imported_declaration:
- return 0;
+ return;
// "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging
// information entries without an address attribute (DW_AT_low_pc,
@@ -1917,7 +1891,7 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
if (Die.findRecursively(
{DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc}))
break;
- return 0;
+ return;
// "DW_TAG_variable debugging information entries with a DW_AT_location
// attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are
@@ -1927,7 +1901,7 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
case DW_TAG_variable:
if (isVariableIndexable(Die, DCtx))
break;
- return 0;
+ return;
default:
break;
@@ -1935,12 +1909,10 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
// Now we know that our Die should be present in the Index. Let's check if
// that's the case.
- unsigned NumErrors = 0;
uint64_t DieUnitOffset = Die.getOffset() - Die.getDwarfUnit()->getOffset();
for (StringRef Name : EntryNames) {
- if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) {
- return E.getDIEUnitOffset() == DieUnitOffset;
- })) {
+ auto iter = NamesToDieOffsets.find(Name);
+ if (iter == NamesToDieOffsets.end() || !iter->second.count(DieUnitOffset)) {
ErrorCategory.Report(
"Name Index DIE entry missing name",
llvm::dwarf::TagString(Die.getTag()), [&]() {
@@ -1949,15 +1921,47 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
"name {3} missing.\n",
NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name);
});
- ++NumErrors;
}
}
- return NumErrors;
}
-unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
- const DataExtractor &StrData) {
- unsigned NumErrors = 0;
+/// Extracts all the data for CU/TUs so we can access it in parallel without
+/// locks.
+static void extractCUsTus(DWARFContext &DCtx) {
+ // Abbrev DeclSet is shared beween the units.
+ for (auto &CUTU : DCtx.normal_units()) {
+ CUTU->getUnitDIE();
+ CUTU->getBaseAddress();
+ }
+ parallelForEach(DCtx.normal_units(), [&](const auto &CUTU) {
+ if (Error E = CUTU->tryExtractDIEsIfNeeded(false))
+ DCtx.getRecoverableErrorHandler()(std::move(E));
+ });
+
+ // Invoking getNonSkeletonUnitDIE() sets up all the base pointers for DWO
+ // Units. This is needed for getBaseAddress().
+ for (const auto &CU : DCtx.compile_units()) {
+ if (!CU->getDWOId())
+ continue;
+ DWARFContext &NonSkeletonContext =
+ CU->getNonSkeletonUnitDIE().getDwarfUnit()->getContext();
+ // Iterates over CUs and TUs.
+ for (auto &CUTU : NonSkeletonContext.dwo_units()) {
+ CUTU->getUnitDIE();
+ CUTU->getBaseAddress();
+ }
+ parallelForEach(NonSkeletonContext.dwo_units(), [&](const auto &CUTU) {
+ if (Error E = CUTU->tryExtractDIEsIfNeeded(false))
+ DCtx.getRecoverableErrorHandler()(std::move(E));
+ });
+ // If context is for DWP we only need to extract once.
+ if (NonSkeletonContext.isDWP())
+ break;
+ }
+}
+
+void DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
+ const DataExtractor &StrData) {
DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), AccelSection,
DCtx.isLittleEndian(), 0);
DWARFDebugNames AccelTable(AccelSectionData, StrData);
@@ -1970,67 +1974,119 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
std::string Msg = toString(std::move(E));
ErrorCategory.Report("Accelerator Table Error",
[&]() { error() << Msg << '\n'; });
- return 1;
+ return;
}
-
- NumErrors += verifyDebugNamesCULists(AccelTable);
- for (const auto &NI : AccelTable)
- NumErrors += verifyNameIndexBuckets(NI, StrData);
+ const uint64_t OriginalNumErrors = ErrorCategory.GetNumErrors();
+ verifyDebugNamesCULists(AccelTable);
for (const auto &NI : AccelTable)
- NumErrors += verifyNameIndexAbbrevs(NI);
+ verifyNameIndexBuckets(NI, StrData);
+ parallelForEach(AccelTable, [&](const DWARFDebugNames::NameIndex &NI) {
+ verifyNameIndexAbbrevs(NI);
+ });
// Don't attempt Entry validation if any of the previous checks found errors
- if (NumErrors > 0)
- return NumErrors;
- for (const auto &NI : AccelTable)
- for (const DWARFDebugNames::NameTableEntry &NTE : NI)
- NumErrors += verifyNameIndexEntries(NI, NTE);
+ if (OriginalNumErrors != ErrorCategory.GetNumErrors())
+ return;
+ DenseMap<uint64_t, DWARFUnit *> CUOffsetsToDUMap;
+ for (const auto &CU : DCtx.compile_units()) {
+ if (!(CU->getVersion() >= 5 && CU->getDWOId()))
+ continue;
+ CUOffsetsToDUMap[CU->getOffset()] =
+ CU->getNonSkeletonUnitDIE().getDwarfUnit();
+ }
+ extractCUsTus(DCtx);
+ for (const DWARFDebugNames::NameIndex &NI : AccelTable) {
+ parallelForEach(NI, [&](DWARFDebugNames::NameTableEntry NTE) {
+ verifyNameIndexEntries(NI, NTE, CUOffsetsToDUMap);
+ });
+ }
- for (const std::unique_ptr<DWARFUnit> &U : DCtx.info_section_units()) {
- if (const DWARFDebugNames::NameIndex *NI =
- AccelTable.getCUOrTUNameIndex(U->getOffset())) {
- DWARFCompileUnit *CU = dyn_cast<DWARFCompileUnit>(U.get());
+ auto populateNameToOffset =
+ [&](const DWARFDebugNames::NameIndex &NI,
+ StringMap<DenseSet<uint64_t>> &NamesToDieOffsets) {
+ for (const DWARFDebugNames::NameTableEntry &NTE : NI) {
+ const char *tName = NTE.getString();
+ const std::string Name = tName ? std::string(tName) : "";
+ uint64_t EntryID = NTE.getEntryOffset();
+ Expected<DWARFDebugNames::Entry> EntryOr = NI.getEntry(&EntryID);
+ auto Iter = NamesToDieOffsets.insert({Name, DenseSet<uint64_t>(3)});
+ for (; EntryOr; EntryOr = NI.getEntry(&EntryID)) {
+ if (std::optional<uint64_t> DieOffset = EntryOr->getDIEUnitOffset())
+ Iter.first->second.insert(*DieOffset);
+ }
+ handleAllErrors(
+ EntryOr.takeError(),
+ [&](const DWARFDebugNames::SentinelError &) {
+ if (!NamesToDieOffsets.empty())
+ return;
+ ErrorCategory.Report(
+ "NameIndex Name is not associated with any entries", [&]() {
+ error()
+ << formatv("Name Index @ {0:x}: Name {1} ({2}) is "
+ "not associated with any entries.\n",
+ NI.getUnitOffset(), NTE.getIndex(), Name);
+ });
+ },
+ [&](const ErrorInfoBase &Info) {
+ ErrorCategory.Report("Uncategorized NameIndex error", [&]() {
+ error() << formatv(
+ "Name Index @ {0:x}: Name {1} ({2}): {3}\n",
+ NI.getUnitOffset(), NTE.getIndex(), Name, Info.message());
+ });
+ });
+ }
+ };
+ // NameIndex can have multiple CUs. For example if it was created by BOLT.
+ // So better to iterate over NI, and then over CUs in it.
+ for (const DWARFDebugNames::NameIndex &NI : AccelTable) {
+ StringMap<DenseSet<uint64_t>> NamesToDieOffsets(NI.getNameCount());
+ populateNameToOffset(NI, NamesToDieOffsets);
+ for (uint32_t i = 0, iEnd = NI.getCUCount(); i < iEnd; ++i) {
+ const uint64_t CUOffset = NI.getCUOffset(i);
+ DWARFUnit *U = DCtx.getUnitForOffset(CUOffset);
+ DWARFCompileUnit *CU = dyn_cast<DWARFCompileUnit>(U);
if (CU) {
if (CU->getDWOId()) {
DWARFDie CUDie = CU->getUnitDIE(true);
DWARFDie NonSkeletonUnitDie =
CUDie.getDwarfUnit()->getNonSkeletonUnitDIE(false);
if (CUDie != NonSkeletonUnitDie) {
- for (const DWARFDebugInfoEntry &Die :
- NonSkeletonUnitDie.getDwarfUnit()->dies())
- NumErrors += verifyNameIndexCompleteness(
- DWARFDie(NonSkeletonUnitDie.getDwarfUnit(), &Die), *NI);
+ parallelForEach(
+ NonSkeletonUnitDie.getDwarfUnit()->dies(),
+ [&](const DWARFDebugInfoEntry &Die) {
+ verifyNameIndexCompleteness(
+ DWARFDie(NonSkeletonUnitDie.getDwarfUnit(), &Die), NI,
+ NamesToDieOffsets);
+ });
}
} else {
- for (const DWARFDebugInfoEntry &Die : CU->dies())
- NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &Die), *NI);
+ parallelForEach(CU->dies(), [&](const DWARFDebugInfoEntry &Die) {
+ verifyNameIndexCompleteness(DWARFDie(CU, &Die), NI,
+ NamesToDieOffsets);
+ });
}
}
}
}
- return NumErrors;
+ return;
}
bool DWARFVerifier::handleAccelTables() {
const DWARFObject &D = DCtx.getDWARFObj();
DataExtractor StrData(D.getStrSection(), DCtx.isLittleEndian(), 0);
- unsigned NumErrors = 0;
if (!D.getAppleNamesSection().Data.empty())
- NumErrors += verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData,
- ".apple_names");
+ verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData, ".apple_names");
if (!D.getAppleTypesSection().Data.empty())
- NumErrors += verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData,
- ".apple_types");
+ verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData, ".apple_types");
if (!D.getAppleNamespacesSection().Data.empty())
- NumErrors += verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData,
- ".apple_namespaces");
+ verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData,
+ ".apple_namespaces");
if (!D.getAppleObjCSection().Data.empty())
- NumErrors += verifyAppleAccelTable(&D.getAppleObjCSection(), &StrData,
- ".apple_objc");
+ verifyAppleAccelTable(&D.getAppleObjCSection(), &StrData, ".apple_objc");
if (!D.getNamesSection().Data.empty())
- NumErrors += verifyDebugNames(D.getNamesSection(), StrData);
- return NumErrors == 0;
+ verifyDebugNames(D.getNamesSection(), StrData);
+ return ErrorCategory.GetNumErrors() == 0;
}
bool DWARFVerifier::handleDebugStrOffsets() {
@@ -2176,6 +2232,8 @@ void OutputCategoryAggregator::Report(
void OutputCategoryAggregator::Report(
StringRef category, StringRef sub_category,
std::function<void(void)> detailCallback) {
+ std::lock_guard<std::mutex> Lock(WriteMutex);
+ ++NumErrors;
std::string category_str = std::string(category);
AggregationData &Agg = Aggregation[category_str];
Agg.OverallCount++;
diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness.s b/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness.s
index b16f8658f87ec..9886968fdab99 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness.s
+++ b/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness.s
@@ -177,4 +177,3 @@
.Lnames_abbrev_end0:
.Lnames_entries0:
.Lnames_end0:
-
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 82dda93b7f1ab..5749b19d7ad49 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -30,9 +30,11 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Threading.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
@@ -284,6 +286,14 @@ static cl::opt<bool>
cat(DwarfDumpCategory));
static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),
cat(DwarfDumpCategory));
+static opt<unsigned> VerifyNumThreads(
+ "verify-num-threads", init(1),
+ desc("Number of threads to use for --verify. Single threaded verification "
+ "is the default unless this option is specified. If 0 is specified, "
+ "maximum hardware threads will be used. This can cause the "
+ "output to be non determinisitic, but can speed up verification and "
+ "is useful when running with the summary only or JSON summary modes."),
+ cat(DwarfDumpCategory));
static opt<ErrorDetailLevel> ErrorDetails(
"error-display", init(Unspecified),
desc("Set the level of detail and summary to display when verifying "
@@ -778,7 +788,8 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
if (filterArch(*Obj)) {
std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
*Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
- RecoverableErrorHandler);
+ RecoverableErrorHandler, WithColor::defaultWarningHandler,
+ /*ThreadSafe=*/true);
DICtx->setParseCUTUIndexManually(ManuallyGenerateUnitIndex);
if (!HandleObj(*Obj, *DICtx, Filename, OS))
Result = false;
@@ -906,6 +917,11 @@ int main(int argc, char **argv) {
bool Success = true;
if (Verify) {
+ if (!VerifyNumThreads)
+ parallel::strategy =
+ hardware_concurrency(hardware_concurrency().compute_thread_count());
+ else
+ parallel::strategy = hardware_concurrency(VerifyNumThreads);
for (StringRef Object : Objects)
Success &= handleFile(Object, verifyObjectFile, OutputFile.os());
} else if (Statistics) {
More information about the llvm-commits
mailing list