From 3cc5a5db0e6a933e6b461d93792aafa0dc901759 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Mon, 8 Sep 2025 10:28:37 -0400 Subject: [PATCH] [LLD][COFF] Make `/summary` work when `/debug` isn't provided Previously, `/summary` was meant to print some PDB information. Now move handling of `/summary` to `Writer.cpp` so that the flag will have an effect when `/debug` isn't provided. This will also provide grounds for extending with more general information. --- lld/COFF/COFFLinkerContext.h | 3 + lld/COFF/PDB.cpp | 72 ++++++++--------------- lld/COFF/PDB.h | 13 ++++ lld/COFF/Writer.cpp | 46 +++++++++++++++ lld/test/COFF/pdb-type-server-simple.test | 4 +- lld/test/COFF/precomp-link.test | 22 ++++++- lld/test/COFF/precomp-summary-fail.test | 4 +- 7 files changed, 112 insertions(+), 52 deletions(-) diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h index b44263b5a3390..0f8f2062b9f2e 100644 --- a/lld/COFF/COFFLinkerContext.h +++ b/lld/COFF/COFFLinkerContext.h @@ -14,6 +14,7 @@ #include "DebugTypes.h" #include "Driver.h" #include "InputFiles.h" +#include "PDB.h" #include "SymbolTable.h" #include "Writer.h" #include "lld/Common/CommonLinkerContext.h" @@ -113,6 +114,8 @@ class COFFLinkerContext : public CommonLinkerContext { Timer tpiStreamLayoutTimer; Timer diskCommitTimer; + std::optional pdbStats; + Configuration config; DynamicRelocsChunk *dynamicRelocs = nullptr; diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp index e5c62af28fdd2..fd54d20da3ccd 100644 --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -44,7 +44,6 @@ #include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/CRC.h" #include "llvm/Support/Endian.h" -#include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" @@ -133,8 +132,8 @@ class PDBLinker { /// Write the PDB to disk and store the Guid generated for it in *Guid. void commit(codeview::GUID *guid); - // Print statistics regarding the final PDB - void printStats(); + // Collect some statistics regarding the final PDB + void collectStats(); private: void pdbMakeAbsolute(SmallVectorImpl &fileName); @@ -154,13 +153,6 @@ class PDBLinker { DebugStringTableSubsection pdbStrTab; llvm::SmallString<128> nativePath; - - // For statistics - uint64_t globalSymbols = 0; - uint64_t moduleSymbols = 0; - uint64_t publicSymbols = 0; - uint64_t nbTypeRecords = 0; - uint64_t nbTypeRecordsBytes = 0; }; /// Represents an unrelocated DEBUG_S_FRAMEDATA subsection. @@ -610,7 +602,9 @@ void PDBLinker::analyzeSymbolSubsection( addGlobalSymbol(builder.getGsiBuilder(), file->moduleDBI->getModuleIndex(), moduleSymOffset, storage); - ++globalSymbols; + + if (ctx.pdbStats.has_value()) + ++ctx.pdbStats->globalSymbols; } // Update the module stream offset and record any string table index @@ -619,7 +613,9 @@ void PDBLinker::analyzeSymbolSubsection( if (symbolGoesInModuleStream(sym, scopeLevel)) { recordStringTableReferences(sym, moduleSymOffset, stringTableFixups); moduleSymOffset += alignedSize; - ++moduleSymbols; + + if (ctx.pdbStats.has_value()) + ++ctx.pdbStats->moduleSymbols; } return Error::success(); @@ -1192,10 +1188,10 @@ void PDBLinker::addObjectsToPDB() { } } - if (ctx.config.showSummary) { + if (ctx.pdbStats.has_value()) { for (TpiSource *source : ctx.tpiSourceList) { - nbTypeRecords += source->nbTypeRecords; - nbTypeRecordsBytes += source->nbTypeRecordsBytes; + ctx.pdbStats->nbTypeRecords += source->nbTypeRecords; + ctx.pdbStats->nbTypeRecordsBytes += source->nbTypeRecordsBytes; } } } @@ -1231,43 +1227,24 @@ void PDBLinker::addPublicsToPDB() { } }); - if (!publics.empty()) { - publicSymbols = publics.size(); + if (ctx.pdbStats.has_value()) + ctx.pdbStats->publicSymbols = publics.size(); + + if (!publics.empty()) gsiBuilder.addPublicSymbols(std::move(publics)); - } } -void PDBLinker::printStats() { +void PDBLinker::collectStats() { if (!ctx.config.showSummary) return; + ctx.pdbStats->nbTPIrecords = builder.getTpiBuilder().getRecordCount(); + ctx.pdbStats->nbIPIrecords = builder.getIpiBuilder().getRecordCount(); + ctx.pdbStats->strTabSize = pdbStrTab.size(); + SmallString<256> buffer; raw_svector_ostream stream(buffer); - stream << center_justify("Summary", 80) << '\n' - << std::string(80, '-') << '\n'; - - auto print = [&](uint64_t v, StringRef s) { - stream << formatv("{0}", - fmt_align(formatv("{0:N}", v), AlignStyle::Right, 20)) - << " " << s << '\n'; - }; - - print(ctx.objFileInstances.size(), - "Input OBJ files (expanded from all cmd-line inputs)"); - print(ctx.consumedInputsSize, - "Size of all consumed OBJ files (non-lazy), in bytes"); - print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies"); - print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies"); - print(nbTypeRecords, "Input type records"); - print(nbTypeRecordsBytes, "Size of all input type records, in bytes"); - print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records"); - print(builder.getIpiBuilder().getRecordCount(), "Merged IPI records"); - print(pdbStrTab.size(), "Output PDB strings"); - print(globalSymbols, "Global symbol records"); - print(moduleSymbols, "Module symbol records"); - print(publicSymbols, "Public symbol records"); - auto printLargeInputTypeRecs = [&](StringRef name, ArrayRef recCounts, TypeCollection &records) { @@ -1318,9 +1295,9 @@ void PDBLinker::printStats() { // FIXME: Reimplement for ghash. printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable()); printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable()); - } - Msg(ctx) << buffer; + ctx.pdbStats->largeInputTypeRecs = buffer.str(); + } } void PDBLinker::addNatvisFiles() { @@ -1624,6 +1601,9 @@ void lld::coff::createPDB(COFFLinkerContext &ctx, { PDBLinker pdb(ctx); + if (ctx.config.showSummary) + ctx.pdbStats.emplace(); + pdb.initialize(buildId); pdb.addObjectsToPDB(); pdb.addImportFilesToPDB(); @@ -1640,8 +1620,8 @@ void lld::coff::createPDB(COFFLinkerContext &ctx, memcpy(&buildId->PDB70.Signature, &guid, 16); } + pdb.collectStats(); t1.stop(); - pdb.printStats(); // Manually start this profile point to measure ~PDBLinker(). if (getTimeTraceProfilerInstance() != nullptr) diff --git a/lld/COFF/PDB.h b/lld/COFF/PDB.h index 991805cc95b38..4a5df37c0fe6f 100644 --- a/lld/COFF/PDB.h +++ b/lld/COFF/PDB.h @@ -30,6 +30,19 @@ void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef sectionTable, std::optional> getFileLineCodeView(const SectionChunk *c, uint32_t addr); +// For statistics +struct PDBStats { + uint64_t globalSymbols = 0; + uint64_t moduleSymbols = 0; + uint64_t publicSymbols = 0; + uint64_t nbTypeRecords = 0; + uint64_t nbTypeRecordsBytes = 0; + uint64_t nbTPIrecords = 0; + uint64_t nbIPIrecords = 0; + uint64_t strTabSize = 0; + std::string largeInputTypeRecs; +}; + } // namespace coff } // namespace lld diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 37577e8dd93d9..e0d0ac18ea5d5 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -27,6 +27,8 @@ #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/TimeProfiler.h" @@ -282,6 +284,8 @@ class Writer { template void prepareLoadConfig(SymbolTable &symtab, T *loadConfig); + void printSummary(); + std::unique_ptr &buffer; std::map partialSections; StringTableBuilder strtab; @@ -823,6 +827,8 @@ void Writer::run() { writePEChecksum(); + printSummary(); + if (errorCount()) return; @@ -3046,3 +3052,43 @@ void Writer::prepareLoadConfig(SymbolTable &symtab, T *loadConfig) { #undef CHECK_VA #undef CHECK_ABSOLUTE } + +void Writer::printSummary() { + if (!ctx.config.showSummary) + return; + + SmallString<256> buffer; + raw_svector_ostream stream(buffer); + + stream << center_justify("Summary", 80) << '\n' + << std::string(80, '-') << '\n'; + + auto print = [&](uint64_t v, StringRef s) { + stream << formatv("{0}", + fmt_align(formatv("{0:N}", v), AlignStyle::Right, 20)) + << " " << s << '\n'; + }; + + bool hasStats = ctx.pdbStats.has_value(); + + print(ctx.objFileInstances.size(), + "Input OBJ files (expanded from all cmd-line inputs)"); + print(ctx.consumedInputsSize, + "Size of all consumed OBJ files (non-lazy), in bytes"); + print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies"); + print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies"); + print(hasStats ? ctx.pdbStats->nbTypeRecords : 0, "Input debug type records"); + print(hasStats ? ctx.pdbStats->nbTypeRecordsBytes : 0, + "Size of all input debug type records, in bytes"); + print(hasStats ? ctx.pdbStats->nbTPIrecords : 0, "Merged TPI records"); + print(hasStats ? ctx.pdbStats->nbIPIrecords : 0, "Merged IPI records"); + print(hasStats ? ctx.pdbStats->strTabSize : 0, "Output PDB strings"); + print(hasStats ? ctx.pdbStats->globalSymbols : 0, "Global symbol records"); + print(hasStats ? ctx.pdbStats->moduleSymbols : 0, "Module symbol records"); + print(hasStats ? ctx.pdbStats->publicSymbols : 0, "Public symbol records"); + + if (hasStats) + stream << ctx.pdbStats->largeInputTypeRecs; + + Msg(ctx) << buffer; +} diff --git a/lld/test/COFF/pdb-type-server-simple.test b/lld/test/COFF/pdb-type-server-simple.test index 5323a078ac432..693bd9b482dae 100644 --- a/lld/test/COFF/pdb-type-server-simple.test +++ b/lld/test/COFF/pdb-type-server-simple.test @@ -110,8 +110,8 @@ SUMMARY-NEXT: 2 Input OBJ files (expanded from all cmd-line i SUMMARY-NEXT: Size of all consumed OBJ files (non-lazy), in bytes SUMMARY-NEXT: 1 PDB type server dependencies SUMMARY-NEXT: 0 Precomp OBJ dependencies -SUMMARY-NEXT: 25 Input type records -SUMMARY-NEXT: 868 Size of all input type records, in bytes +SUMMARY-NEXT: 25 Input debug type records +SUMMARY-NEXT: 868 Size of all input debug type records, in bytes SUMMARY-NEXT: 9 Merged TPI records SUMMARY-NEXT: 16 Merged IPI records SUMMARY-NEXT: 3 Output PDB strings diff --git a/lld/test/COFF/precomp-link.test b/lld/test/COFF/precomp-link.test index 1c26042a44c33..389747920ffcf 100644 --- a/lld/test/COFF/precomp-link.test +++ b/lld/test/COFF/precomp-link.test @@ -3,6 +3,9 @@ RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY-NODEBUG +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY-NODEBUG + RUN: lld-link %S/Inputs/precomp.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s @@ -63,8 +66,8 @@ SUMMARY-NEXT: 3 Input OBJ files (expanded from all cmd-line i SUMMARY-NEXT: Size of all consumed OBJ files (non-lazy), in bytes SUMMARY-NEXT: 0 PDB type server dependencies SUMMARY-NEXT: 1 Precomp OBJ dependencies -SUMMARY-NEXT: 1,066 Input type records -SUMMARY-NEXT: 55,968 Size of all input type records, in bytes +SUMMARY-NEXT: 1,066 Input debug type records +SUMMARY-NEXT: 55,968 Size of all input debug type records, in bytes SUMMARY-NEXT: 874 Merged TPI records SUMMARY-NEXT: 170 Merged IPI records SUMMARY-NEXT: 5 Output PDB strings @@ -72,6 +75,21 @@ SUMMARY-NEXT: 167 Global symbol records SUMMARY-NEXT: 20 Module symbol records SUMMARY-NEXT: 3 Public symbol records +SUMMARY-NODEBUG: Summary +SUMMARY-NODEBUG-NEXT: -------------------------------------------------------------------------------- +SUMMARY-NODEBUG-NEXT: 3 Input OBJ files (expanded from all cmd-line inputs) +SUMMARY-NODEBUG-NEXT: Size of all consumed OBJ files (non-lazy), in bytes +SUMMARY-NODEBUG-NEXT: 0 PDB type server dependencies +SUMMARY-NODEBUG-NEXT: 0 Precomp OBJ dependencies +SUMMARY-NODEBUG-NEXT: 0 Input debug type records +SUMMARY-NODEBUG-NEXT: 0 Size of all input debug type records, in bytes +SUMMARY-NODEBUG-NEXT: 0 Merged TPI records +SUMMARY-NODEBUG-NEXT: 0 Merged IPI records +SUMMARY-NODEBUG-NEXT: 0 Output PDB strings +SUMMARY-NODEBUG-NEXT: 0 Global symbol records +SUMMARY-NODEBUG-NEXT: 0 Module symbol records +SUMMARY-NODEBUG-NEXT: 0 Public symbol records + // precomp.h #pragma once int Function(char A); diff --git a/lld/test/COFF/precomp-summary-fail.test b/lld/test/COFF/precomp-summary-fail.test index 0d528c76c2180..5568316ff63c6 100644 --- a/lld/test/COFF/precomp-summary-fail.test +++ b/lld/test/COFF/precomp-summary-fail.test @@ -15,8 +15,8 @@ SUMMARY-NEXT: 2 Input OBJ files (expanded from all cmd-line i SUMMARY-NEXT: Size of all consumed OBJ files (non-lazy), in bytes SUMMARY-NEXT: 0 PDB type server dependencies SUMMARY-NEXT: 1 Precomp OBJ dependencies -SUMMARY-NEXT: 8 Input type records -SUMMARY-NEXT: 232 Size of all input type records, in bytes +SUMMARY-NEXT: 8 Input debug type records +SUMMARY-NEXT: 232 Size of all input debug type records, in bytes SUMMARY-NEXT: 3 Merged TPI records SUMMARY-NEXT: 2 Merged IPI records SUMMARY-NEXT: 1 Output PDB strings