diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -54,6 +54,7 @@ #include "llvm/Support/CRC.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" @@ -189,6 +190,11 @@ uint64_t globalSymbols = 0; uint64_t moduleSymbols = 0; uint64_t publicSymbols = 0; + + // When showSummary is enabled, these are histograms of TPI and IPI records + // keyed by type index. + SmallVector tpiCounts; + SmallVector ipiCounts; }; class DebugSHandler { @@ -333,6 +339,22 @@ }); } +// FIXME: Duplicated from TypeStreamMerger. +static bool isIdRecord(TypeLeafKind K) { + switch (K) { + case TypeLeafKind::LF_FUNC_ID: + case TypeLeafKind::LF_MFUNC_ID: + case TypeLeafKind::LF_STRING_ID: + case TypeLeafKind::LF_SUBSTR_LIST: + case TypeLeafKind::LF_BUILDINFO: + case TypeLeafKind::LF_UDT_SRC_LINE: + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + return true; + default: + return false; + } +} + Expected PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { ScopedTimer t(typeMergingTimer); @@ -415,6 +437,25 @@ fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } + + if (config->showSummary) { + // Count how many times we saw each type record in our input. This + // calculation requires a second pass over the type records to classify each + // record as a type or index. This is slow, but this code executes when + // collecting statistics. + tpiCounts.resize(tMerger.getTypeTable().size()); + ipiCounts.resize(tMerger.getIDTable().size()); + uint32_t srcIdx = 0; + for (CVType &ty : types) { + TypeIndex dstIdx = objectIndexMap->tpiMap[srcIdx++]; + if (dstIdx.isSimple()) + continue; // Untranslated or other. + SmallVectorImpl &counts = + isIdRecord(ty.kind()) ? ipiCounts : tpiCounts; + ++counts[dstIdx.toArrayIndex()]; + } + } + return *objectIndexMap; } @@ -482,6 +523,18 @@ } } + if (config->showSummary) { + // Count how many times we saw each type record in our input. If a + // destination type index is present in the source to destination type index + // map, that means we saw it once in the input. Add it to our histogram. + tpiCounts.resize(tMerger.getTypeTable().size()); + ipiCounts.resize(tMerger.getIDTable().size()); + for (auto ti : indexMap.tpiMap) + ++tpiCounts[ti.toArrayIndex()]; + for (auto ti : indexMap.ipiMap) + ++ipiCounts[ti.toArrayIndex()]; + } + return indexMap; } @@ -1334,6 +1387,48 @@ print(moduleSymbols, "Module symbol records"); print(publicSymbols, "Public symbol records"); + auto printLargeInputTypeRecs = [&](StringRef name, + ArrayRef recCounts, + TypeCollection &records) { + // Figure out which type indices were responsible for the most duplicate + // bytes in the input files. These should be frequently emitted LF_CLASS and + // LF_FIELDLIST records. + struct TypeSizeInfo { + uint32_t TypeSize; + uint32_t DupCount; + TypeIndex TI; + uint64_t totalInputSize() const { return uint64_t(DupCount) * TypeSize; } + bool operator<(const TypeSizeInfo &RHS) const { + return totalInputSize() < RHS.totalInputSize(); + } + }; + SmallVector TSIs; + for (auto E : enumerate(recCounts)) { + TypeIndex TI = TypeIndex::fromArrayIndex(E.index()); + uint32_t TypeSize = records.getType(TI).length(); + uint32_t DupCount = E.value(); + TSIs.push_back({TypeSize, DupCount, TI}); + } + + if (!TSIs.empty()) { + stream << "\nTop 10 types responsible for the most " << name + << " input:\n"; + stream << " index total bytes count size\n"; + llvm::sort(TSIs); + unsigned I = 0; + for (const auto &TSI : reverse(TSIs)) { + stream << formatv(" {0,10:X}: {1,14:N} = {2,5:N} * {3,6:N}\n", + TSI.TI.getIndex(), TSI.totalInputSize(), TSI.DupCount, + TSI.TypeSize); + if (++I >= 10) + break; + } + } + }; + + printLargeInputTypeRecs("TPI", tpiCounts, tMerger.getTypeTable()); + printLargeInputTypeRecs("IPI", ipiCounts, tMerger.getIDTable()); + message(buffer); } diff --git a/lld/test/COFF/pdb-type-server-simple.test b/lld/test/COFF/pdb-type-server-simple.test --- a/lld/test/COFF/pdb-type-server-simple.test +++ b/lld/test/COFF/pdb-type-server-simple.test @@ -105,4 +105,12 @@ SUMMARY-NEXT: 3 Output PDB strings SUMMARY-NEXT: 4 Global symbol records SUMMARY-NEXT: 14 Module symbol records -SUMMARY-NEXT: 2 Public symbol records \ No newline at end of file +SUMMARY-NEXT: 2 Public symbol records + +SUMMARY: Top 10 types responsible for the most TPI input: +SUMMARY-NEXT: index total bytes count size +SUMMARY-NEXT: 0x1006: 36 = 1 * 36 + +SUMMARY: Top 10 types responsible for the most IPI input: +SUMMARY-NEXT: index total bytes count size +SUMMARY-NEXT: 0x1006: 256 = 1 * 256 diff --git a/llvm/include/llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h b/llvm/include/llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h --- a/llvm/include/llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h +++ b/llvm/include/llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h @@ -43,7 +43,7 @@ /// Contains a list of all records indexed by TypeIndex.toArrayIndex(). SmallVector, 2> SeenRecords; - /// Contains a list of all hash values inexed by TypeIndex.toArrayIndex(). + /// Contains a list of all hash values indexed by TypeIndex.toArrayIndex(). SmallVector SeenHashes; public: