Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -191,6 +191,7 @@ bool trace; bool thinLTOEmitImportsFiles; bool thinLTOIndexOnly; + bool timeTraceEnabled; bool tocOptimize; bool undefinedVersion; bool useAndroidRelrTags = false; @@ -240,6 +241,7 @@ unsigned ltoo; unsigned optimize; unsigned thinLTOJobs; + unsigned timeTraceGranularity; int32_t splitStackAdjustSize; // The following config options do not directly correspond to any Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -56,6 +56,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -500,6 +501,11 @@ if (errorCount()) return; + // Initialize time trace. + if (config->timeTraceEnabled) + llvm::timeTraceProfilerInitialize(config->timeTraceGranularity, + config->progName); + // The Target instance handles target-specific stuff, such as applying // relocations or writing a PLT section. It also contains target-dependent // values such as a default image base address. @@ -508,19 +514,34 @@ switch (config->ekind) { case ELF32LEKind: link(args); - return; + break; case ELF32BEKind: link(args); - return; + break; case ELF64LEKind: link(args); - return; + break; case ELF64BEKind: link(args); - return; + break; default: llvm_unreachable("unknown Config->EKind"); } + + if (llvm::timeTraceProfilerEnabled()) { + SmallString<128> path(config->outputFile); + llvm::sys::path::replace_extension(path, "json"); + if (!args.getLastArgValue(OPT_time_trace_eq).empty()) + path = args.getLastArgValue(OPT_time_trace_eq); + std::error_code errorCode; + raw_fd_ostream profilerOS(path, errorCode, sys::fs::F_Text); + if (errorCode) { + error("cannot open " + path + ": " + errorCode.message()); + return; + } + llvm::timeTraceProfilerWrite(profilerOS); + llvm::timeTraceProfilerCleanup(); + } } static std::string getRpath(opt::InputArgList &args) { @@ -952,6 +973,10 @@ getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); config->thinLTOPrefixReplace = getOldNewOptions(args, OPT_thinlto_prefix_replace_eq); + config->timeTraceEnabled = + args.hasArg(OPT_time_trace) || args.hasArg(OPT_time_trace_eq); + config->timeTraceGranularity = + args::getInteger(args, OPT_time_trace_granularity, 500u); config->trace = args.hasArg(OPT_trace); config->undefined = args::getStrings(args, OPT_undefined); config->undefinedVersion = @@ -1596,6 +1621,7 @@ // Because all bitcode files that the program consists of are passed to // the compiler at once, it can do a whole-program optimization. template void LinkerDriver::compileBitcodeFiles() { + llvm::TimeTraceScope timeScope("LTO", StringRef("")); // Compile bitcode files and replace bitcode symbols. lto.reset(new BitcodeCompiler); for (BitcodeFile *file : bitcodeFiles) @@ -1722,6 +1748,8 @@ // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &args) { + llvm::TimeTraceScope timeScope("ExecuteLinker", StringRef("")); + // If a -hash-style option was not given, set to a default value, // which varies depending on the target. if (!args.hasArg(OPT_hash_style)) { @@ -1761,8 +1789,11 @@ // symbols that we need to the symbol table. This process might // add files to the link, via autolinking, these files are always // appended to the Files vector. - for (size_t i = 0; i < files.size(); ++i) - parseFile(files[i]); + { + llvm::TimeTraceScope timeScope("Parse input files", StringRef("")); + for (size_t i = 0; i < files.size(); ++i) + parseFile(files[i]); + } // Now that we have every file, we can decide if we will need a // dynamic symbol table. Index: lld/ELF/ICF.cpp =================================================================== --- lld/ELF/ICF.cpp +++ lld/ELF/ICF.cpp @@ -84,6 +84,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" #include #include @@ -445,6 +446,7 @@ // The main function of ICF. template void ICF::run() { + llvm::TimeTraceScope timeScope("ICF", StringRef("")); // Collect sections to merge. for (InputSectionBase *sec : inputSections) { auto *s = cast(sec); Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ lld/ELF/LTO.cpp @@ -108,6 +108,9 @@ c.DebugPassManager = config->ltoDebugPassManager; c.DwoDir = config->dwoDir; + c.TimeTraceEnabled = config->timeTraceEnabled; + c.TimeTraceGranularity = config->timeTraceGranularity; + c.CSIRProfile = config->ltoCSProfileFile; c.RunCSIRInstr = config->ltoCSProfileGenerate; Index: lld/ELF/MarkLive.cpp =================================================================== --- lld/ELF/MarkLive.cpp +++ lld/ELF/MarkLive.cpp @@ -31,6 +31,7 @@ #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/TimeProfiler.h" #include #include @@ -323,6 +324,7 @@ // input sections. This function make some or all of them on // so that they are emitted to the output file. template void markLive() { + llvm::TimeTraceScope timeScope("GC", StringRef("")); // If -gc-sections is not given, no sections are removed. if (!config->gcSections) { for (InputSectionBase *sec : inputSections) Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -364,6 +364,12 @@ "Run the linker multi-threaded (default)", "Do not run the linker multi-threaded">; +def time_trace: F<"time-trace">, HelpText<"Record time trace">; +def time_trace_eq: J<"time-trace=">, HelpText<"Record time trace to specified file">; + +defm time_trace_granularity: Eq<"time-trace-granularity", + "Minimum time granularity (in microseconds) traced by time profiler">; + defm toc_optimize : B<"toc-optimize", "(PowerPC64) Enable TOC related optimizations (default)", "(PowerPC64) Disable TOC related optimizations">; Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/SHA1.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" #include @@ -139,7 +140,10 @@ script->needsInterpSection(); } -template void writeResult() { Writer().run(); } +template void writeResult() { + llvm::TimeTraceScope timeScope("Write output file", StringRef("")); + Writer().run(); +} static void removeEmptyPTLoad(std::vector &phdrs) { llvm::erase_if(phdrs, [&](const PhdrEntry *p) { Index: llvm/include/llvm/LTO/Config.h =================================================================== --- llvm/include/llvm/LTO/Config.h +++ llvm/include/llvm/LTO/Config.h @@ -117,6 +117,12 @@ /// Statistics output file path. std::string StatsFile; + /// Time trace enabled + bool TimeTraceEnabled = false; + + /// Time trace granularity + unsigned TimeTraceGranularity = 500; + bool ShouldDiscardValueNames = true; DiagnosticHandlerFunction DiagHandler; Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/VCSRevision.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" @@ -1133,6 +1134,9 @@ &ResolvedODR, const GVSummaryMapTy &DefinedGlobals, MapVector &ModuleMap) { + if (Conf.TimeTraceEnabled) + timeTraceProfilerInitialize(Conf.TimeTraceGranularity, + "thin backend"); Error E = runThinLTOBackendThread( AddStream, Cache, Task, BM, CombinedIndex, ImportList, ExportList, ResolvedODR, DefinedGlobals, ModuleMap); @@ -1143,6 +1147,8 @@ else Err = std::move(E); } + if (timeTraceProfilerEnabled()) + timeTraceProfilerFinishThread(); }, BM, std::ref(CombinedIndex), std::ref(ImportList), std::ref(ExportList), std::ref(ResolvedODR), std::ref(DefinedGlobals), std::ref(ModuleMap));