[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();
// 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,
Optional<const Elf_Shdr *> FunctionSec,
const Elf_Shdr &StackSizeSec, DataExtractor Data,
@ -353,6 +360,7 @@ protected:
const Elf_Shdr *DotAddrsigSec = nullptr;
DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables;
Optional<uint64_t> SONameOffset;
Optional<DenseMap<uint64_t, std::vector<uint32_t>>> AddressToIndexMap;
const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version
const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r
@ -5703,64 +5711,82 @@ template <class ELFT> void GNUELFDumper<ELFT>::printDependentLibs() {
}
template <class ELFT>
bool ELFDumper<ELFT>::printFunctionStackSize(
uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec,
const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) {
uint32_t FuncSymIndex = 0;
if (this->DotSymtabSec) {
if (Expected<Elf_Sym_Range> SymsOrError = Obj.symbols(this->DotSymtabSec)) {
uint32_t Index = (uint32_t)-1;
for (const Elf_Sym &Sym : *SymsOrError) {
++Index;
Optional<uint32_t> ELFDumper<ELFT>::getSymbolIndexForFunctionAddress(
uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec) {
if (!this->AddressToIndexMap.hasValue()) {
// Populate the address to index map upon the first invocation of this
// function.
this->AddressToIndexMap.emplace();
if (this->DotSymtabSec) {
if (Expected<Elf_Sym_Range> SymsOrError =
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)
continue;
if (Expected<uint64_t> SymAddrOrErr =
ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress()) {
if (SymValue != *SymAddrOrErr)
if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC)
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
// means "any section".
if (FunctionSec) {
if (Expected<const Elf_Shdr *> SecOrErr =
Obj.getSection(Sym, this->DotSymtabSec,
this->getShndxTable(this->DotSymtabSec))) {
if (*FunctionSec != *SecOrErr)
continue;
} else {
Expected<uint64_t> SymAddrOrErr =
ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress();
if (!SymAddrOrErr) {
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()));
break;
reportUniqueWarning("unable to get address of symbol '" + Name +
"': " + toString(SymAddrOrErr.takeError()));
return None;
}
}
FuncSymIndex = Index;
break;
(*this->AddressToIndexMap)[*SymAddrOrErr].push_back(Index);
}
} 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 = "?";
if (!FuncSymIndex)
reportUniqueWarning(
"could not identify function symbol for stack size entry in " +
describe(StackSizeSec));
else
FuncName = this->getStaticSymbolName(FuncSymIndex);
FuncName = this->getStaticSymbolName(*FuncSymIndex);
// Extract the size. The expectation is that Offset is pointing to the right
// place, i.e. past the function address.