Skip to content

Commit

Permalink
[llvm-readobj] Add ELF hash histogram printing
Browse files Browse the repository at this point in the history
Differential Revision: http://reviews.llvm.org/D18907

llvm-svn: 265967
  • Loading branch information
Hemant Kulkarni committed Apr 11, 2016
1 parent 8dd4ca8 commit 9b1b7f0
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 0 deletions.
27 changes: 27 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-hash-histogram.test
@@ -0,0 +1,27 @@
RUN: llvm-readobj -elf-hash-histogram %p/Inputs/gnuhash.so.elf-ppc64 \
RUN: --elf-output-style=GNU | FileCheck %s -check-prefix PPC64GNU
RUN: llvm-readobj -elf-hash-histogram %p/Inputs/gnuhash.so.elf-x86_64 --elf-output-style=GNU \
RUN: | FileCheck %s -check-prefix X86GNU
RUN: llvm-readobj -elf-hash-histogram %p/Inputs/got-plt.exe.elf-mipsel --elf-output-style=GNU \
RUN: | FileCheck %s -check-prefix SYSV

PPC64GNU: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
PPC64GNU-NEXT: Length Number % of total Coverage
PPC64GNU-NEXT: 0 1 ( 33.3%) 0.0%
PPC64GNU-NEXT: 1 1 ( 33.3%) 25.0%
PPC64GNU-NEXT: 2 0 ( 0.0%) 25.0%
PPC64GNU-NEXT: 3 1 ( 33.3%) 100.0%

X86GNU: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
X86GNU-NEXT: Length Number % of total Coverage
X86GNU-NEXT: 0 1 ( 33.3%) 0.0%
X86GNU-NEXT: 1 1 ( 33.3%) 25.0%
X86GNU-NEXT: 2 0 ( 0.0%) 25.0%
X86GNU-NEXT: 3 1 ( 33.3%) 100.0%

SYSV: Histogram for bucket list length (total of 3 buckets)
SYSV-NEXT: Length Number % of total Coverage
SYSV-NEXT: 0 0 ( 0.0%) 0.0%
SYSV-NEXT: 1 0 ( 0.0%) 0.0%
SYSV-NEXT: 2 2 ( 66.7%) 57.1%
SYSV-NEXT: 3 1 ( 33.3%) 100.0%
121 changes: 121 additions & 0 deletions llvm/tools/llvm-readobj/ELFDumper.cpp
Expand Up @@ -61,6 +61,8 @@ using namespace ELF;
typedef typename ELFO::Elf_Half Elf_Half; \
typedef typename ELFO::Elf_Ehdr Elf_Ehdr; \
typedef typename ELFO::Elf_Word Elf_Word; \
typedef typename ELFO::Elf_Hash Elf_Hash; \
typedef typename ELFO::Elf_GnuHash Elf_GnuHash; \
typedef typename ELFO::uintX_t uintX_t;

namespace {
Expand Down Expand Up @@ -121,6 +123,8 @@ class ELFDumper : public ObjDumper {

void printStackMap() const override;

void printHashHistogram() override;

private:
std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle;
typedef ELFFile<ELFT> ELFO;
Expand Down Expand Up @@ -234,6 +238,8 @@ class ELFDumper : public ObjDumper {
const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; }
const DynRegionInfo &getDynRelaRegion() const { return DynRelaRegion; }
const DynRegionInfo &getDynPLTRelRegion() const { return DynPLTRelRegion; }
const Elf_Hash *getHashTable() const { return HashTable; }
const Elf_GnuHash *getGnuHashTable() const { return GnuHashTable; }
};

template <class ELFT>
Expand Down Expand Up @@ -284,6 +290,7 @@ template <typename ELFT> class DumpStyle {
const Elf_Sym *FirstSym, StringRef StrTable,
bool IsDynamic) = 0;
virtual void printProgramHeaders(const ELFFile<ELFT> *Obj) = 0;
virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0;
const ELFDumper<ELFT> *dumper() const { return Dumper; }
private:
const ELFDumper<ELFT> *Dumper;
Expand All @@ -305,6 +312,7 @@ template <typename ELFT> class GNUStyle : public DumpStyle<ELFT> {
virtual void printSymtabMessage(const ELFO *Obj, StringRef Name,
size_t Offset) override;
void printProgramHeaders(const ELFO *Obj) override;
void printHashHistogram(const ELFFile<ELFT> *Obj) override;

private:
struct Field {
Expand Down Expand Up @@ -357,6 +365,7 @@ template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> {
void printDynamicSymbols(const ELFO *Obj) override;
void printDynamicRelocations(const ELFO *Obj) override;
void printProgramHeaders(const ELFO *Obj) override;
void printHashHistogram(const ELFFile<ELFT> *Obj) override;

private:
void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab);
Expand Down Expand Up @@ -1390,6 +1399,9 @@ void ELFDumper<ELFT>::printDynamicSymbols() {
ELFDumperStyle->printDynamicSymbols(Obj);
}

template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
ELFDumperStyle->printHashHistogram(Obj);
}
#define LLVM_READOBJ_TYPE_CASE(name) \
case DT_##name: return #name

Expand Down Expand Up @@ -2920,6 +2932,111 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
}
}

// Hash histogram shows statistics of how efficient the hash was for the
// dynamic symbol table. The table shows number of hash buckets for different
// lengths of chains as absolute number and percentage of the total buckets.
// Additionally cumulative coverage of symbols for each set of buckets.
template <class ELFT>
void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {

const Elf_Hash *HashTable = this->dumper()->getHashTable();
const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable();

// Print histogram for .hash section
if (HashTable) {
size_t NBucket = HashTable->nbucket;
size_t NChain = HashTable->nchain;
ArrayRef<Elf_Word> Buckets = HashTable->buckets();
ArrayRef<Elf_Word> Chains = HashTable->chains();
size_t TotalSyms = 0;
// If hash table is correct, we have at least chains with 0 length
size_t MaxChain = 1;
size_t CumulativeNonZero = 0;

if (NChain == 0 || NBucket == 0)
return;

std::vector<size_t> ChainLen(NBucket, 0);
// Go over all buckets and and note chain lengths of each bucket (total
// unique chain lengths).
for (size_t B = 0; B < NBucket; B++) {
for (size_t C = Buckets[B]; C > 0 && C < NChain; C = Chains[C])
if (MaxChain <= ++ChainLen[B])
MaxChain++;
TotalSyms += ChainLen[B];
}

if (!TotalSyms)
return;

std::vector<size_t> Count(MaxChain, 0) ;
// Count how long is the chain for each bucket
for (size_t B = 0; B < NBucket; B++)
++Count[ChainLen[B]];
// Print Number of buckets with each chain lengths and their cumulative
// coverage of the symbols
OS << "Histogram for bucket list length (total of " << NBucket
<< " buckets)\n"
<< " Length Number % of total Coverage\n";
for (size_t I = 0; I < MaxChain; I++) {
CumulativeNonZero += Count[I] * I;
OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
(Count[I] * 100.0) / NBucket,
(CumulativeNonZero * 100.0) / TotalSyms);
}
}

// Print histogram for .gnu.hash section
if (GnuHashTable) {
size_t NBucket = GnuHashTable->nbuckets;
ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
unsigned NumSyms = this->dumper()->dynamic_symbols().size();
if (!NumSyms)
return;
ArrayRef<Elf_Word> Chains = GnuHashTable->values(NumSyms);
size_t Symndx = GnuHashTable->symndx;
size_t TotalSyms = 0;
size_t MaxChain = 1;
size_t CumulativeNonZero = 0;

if (Chains.size() == 0 || NBucket == 0)
return;

std::vector<size_t> ChainLen(NBucket, 0);

for (size_t B = 0; B < NBucket; B++) {
if (!Buckets[B])
continue;
size_t Len = 1;
for (size_t C = Buckets[B] - Symndx;
C < Chains.size() && (Chains[C] & 1) == 0; C++)
if (MaxChain < ++Len)
MaxChain++;
ChainLen[B] = Len;
TotalSyms += Len;
}
MaxChain++;

if (!TotalSyms)
return;

std::vector<size_t> Count(MaxChain, 0) ;
for (size_t B = 0; B < NBucket; B++)
++Count[ChainLen[B]];
// Print Number of buckets with each chain lengths and their cumulative
// coverage of the symbols
OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket
<< " buckets)\n"
<< " Length Number % of total Coverage\n";
for (size_t I = 0; I <MaxChain; I++) {
CumulativeNonZero += Count[I] * I;
OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
(Count[I] * 100.0) / NBucket,
(CumulativeNonZero * 100.0) / TotalSyms);
}
}
}

template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
const Elf_Ehdr *e = Obj->getHeader();
{
Expand Down Expand Up @@ -3281,3 +3398,7 @@ void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) {
W.printNumber("Alignment", Phdr.p_align);
}
}
template <class ELFT>
void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
W.startLine() << "Hash Histogram not implemented!\n";
}
1 change: 1 addition & 0 deletions llvm/tools/llvm-readobj/ObjDumper.h
Expand Up @@ -43,6 +43,7 @@ class ObjDumper {
virtual void printLoadName() {}
virtual void printVersionInfo() {}
virtual void printGroupSections() {}
virtual void printHashHistogram() {}

// Only implemented for ARM ELF at this time.
virtual void printAttributes() { }
Expand Down
7 changes: 7 additions & 0 deletions llvm/tools/llvm-readobj/llvm-readobj.cpp
Expand Up @@ -232,6 +232,11 @@ namespace opts {
cl::desc("Display ELF section group contents"));
cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"),
cl::aliasopt(SectionGroups));
cl::opt<bool> HashHistogram(
"elf-hash-histogram",
cl::desc("Display bucket list histogram for hash sections"));
cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"),
cl::aliasopt(HashHistogram));

cl::opt<OutputStyleTy>
Output("elf-output-style", cl::desc("Specify ELF dump style"),
Expand Down Expand Up @@ -360,6 +365,8 @@ static void dumpObject(const ObjectFile *Obj) {
}
if (opts::SectionGroups)
Dumper->printGroupSections();
if (opts::HashHistogram)
Dumper->printHashHistogram();
}
if (Obj->isCOFF()) {
if (opts::COFFImports)
Expand Down

0 comments on commit 9b1b7f0

Please sign in to comment.