diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -110,6 +110,7 @@ llvm::StringRef optRemarksPasses; llvm::StringRef optRemarksFormat; llvm::StringRef progName; + llvm::StringRef printArchiveStats; llvm::StringRef printSymbolOrder; llvm::StringRef soName; llvm::StringRef sysroot; diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h --- a/lld/ELF/Driver.h +++ b/lld/ELF/Driver.h @@ -30,6 +30,8 @@ void addFile(StringRef path, bool withLOption); void addLibrary(StringRef name); + std::vector files; + private: void createFiles(llvm::opt::InputArgList &args); void inferMachineType(); @@ -44,8 +46,6 @@ // For LTO. std::unique_ptr lto; - - std::vector files; }; // Parses command line options. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -954,6 +954,7 @@ args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); config->printGcSections = args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); + config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats); config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); config->rpath = getRpath(args); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -326,6 +326,9 @@ // more than once.) void fetch(const Archive::Symbol &sym); + size_t getMemberCount() const; + size_t getFetchedMemberCount() const { return seen.size(); } + private: std::unique_ptr file; llvm::DenseSet seen; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1165,6 +1165,17 @@ parseFile(file); } +size_t ArchiveFile::getMemberCount() const { + size_t count = 0; + Error err = Error::success(); + for (const Archive::Child &c : file->children(err)) { + (void)c; + ++count; + } + consumeError(std::move(err)); + return count; +} + unsigned SharedFile::vernauxNum; // Parse the version definitions in the object file if present, and return a diff --git a/lld/ELF/MapFile.h b/lld/ELF/MapFile.h --- a/lld/ELF/MapFile.h +++ b/lld/ELF/MapFile.h @@ -13,6 +13,7 @@ namespace elf { void writeMapFile(); void writeCrossReferenceTable(); +void writeArchiveStats(); } // namespace elf } // namespace lld diff --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp --- a/lld/ELF/MapFile.cpp +++ b/lld/ELF/MapFile.cpp @@ -19,6 +19,7 @@ //===----------------------------------------------------------------------===// #include "MapFile.h" +#include "Driver.h" #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" @@ -259,5 +260,23 @@ } } +void writeArchiveStats() { + if (config->printArchiveStats.empty()) + return; + + std::error_code ec; + raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None); + if (ec) { + error("cannot open " + config->printArchiveStats + ": " + ec.message()); + return; + } + + os << "members\tfetched\tarchive\n"; + for (const elf::InputFile *file : driver->files) + if (const auto *f = dyn_cast(file)) + os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t' + << f->getName() << '\n'; +} + } // namespace elf } // namespace lld diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -297,6 +297,9 @@ "List identical folded sections", "Do not list identical folded sections (default)">; +def print_archive_stats: J<"print-archive-stats=">, + HelpText<"Print the numbers of members and fetched members for each archive into the speicified file">; + defm print_symbol_order: Eq<"print-symbol-order", "Print a symbol order specified by --call-graph-ordering-file into the specified file">; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -606,6 +606,7 @@ // fails, for example, due to an erroneous file size. writeMapFile(); writeCrossReferenceTable(); + writeArchiveStats(); if (config->checkSections) checkSections(); diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -414,6 +414,8 @@ List identical folded sections. .It Fl -print-map Print a link map to the standard output. +.It Fl -print-archive-stats Ns = Ns Ar file +Print the numbers of members and fetched members for each archive into the speicified file. .It Fl -push-state Save the current state of .Fl -as-needed , diff --git a/lld/test/ELF/print-archive-stats.s b/lld/test/ELF/print-archive-stats.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/print-archive-stats.s @@ -0,0 +1,37 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: echo '.globl weak; weak:' | llvm-mc -filetype=obj -triple=x86_64 - -o %tweak.o +# RUN: echo '.global foo; foo:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o +# RUN: echo '.global bar; bar:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o +# RUN: echo '.global baz; baz:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t3.o +# RUN: rm -f %tweak.a && llvm-ar rc %tweak.a %tweak.o +# RUN: rm -f %t1.a && llvm-ar rc %t1.a %t1.o %t2.o %t3.o + +# RUN: ld.lld %t.o %tweak.a %t1.a --print-archive-stats=%t.txt -o /dev/null +# RUN: FileCheck --input-file=%t.txt -DT=%t %s --match-full-lines --strict-whitespace + +## Fetches 0 member from %tweak.a and 2 members from %t1.a +# CHECK:members fetched archive +# CHECK-NEXT:1 0 [[T]]weak.a +# CHECK-NEXT:3 2 [[T]]1.a + +# RUN: ld.lld %t.o %tweak.a %t1.a --print-archive-stats=/dev/stdout -o /dev/null | diff %t.txt - + +## The second %t1.a has 0 fetched member. +# RUN: ld.lld %t.o %tweak.a %t1.a %t1.a --print-archive-stats=/dev/stdout -o /dev/null | \ +# RUN: FileCheck --check-prefix=CHECK2 %s +# CHECK2: members fetched archive +# CHECK2-NEXT: 1 0 {{.*}}weak.a +# CHECK2-NEXT: 3 2 {{.*}}1.a +# CHECK2-NEXT: 3 0 {{.*}}1.a + +# RUN: not ld.lld -shared %t.o --print-archive-stats=/ -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s +# ERR: error: cannot open /: {{.*}} + +.globl _start +.weak weak +_start: + call foo + call bar + call weak