[lld] b912b88 - [ELF] Add --print-archive-stats=

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 18:09:01 PDT 2020


Author: Fangrui Song
Date: 2020-04-29T18:04:37-07:00
New Revision: b912b887d87c96a7ef900091cf11610a08a98c24

URL: https://github.com/llvm/llvm-project/commit/b912b887d87c96a7ef900091cf11610a08a98c24
DIFF: https://github.com/llvm/llvm-project/commit/b912b887d87c96a7ef900091cf11610a08a98c24.diff

LOG: [ELF] Add --print-archive-stats=

gold has an option --print-symbol-counts= which prints:

  // For each archive
  archive $archive $members $fetched_members
  // For each object file
  symbols $object $defined_symbols $used_defined_symbols

In most cases, `$defined_symbols = $used_defined_symbols` unless weak
symbols are present. Strangely `$used_defined_symbols` includes symbols defined relative to --gc-sections discarded sections.
The `symbols` lines do not appear to be useful.

`archive` lines are useful: `$fetched_members=0` lines correspond to
unused archives. The information can be used to trim dependencies.

This patch implements --print-archive-stats= which prints the number of
members and the number of fetched members for each archive.

Reviewed By: grimar

Differential Revision: https://reviews.llvm.org/D78983

Added: 
    lld/test/ELF/print-archive-stats.s

Modified: 
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/InputFiles.cpp
    lld/ELF/InputFiles.h
    lld/ELF/MapFile.cpp
    lld/ELF/MapFile.h
    lld/ELF/Options.td
    lld/ELF/Writer.cpp
    lld/docs/ld.lld.1

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index cf2c70c4b99a..7e5ee7acef08 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -110,6 +110,7 @@ struct Configuration {
   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.cpp b/lld/ELF/Driver.cpp
index 31a2a3a3a860..4fa1d60826d0 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -90,6 +90,7 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
 
   inputSections.clear();
   outputSections.clear();
+  archiveFiles.clear();
   binaryFiles.clear();
   bitcodeFiles.clear();
   lazyObjFiles.clear();
@@ -955,6 +956,7 @@ static void readConfigs(opt::InputArgList &args) {
       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.cpp b/lld/ELF/InputFiles.cpp
index f3dd11fde3f6..e3cf340cabe1 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -55,6 +55,7 @@ std::string toString(const elf::InputFile *f) {
 namespace elf {
 bool InputFile::isInGroup;
 uint32_t InputFile::nextGroupId;
+std::vector<ArchiveFile *> archiveFiles;
 std::vector<BinaryFile *> binaryFiles;
 std::vector<BitcodeFile *> bitcodeFiles;
 std::vector<LazyObjFile *> lazyObjFiles;
@@ -173,6 +174,7 @@ template <class ELFT> static void doParseFile(InputFile *file) {
 
   // .a file
   if (auto *f = dyn_cast<ArchiveFile>(file)) {
+    archiveFiles.push_back(f);
     f->parse();
     return;
   }
@@ -1165,6 +1167,19 @@ void ArchiveFile::fetch(const Archive::Symbol &sym) {
   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;
+  }
+  // This function is used by --print-archive-stats=, where an error does not
+  // really matter.
+  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/InputFiles.h b/lld/ELF/InputFiles.h
index d08c533e047b..48146e60e63e 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -326,6 +326,9 @@ class ArchiveFile : public InputFile {
   // more than once.)
   void fetch(const Archive::Symbol &sym);
 
+  size_t getMemberCount() const;
+  size_t getFetchedMemberCount() const { return seen.size(); }
+
 private:
   std::unique_ptr<Archive> file;
   llvm::DenseSet<uint64_t> seen;
@@ -387,6 +390,7 @@ inline bool isBitcode(MemoryBufferRef mb) {
 
 std::string replaceThinLTOSuffix(StringRef path);
 
+extern std::vector<ArchiveFile *> archiveFiles;
 extern std::vector<BinaryFile *> binaryFiles;
 extern std::vector<BitcodeFile *> bitcodeFiles;
 extern std::vector<LazyObjFile *> lazyObjFiles;

diff  --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp
index e5f5c4f4ff23..42181574d106 100644
--- a/lld/ELF/MapFile.cpp
+++ b/lld/ELF/MapFile.cpp
@@ -259,5 +259,23 @@ void writeCrossReferenceTable() {
   }
 }
 
+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("--print-archive-stats=: cannot open " + config->printArchiveStats +
+          ": " + ec.message());
+    return;
+  }
+
+  os << "members\tfetched\tarchive\n";
+  for (const ArchiveFile *f : archiveFiles)
+    os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t'
+       << f->getName() << '\n';
+}
+
 } // namespace elf
 } // namespace lld

diff  --git a/lld/ELF/MapFile.h b/lld/ELF/MapFile.h
index 7e7938919edf..c4da18f8ad7f 100644
--- a/lld/ELF/MapFile.h
+++ b/lld/ELF/MapFile.h
@@ -13,6 +13,7 @@ namespace lld {
 namespace elf {
 void writeMapFile();
 void writeCrossReferenceTable();
+void writeArchiveStats();
 } // namespace elf
 } // namespace lld
 

diff  --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index cb0c12a9a320..ae32cab5c33b 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -294,6 +294,10 @@ defm print_icf_sections: B<"print-icf-sections",
     "List identical folded sections",
     "Do not list identical folded sections (default)">;
 
+def print_archive_stats: J<"print-archive-stats=">,
+  HelpText<"Write archive usage statistics to the specified file. "
+           "Print the numbers of members and fetched members for each archive">;
+
 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
index bca5989861f9..6236ac51b1bb 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -601,11 +601,13 @@ template <class ELFT> void Writer<ELFT>::run() {
     for (OutputSection *sec : outputSections)
       sec->addr = 0;
 
-  // Handle --print-map(-M)/--Map and --cref. Dump them before checkSections()
-  // because the files may be useful in case checkSections() or openFile()
-  // fails, for example, due to an erroneous file size.
+  // Handle --print-map(-M)/--Map, --cref and --print-archive-stats=. Dump them
+  // before checkSections() because the files may be useful in case
+  // checkSections() or openFile() 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
index 1d55cf052c9b..54c0d7162d6b 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -414,6 +414,9 @@ List removed unused sections.
 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
+Write archive usage statistics to the specified file.
+Print the numbers of members and fetched members for each archive.
 .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
index 000000000000..3f5c4820f0c6
--- /dev/null
+++ b/lld/test/ELF/print-archive-stats.s
@@ -0,0 +1,38 @@
+# 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
+
+## - means stdout.
+# RUN: ld.lld %t.o %tweak.a %t1.a --print-archive-stats=- -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=- -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: --print-archive-stats=: cannot open /: {{.*}}
+
+.globl _start
+.weak weak
+_start:
+  call foo
+  call bar
+  call weak


        


More information about the llvm-commits mailing list