--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
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;
Symtab.scanDynamicList();
Symtab.scanVersionScript();
Symtab.scanSymbolVersions();
- Symtab.traceDefined();
Symtab.addCombinedLtoObject();
if (HasError)
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)
case SymbolBody::LazyArchiveKind:
case SymbolBody::LazyObjectKind:
break;
+ case SymbolBody::PlaceholderKind:
case SymbolBody::DefinedBitcodeKind:
llvm_unreachable("should have been replaced");
}
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) {
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
}
}
-// 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>;
void scanDynamicList();
void scanVersionScript();
void scanSymbolVersions();
- void traceDefined();
SymbolBody *find(StringRef Name);
+
+ void trace(StringRef Name);
void wrap(StringRef Name);
private:
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");
}
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;
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
+ PlaceholderKind,
};
+ SymbolBody(Kind K) : SymbolKind(K) {}
+
Symbol *symbol();
const Symbol *symbol() const {
return const_cast<SymbolBody *>(this)->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; }
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");
"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() {
# 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 | \