[llvm-readobj] Optimize printing stack sizes to linear time.

Currently, each function name lookup is a linear iteration over all symbols defined in the object file which makes the total running time quadratic.

This patch optimizes the function name lookup by populating an **address to index** map upon the first function name lookup which is used to lookup each function name in O(1).

**impact**: For the clang binary built with `-fstack-size-section`, this improves the running time of `llvm-readobj --stack-size` from 7 minutes to 0.25 seconds.

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D103072
This commit is contained in:
Rahman Lavaee 2021-05-26 13:12:36 -07:00
parent fdf10e6197
commit 6505c63040
1 changed files with 68 additions and 42 deletions

View File

@ -298,6 +298,13 @@ protected:
std::vector<GroupSection> getGroups(); std::vector<GroupSection> getGroups();
// Returns the function symbol index for the given address. Matches the
// symbol's section with FunctionSec when specified.
// Returns None if no function symbol can be found for the address or in case
// it is not defined in the specified section.
Optional<uint32_t>
getSymbolIndexForFunctionAddress(uint64_t SymValue,
Optional<const Elf_Shdr *> FunctionSec);
bool printFunctionStackSize(uint64_t SymValue, bool printFunctionStackSize(uint64_t SymValue,
Optional<const Elf_Shdr *> FunctionSec, Optional<const Elf_Shdr *> FunctionSec,
const Elf_Shdr &StackSizeSec, DataExtractor Data, const Elf_Shdr &StackSizeSec, DataExtractor Data,
@ -353,6 +360,7 @@ protected:
const Elf_Shdr *DotAddrsigSec = nullptr; const Elf_Shdr *DotAddrsigSec = nullptr;
DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables; DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables;
Optional<uint64_t> SONameOffset; Optional<uint64_t> SONameOffset;
Optional<DenseMap<uint64_t, std::vector<uint32_t>>> AddressToIndexMap;
const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version
const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r
@ -5703,64 +5711,82 @@ template <class ELFT> void GNUELFDumper<ELFT>::printDependentLibs() {
} }
template <class ELFT> template <class ELFT>
bool ELFDumper<ELFT>::printFunctionStackSize( Optional<uint32_t> ELFDumper<ELFT>::getSymbolIndexForFunctionAddress(
uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec) {
const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) { if (!this->AddressToIndexMap.hasValue()) {
uint32_t FuncSymIndex = 0; // Populate the address to index map upon the first invocation of this
if (this->DotSymtabSec) { // function.
if (Expected<Elf_Sym_Range> SymsOrError = Obj.symbols(this->DotSymtabSec)) { this->AddressToIndexMap.emplace();
uint32_t Index = (uint32_t)-1; if (this->DotSymtabSec) {
for (const Elf_Sym &Sym : *SymsOrError) { if (Expected<Elf_Sym_Range> SymsOrError =
++Index; Obj.symbols(this->DotSymtabSec)) {
uint32_t Index = (uint32_t)-1;
for (const Elf_Sym &Sym : *SymsOrError) {
++Index;
if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC) if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC)
continue;
if (Expected<uint64_t> SymAddrOrErr =
ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress()) {
if (SymValue != *SymAddrOrErr)
continue; continue;
} else {
std::string Name = this->getStaticSymbolName(Index);
reportUniqueWarning("unable to get address of symbol '" + Name +
"': " + toString(SymAddrOrErr.takeError()));
break;
}
// Check if the symbol is in the right section. FunctionSec == None Expected<uint64_t> SymAddrOrErr =
// means "any section". ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress();
if (FunctionSec) { if (!SymAddrOrErr) {
if (Expected<const Elf_Shdr *> SecOrErr =
Obj.getSection(Sym, this->DotSymtabSec,
this->getShndxTable(this->DotSymtabSec))) {
if (*FunctionSec != *SecOrErr)
continue;
} else {
std::string Name = this->getStaticSymbolName(Index); std::string Name = this->getStaticSymbolName(Index);
// Note: it is impossible to trigger this error currently, it is reportUniqueWarning("unable to get address of symbol '" + Name +
// untested. "': " + toString(SymAddrOrErr.takeError()));
reportUniqueWarning("unable to get section of symbol '" + Name + return None;
"': " + toString(SecOrErr.takeError()));
break;
} }
}
FuncSymIndex = Index; (*this->AddressToIndexMap)[*SymAddrOrErr].push_back(Index);
break; }
} else {
reportUniqueWarning("unable to read the symbol table: " +
toString(SymsOrError.takeError()));
} }
} else {
reportUniqueWarning("unable to read the symbol table: " +
toString(SymsOrError.takeError()));
} }
} }
auto Symbols = this->AddressToIndexMap->find(SymValue);
if (Symbols == this->AddressToIndexMap->end())
return None;
for (uint32_t Index : Symbols->second) {
// Check if the symbol is in the right section. FunctionSec == None
// means "any section".
if (FunctionSec) {
const Elf_Sym &Sym = *cantFail(Obj.getSymbol(this->DotSymtabSec, Index));
if (Expected<const Elf_Shdr *> SecOrErr =
Obj.getSection(Sym, this->DotSymtabSec,
this->getShndxTable(this->DotSymtabSec))) {
if (*FunctionSec != *SecOrErr)
continue;
} else {
std::string Name = this->getStaticSymbolName(Index);
// Note: it is impossible to trigger this error currently, it is
// untested.
reportUniqueWarning("unable to get section of symbol '" + Name +
"': " + toString(SecOrErr.takeError()));
return None;
}
}
return Index;
}
return None;
}
template <class ELFT>
bool ELFDumper<ELFT>::printFunctionStackSize(
uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec,
const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) {
Optional<uint32_t> FuncSymIndex =
this->getSymbolIndexForFunctionAddress(SymValue, FunctionSec);
std::string FuncName = "?"; std::string FuncName = "?";
if (!FuncSymIndex) if (!FuncSymIndex)
reportUniqueWarning( reportUniqueWarning(
"could not identify function symbol for stack size entry in " + "could not identify function symbol for stack size entry in " +
describe(StackSizeSec)); describe(StackSizeSec));
else else
FuncName = this->getStaticSymbolName(FuncSymIndex); FuncName = this->getStaticSymbolName(*FuncSymIndex);
// Extract the size. The expectation is that Offset is pointing to the right // Extract the size. The expectation is that Offset is pointing to the right
// place, i.e. past the function address. // place, i.e. past the function address.