--- /dev/null
+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%
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 {
void printStackMap() const override;
+ void printHashHistogram() override;
+
private:
std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle;
typedef ELFFile<ELFT> ELFO;
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>
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;
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 {
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);
ELFDumperStyle->printDynamicSymbols(Obj);
}
+template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
+ ELFDumperStyle->printHashHistogram(Obj);
+}
#define LLVM_READOBJ_TYPE_CASE(name) \
case DT_##name: return #name
}
}
+// 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();
{
W.printNumber("Alignment", Phdr.p_align);
}
}
+template <class ELFT>
+void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
+ W.startLine() << "Hash Histogram not implemented!\n";
+}