Index: lld/COFF/Config.h =================================================================== --- lld/COFF/Config.h +++ lld/COFF/Config.h @@ -13,6 +13,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" +#include #include #include #include @@ -90,8 +91,10 @@ bool Debug = false; bool DebugDwarf = false; bool DebugGHashes = false; + bool ShowTiming = false; unsigned DebugTypes = static_cast(DebugType::None); llvm::SmallString<128> PDBPath; + std::vector Argv; // Symbols in this set are considered as live by the garbage collector. Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -831,6 +831,10 @@ Config->DebugTypes = getDefaultDebugType(Args); } + if (Args.hasArg(OPT_show_timing)) { + Config->ShowTiming = true; + } + // Handle /pdb bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash); if (ShouldCreatePDB) Index: lld/COFF/Options.td =================================================================== --- lld/COFF/Options.td +++ lld/COFF/Options.td @@ -132,6 +132,7 @@ // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-"], "lldmap:">; +def show_timing : F<"showtiming">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. Index: lld/COFF/PDB.h =================================================================== --- lld/COFF/PDB.h +++ lld/COFF/PDB.h @@ -23,11 +23,12 @@ namespace coff { class OutputSection; class SymbolTable; +struct TimingInfo; void createPDB(SymbolTable *Symtab, llvm::ArrayRef OutputSections, llvm::ArrayRef SectionTable, - const llvm::codeview::DebugInfo &BuildId); + const llvm::codeview::DebugInfo &BuildId, TimingInfo &Metrics); } } Index: lld/COFF/PDB.cpp =================================================================== --- lld/COFF/PDB.cpp +++ lld/COFF/PDB.cpp @@ -15,6 +15,7 @@ #include "Symbols.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/LinkTimer.h" #include "llvm/DebugInfo/CodeView/CVDebugRecord.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" @@ -73,9 +74,10 @@ class PDBLinker { public: - PDBLinker(SymbolTable *Symtab) + PDBLinker(SymbolTable *Symtab, TimingInfo &Metrics) : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), - IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {} + IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc), + Metrics(Metrics) {} /// Emit the basic PDB structure: initial streams, headers, etc. void initialize(const llvm::codeview::DebugInfo &BuildId); @@ -141,6 +143,8 @@ /// Type index mappings of type server PDBs that we've loaded so far. std::map TypeServerIndexMappings; + + TimingInfo &Metrics; }; } @@ -233,6 +237,10 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap) { + LinkTimer TypeMergingTimer; + if (Config->ShowTiming) + TypeMergingTimer.start(Metrics[TimingInfo::PdbTypeMergingTime]); + ArrayRef Data = getDebugSection(File, ".debug$T"); if (Data.empty()) return ObjectIndexMap; @@ -747,6 +755,11 @@ CVIndexMap ObjectIndexMap; const CVIndexMap &IndexMap = mergeDebugT(File, ObjectIndexMap); + LinkTimer SymbolTimer; + + if (Config->ShowTiming) + SymbolTimer.start(Metrics[TimingInfo::PdbSymbolMergingTime]); + // Now do all live .debug$S sections. DebugStringTableSubsectionRef CVStrTab; DebugChecksumsSubsectionRef Checksums; @@ -864,11 +877,16 @@ // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { + LinkTimer PublicsTimer; + LinkTimer TpiTimer; + for (ObjFile *File : ObjFile::Instances) addObjFile(File); Builder.getStringTableBuilder().setStrings(PDBStrTab); + if (Config->ShowTiming) + TpiTimer.start(Metrics[TimingInfo::PdbTpiConstructionTime]); // Construct TPI and IPI stream contents. if (Config->DebugGHashes) { addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable); @@ -877,6 +895,10 @@ addTypeInfo(Builder.getTpiBuilder(), TypeTable); addTypeInfo(Builder.getIpiBuilder(), IDTable); } + TpiTimer.end(); + + if (Config->ShowTiming) + PublicsTimer.start(Metrics[TimingInfo::PdbPublicsConstructionTime]); // Compute the public and global symbols. auto &GsiBuilder = Builder.getGsiBuilder(); @@ -973,11 +995,21 @@ void coff::createPDB(SymbolTable *Symtab, ArrayRef OutputSections, ArrayRef SectionTable, - const llvm::codeview::DebugInfo &BuildId) { - PDBLinker PDB(Symtab); + const llvm::codeview::DebugInfo &BuildId, + TimingInfo &Metrics) { + LinkTimer FullPdbLinkTimer; + LinkTimer PdbCommitTimer; + if (Config->ShowTiming) + FullPdbLinkTimer.start(Metrics[TimingInfo::PdbLinkTime]); + + PDBLinker PDB(Symtab, Metrics); PDB.initialize(BuildId); PDB.addObjectsToPDB(); PDB.addSections(OutputSections, SectionTable); + + if (Config->ShowTiming) + PdbCommitTimer.start(Metrics[TimingInfo::PdbDiskCommitTime]); + PDB.commit(); } Index: lld/COFF/Writer.h =================================================================== --- lld/COFF/Writer.h +++ lld/COFF/Writer.h @@ -13,6 +13,7 @@ #include "Chunks.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" +#include #include #include @@ -22,6 +23,41 @@ void writeResult(); +struct TimingInfo { + enum { + TotalLinkTime, + + CodeLayoutTime, + CodeDiskCommitTime, + PdbLinkTime, + PdbPublicsConstructionTime, + PdbTpiConstructionTime, + PdbTypeMergingTime, + PdbSymbolMergingTime, + PdbDiskCommitTime, + + MaxMetric + }; + + TimingInfo() { + using namespace std::chrono_literals; + for (auto &T : Timings) { + T = 0ms; + } + } + + std::chrono::milliseconds longest() const { + return *std::max_element(Timings.begin(), Timings.end()); + } + const std::chrono::milliseconds &operator[](int N) const { + return Timings[N]; + } + std::chrono::milliseconds &operator[](int N) { return Timings[N]; } + +private: + std::array Timings; +}; + // OutputSection represents a section in an output file. It's a // container of chunks. OutputSection and Chunk are 1:N relationship. // Chunks cannot belong to more than one OutputSections. The writer Index: lld/COFF/Writer.cpp =================================================================== --- lld/COFF/Writer.cpp +++ lld/COFF/Writer.cpp @@ -16,6 +16,7 @@ #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/LinkTimer.h" #include "lld/Common/Memory.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" @@ -24,12 +25,15 @@ #include "llvm/Support/Debug.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 #include #include #include +#include #include using namespace llvm; @@ -282,8 +286,60 @@ return None; } +static void outputTimingInformation(const TimingInfo &Metrics) { + using C = Configuration; + + using namespace std::chrono_literals; + std::chrono::milliseconds TotalDuration = 0ms; + auto MaxDuration = Metrics.longest(); + TotalDuration = Metrics[TimingInfo::TotalLinkTime]; + int Digits = std::ceil(std::log10(MaxDuration.count())); + int MaxLength = 10 + Digits + 3; // ( AB.CD%) ms + + auto Format = [Digits, TotalDuration, Metrics](int N) { + float P = 100.0f * (float)Metrics[N].count() / (float)TotalDuration.count(); + std::string Result = + formatv("({0,+6}%) {1:ms}", P, + fmt_align(Metrics[N], AlignStyle::Right, Digits + 3)); + return Result; + }; + + message("Timing:"); + message(formatv(" Code Layout Time: {0}", + Format(TimingInfo::CodeLayoutTime))); + message(formatv(" Commit Output File: {0}", + Format(TimingInfo::CodeDiskCommitTime))); + message(formatv(" PDB Emission (Cumulative): {0}", + Format(TimingInfo::PdbLinkTime))); + message(formatv(" Globals Stream Layout: {0}", + Format(TimingInfo::PdbPublicsConstructionTime))); + message(formatv(" Symbol Merging: {0}", + Format(TimingInfo::PdbSymbolMergingTime))); + message(formatv(" Type Merging: {0}", + Format(TimingInfo::PdbTypeMergingTime))); + message(formatv(" TPI Stream Layout: {0}", + Format(TimingInfo::PdbTpiConstructionTime))); + message(formatv(" Commit to Disk: {0}", + Format(TimingInfo::PdbDiskCommitTime))); + message( + formatv("-----------------------------{0}", fmt_repeat("-", MaxLength))); + message(formatv(" Total Link Time: {0}", + Format(TimingInfo::TotalLinkTime))); +} + // The main function of the writer. void Writer::run() { + TimingInfo Metrics; + + LinkTimer TotalLinkTimer; + LinkTimer CodeLayoutTimer; + LinkTimer CodeDiskCommitTimer; + + if (Config->ShowTiming) { + TotalLinkTimer.start(Metrics[TimingInfo::TotalLinkTime]); + CodeLayoutTimer.start(Metrics[TimingInfo::CodeLayoutTime]); + } + createSections(); createMiscChunks(); createImportTables(); @@ -308,16 +364,24 @@ sortExceptionTable(); writeBuildId(); - if (!Config->PDBPath.empty() && Config->Debug) { + CodeLayoutTimer.end(); + if (!Config->PDBPath.empty() && Config->Debug) { assert(BuildId); - createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId); + createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId, Metrics); } writeMapFile(OutputSections); + if (Config->ShowTiming) + CodeDiskCommitTimer.start(Metrics[TimingInfo::CodeDiskCommitTime]); if (auto E = Buffer->commit()) fatal("failed to write the output file: " + toString(std::move(E))); + CodeDiskCommitTimer.end(); + TotalLinkTimer.end(); + + if (Config->ShowTiming) + outputTimingInformation(Metrics); } static StringRef getOutputSection(StringRef Name) { Index: lld/include/lld/Common/LinkTimer.h =================================================================== --- /dev/null +++ lld/include/lld/Common/LinkTimer.h @@ -0,0 +1,40 @@ +//===- LinkTimer.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_LINKTIMER_H +#define LLD_LINKTIMER_H + +#include +#include + +namespace lld { + +struct LinkTimer { + ~LinkTimer() { end(); } + + void start(std::chrono::milliseconds &DurationToUpdate) { + assert(!this->DurationToUpdate); + this->DurationToUpdate = &DurationToUpdate; + StartTime = std::chrono::system_clock::now(); + } + + void end() { + if (!DurationToUpdate) + return; + auto EndTime = std::chrono::system_clock::now(); + *DurationToUpdate += std::chrono::duration_cast( + EndTime - StartTime); + DurationToUpdate = nullptr; + } + std::chrono::time_point StartTime; + std::chrono::milliseconds *DurationToUpdate = nullptr; +}; +} // namespace lld + +#endif