[LLD][COFF] Make /summary work when /debug isn't provided (#157476)

Previously, `/summary` was meant to print some PDB information. Now move
handling of `/summary` to `Writer.cpp` so that it can have an effect
when `/debug` isn't provided. This will also provide grounds for
extending with more general information.
This commit is contained in:
Alexandre Ganea 2025-09-09 07:28:24 -04:00 committed by GitHub
parent bf5ea876ef
commit 14f7e5fa06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 112 additions and 52 deletions

View File

@ -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 @@ public:
Timer tpiStreamLayoutTimer;
Timer diskCommitTimer;
std::optional<PDBStats> pdbStats;
Configuration config;
DynamicRelocsChunk *dynamicRelocs = nullptr;

View File

@ -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 @@ public:
/// 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<char> &fileName);
@ -154,13 +153,6 @@ private:
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<uint32_t> 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)

View File

@ -30,6 +30,19 @@ void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef<uint8_t> sectionTable,
std::optional<std::pair<llvm::StringRef, uint32_t>>
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

View File

@ -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 @@ private:
template <typename T>
void prepareLoadConfig(SymbolTable &symtab, T *loadConfig);
void printSummary();
std::unique_ptr<FileOutputBuffer> &buffer;
std::map<PartialSectionKey, PartialSection *> 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;
}

View File

@ -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

View File

@ -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);

View File

@ -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