From 69c778c084faafe8ddbc7ece947762241594a92f Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 17 Jul 2016 17:50:09 +0000 Subject: [PATCH] Implement almost-zero-cost --trace-symbol. --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 | 5 ++++- lld/ELF/InputFiles.cpp | 5 ----- lld/ELF/OutputSections.cpp | 1 + lld/ELF/SymbolTable.cpp | 36 +++++++++++++++++++++++------------- lld/ELF/SymbolTable.h | 3 ++- lld/ELF/Symbols.cpp | 16 ++++++++++++++++ lld/ELF/Symbols.h | 14 ++++++++++++++ lld/test/ELF/trace-symbols.s | 18 +++++------------- 8 files changed, 65 insertions(+), 33 deletions(-) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index fa3d6282670b..d397cae5329e 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -528,6 +528,10 @@ template 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 void LinkerDriver::link(opt::InputArgList &Args) { Symtab.scanDynamicList(); Symtab.scanVersionScript(); Symtab.scanSymbolVersions(); - Symtab.traceDefined(); Symtab.addCombinedLtoObject(); if (HasError) diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index dbc004cac8be..57e556395937 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -360,11 +360,6 @@ SymbolBody *elf::ObjectFile::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::X ->addUndefined(Name, Binding, Sym->st_other, Sym->getType(), /*CanOmitFromDynSym*/ false, this) diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 50b94015f229..fd97c6be3820 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -1465,6 +1465,7 @@ SymbolTableSection::getOutputSection(SymbolBody *Sym) { case SymbolBody::LazyArchiveKind: case SymbolBody::LazyObjectKind: break; + case SymbolBody::PlaceholderKind: case SymbolBody::DefinedBitcodeKind: llvm_unreachable("should have been replaced"); } diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index cce0acbc71e4..0eac1127de9d 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -140,6 +140,22 @@ DefinedRegular *SymbolTable::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 void SymbolTable::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 void SymbolTable::wrap(StringRef Name) { @@ -170,19 +186,24 @@ template std::pair SymbolTable::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 void SymbolTable::scanSymbolVersions() { } } -// Print the module names which define the notified -// symbols provided through -y or --trace-symbol option. -template void SymbolTable::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; template class elf::SymbolTable; template class elf::SymbolTable; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index 61fd50fb4c67..727f2e96c2b5 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -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: diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 724a92247e8b..4f95281e04dd 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -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() const; template bool SymbolBody::hasThunk() const; template bool SymbolBody::hasThunk() const; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index fb9eb68c18d7..6847f46a0836 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -51,8 +51,11 @@ public: UndefinedKind, LazyArchiveKind, LazyObjectKind, + PlaceholderKind, }; + SymbolBody(Kind K) : SymbolKind(K) {} + Symbol *symbol(); const Symbol *symbol() const { return const_cast(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(this)->body(); } }; +void printTraceSymbol(Symbol *Sym); + template 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(static_cast(nullptr)) == nullptr && "Not a SymbolBody"); + new (S->Body.buffer) T(std::forward(Arg)...); + + // Print out a log message if --trace-symbol was specified. + // This is for debugging. + if (S->Traced) + printTraceSymbol(S); } inline Symbol *SymbolBody::symbol() { diff --git a/lld/test/ELF/trace-symbols.s b/lld/test/ELF/trace-symbols.s index 2dd426db635b..7f6bca8be216 100644 --- a/lld/test/ELF/trace-symbols.s +++ b/lld/test/ELF/trace-symbols.s @@ -16,13 +16,14 @@ # 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 @@ -38,15 +39,6 @@ # 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 | \