Implement almost-zero-cost --trace-symbol.
authorRui Ueyama <ruiu@google.com>
Sun, 17 Jul 2016 17:50:09 +0000 (17:50 +0000)
committerRui Ueyama <ruiu@google.com>
Sun, 17 Jul 2016 17:50:09 +0000 (17:50 +0000)
--trace-symbol is a command line option to watch a symbol.
Previosly, we looked up a hash table for a new symbol if the
option is given. Any code that looks up a hash table for each
symbol is expensive because the linker handles a lot of symbols.
In our design, we look up a hash table strictly only once
for a symbol, so --trace-symbol was an exception.

This patch improves efficiency of the option by merging the
hash table into the symbol table.

Instead of looking up a separate hash table with a string,
this patch sets `Traced` flag to symbols specified by --trace-symbol.
So, if you insert a symbol and get a symbol with `Traced` flag on,
you know that you need to print out a log message for the symbol.
This is nearly zero cost.

llvm-svn: 275716

lld/ELF/Driver.cpp
lld/ELF/InputFiles.cpp
lld/ELF/OutputSections.cpp
lld/ELF/SymbolTable.cpp
lld/ELF/SymbolTable.h
lld/ELF/Symbols.cpp
lld/ELF/Symbols.h
lld/test/ELF/trace-symbols.s

index fa3d628..d397cae 100644 (file)
@@ -528,6 +528,10 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
   if (Config->OutputFile.empty())
     Config->OutputFile = "a.out";
 
+  // Handle --trace-symbol.
+  for (auto *Arg : Args.filtered(OPT_trace_symbol))
+    Symtab.trace(Arg->getValue());
+
   // Set either EntryAddr (if S is a number) or EntrySym (otherwise).
   if (!Config->Entry.empty()) {
     StringRef S = Config->Entry;
@@ -556,7 +560,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
   Symtab.scanDynamicList();
   Symtab.scanVersionScript();
   Symtab.scanSymbolVersions();
-  Symtab.traceDefined();
 
   Symtab.addCombinedLtoObject();
   if (HasError)
index dbc004c..57e5563 100644 (file)
@@ -360,11 +360,6 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
 
   switch (Sym->st_shndx) {
   case SHN_UNDEF:
-    // Handle --trace-symbol option. Prints out a log message
-    // if the current symbol is being watched. Useful for debugging.
-    if (!Config->TraceSymbol.empty() && Config->TraceSymbol.count(Name))
-      outs() << getFilename(this) << ": reference to " << Name << "\n";
-
     return elf::Symtab<ELFT>::X
         ->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
                        /*CanOmitFromDynSym*/ false, this)
index 50b9401..fd97c6b 100644 (file)
@@ -1465,6 +1465,7 @@ SymbolTableSection<ELFT>::getOutputSection(SymbolBody *Sym) {
   case SymbolBody::LazyArchiveKind:
   case SymbolBody::LazyObjectKind:
     break;
+  case SymbolBody::PlaceholderKind:
   case SymbolBody::DefinedBitcodeKind:
     llvm_unreachable("should have been replaced");
   }
index cce0acb..0eac112 100644 (file)
@@ -140,6 +140,22 @@ DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name,
   return addAbsolute(Name, Visibility);
 }
 
+// Set a flag for --trace-symbol so that we can print out a log message
+// if a new symbol with the same name is inserted into the symbol table.
+template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
+  Symbol *S;
+  bool WasInserted;
+  std::tie(S, WasInserted) = insert(Name);
+  assert(WasInserted);
+
+  S->Traced = true;
+
+  // We created a new symbol just to turn on Trace flag.
+  // Write a dummy SymbolBody so that trace() does not affect
+  // normal symbol operations.
+  new (S->body()) SymbolBody(SymbolBody::PlaceholderKind);
+}
+
 // Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
 // Used to implement --wrap.
 template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
@@ -170,19 +186,24 @@ template <class ELFT>
 std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
   unsigned NumSyms = SymVector.size();
   auto P = Symtab.insert(std::make_pair(Name, NumSyms));
+  bool IsNew = P.second;
+
   Symbol *Sym;
-  if (P.second) {
+  if (IsNew) {
     Sym = new (Alloc) Symbol;
     Sym->Binding = STB_WEAK;
     Sym->Visibility = STV_DEFAULT;
     Sym->IsUsedInRegularObj = false;
     Sym->ExportDynamic = false;
     Sym->VersionId = Config->DefaultSymbolVersion;
+    Sym->Traced = false;
     SymVector.push_back(Sym);
   } else {
     Sym = SymVector[P.first->second];
+    if (Sym->body()->kind() == SymbolBody::PlaceholderKind)
+      IsNew = true;
   }
-  return {Sym, P.second};
+  return {Sym, IsNew};
 }
 
 // Find an existing symbol or create and insert a new one, then apply the given
@@ -689,17 +710,6 @@ template <class ELFT> void SymbolTable<ELFT>::scanSymbolVersions() {
   }
 }
 
-// Print the module names which define the notified
-// symbols provided through -y or --trace-symbol option.
-template <class ELFT> void SymbolTable<ELFT>::traceDefined() {
-  for (const auto &Symbol : Config->TraceSymbol)
-    if (SymbolBody *B = find(Symbol.getKey()))
-      if (B->isDefined() || B->isCommon())
-        if (B->File)
-          outs() << getFilename(B->File) << ": definition of " << B->getName()
-                 << "\n";
-}
-
 template class elf::SymbolTable<ELF32LE>;
 template class elf::SymbolTable<ELF32BE>;
 template class elf::SymbolTable<ELF64LE>;
index 61fd50f..727f2e9 100644 (file)
@@ -83,9 +83,10 @@ public:
   void scanDynamicList();
   void scanVersionScript();
   void scanSymbolVersions();
-  void traceDefined();
 
   SymbolBody *find(StringRef Name);
+
+  void trace(StringRef Name);
   void wrap(StringRef Name);
 
 private:
index 724a922..4f95281 100644 (file)
@@ -79,6 +79,7 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
   case SymbolBody::LazyObjectKind:
     assert(Body.symbol()->IsUsedInRegularObj && "lazy symbol reached writer");
     return 0;
+  case SymbolBody::PlaceholderKind:
   case SymbolBody::DefinedBitcodeKind:
     llvm_unreachable("should have been replaced");
   }
@@ -270,6 +271,21 @@ bool Symbol::includeInDynsym() const {
   return (ExportDynamic && VersionId != VER_NDX_LOCAL) || body()->isShared() ||
          (body()->isUndefined() && Config->Shared);
 }
+
+// Print out a log message for --trace-symbol.
+void elf::printTraceSymbol(Symbol *Sym) {
+  SymbolBody *B = Sym->body();
+  outs() << getFilename(B->File);
+
+  if (B->isUndefined())
+    outs() << ": reference to ";
+  else if (B->isCommon())
+    outs() << ": common definition of ";
+  else
+    outs() << ": definition of ";
+  outs() << B->getName() << "\n";
+}
+
 template bool SymbolBody::hasThunk<ELF32LE>() const;
 template bool SymbolBody::hasThunk<ELF32BE>() const;
 template bool SymbolBody::hasThunk<ELF64LE>() const;
index fb9eb68..6847f46 100644 (file)
@@ -51,8 +51,11 @@ public:
     UndefinedKind,
     LazyArchiveKind,
     LazyObjectKind,
+    PlaceholderKind,
   };
 
+  SymbolBody(Kind K) : SymbolKind(K) {}
+
   Symbol *symbol();
   const Symbol *symbol() const {
     return const_cast<SymbolBody *>(this)->symbol();
@@ -420,6 +423,9 @@ struct Symbol {
   // --export-dynamic, and by dynamic lists.
   unsigned ExportDynamic : 1;
 
+  // True if this symbol is specified by --trace-symbol option.
+  unsigned Traced : 1;
+
   bool includeInDynsym() const;
   bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
 
@@ -438,6 +444,8 @@ struct Symbol {
   const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
 };
 
+void printTraceSymbol(Symbol *Sym);
+
 template <typename T, typename... ArgT>
 void replaceBody(Symbol *S, ArgT &&... Arg) {
   static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
@@ -446,7 +454,13 @@ void replaceBody(Symbol *S, ArgT &&... Arg) {
                 "Body not aligned enough");
   assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
          "Not a SymbolBody");
+
   new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
+
+  // Print out a log message if --trace-symbol was specified.
+  // This is for debugging.
+  if (S->Traced)
+    printTraceSymbol(S);
 }
 
 inline Symbol *SymbolBody::symbol() {
index 2dd426d..7f6bca8 100644 (file)
 
 # RUN: ld.lld -y foo -trace-symbol=common -trace-symbol=hsymbol \
 # RUN:   %t %t1 %t2 -o %t3 2>&1 | FileCheck -check-prefix=OBJECTDCOMMON %s
-# OBJECTDCOMMON: trace-symbols.s.tmp1: definition of common
+# OBJECTDCOMMON: trace-symbols.s.tmp1: common definition of common
 
-# RUN: ld.lld -y foo -y common %t %t2 %t1 -o %t3 2>&1 | \
-# RUN:   FileCheck -check-prefix=OBJECTD1FOO %s
 # RUN: ld.lld -y foo -trace-symbol=common -trace-symbol=hsymbol \
 # RUN:   %t %t1 %t2 -o %t3 2>&1 | FileCheck -check-prefix=OBJECTD1FOO %s
-# OBJECTD1FOO-NOT: trace-symbols.s.tmp1: definition of foo
+# OBJECTD1FOO: trace-symbols.s.tmp: reference to foo
+# OBJECTD1FOO: trace-symbols.s.tmp1: common definition of common
+# OBJECTD1FOO: trace-symbols.s.tmp1: definition of foo
+# OBJECTD1FOO: trace-symbols.s.tmp2: definition of foo
 
 # RUN: ld.lld -y foo -trace-symbol=common -trace-symbol=hsymbol \
 # RUN:   %t %t1 %t2 -o %t3 2>&1 | FileCheck -check-prefix=OBJECTD2FOO %s
 # RUN:   FileCheck -check-prefix=SHLIBDCOMMON %s
 # SHLIBDCOMMON: trace-symbols.s.tmp1.so: definition of common
 
-# RUN: ld.lld -y foo -y common %t %t1.so %t2.so -o %t3 2>&1 | \
-# RUN:   FileCheck -check-prefix=SHLIBD1FOO %s
-# RUN: ld.lld -y foo %t %t1.so %t2.a -o %t3 | \
-# RUN:   FileCheck -check-prefix=SHLIBD1FOO %s
-# RUN: ld.lld -y foo -y common %t %t1.so %t2 -o %t3 2>&1 | \
-# RUN:   FileCheck -check-prefix=NO-SHLIBD1FOO %s
-# SHLIBD1FOO:        trace-symbols.s.tmp1.so: definition of foo
-# NO-SHLIBD1FOO-NOT: trace-symbols.s.tmp1.so: definition of foo
-
 # RUN: ld.lld -y foo -y common %t %t2.so %t1.so -o %t3 2>&1 | \
 # RUN:   FileCheck -check-prefix=SHLIBD2FOO %s
 # RUN: ld.lld -y foo %t %t1.a %t2.so -o %t3 | \