[llvm] r265967 - [llvm-readobj] Add ELF hash histogram printing

Hemant Kulkarni via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 11 10:15:30 PDT 2016


Author: khemant
Date: Mon Apr 11 12:15:30 2016
New Revision: 265967

URL: http://llvm.org/viewvc/llvm-project?rev=265967&view=rev
Log:
[llvm-readobj] Add ELF hash histogram printing

Differential Revision: http://reviews.llvm.org/D18907

Added:
    llvm/trunk/test/tools/llvm-readobj/elf-hash-histogram.test
Modified:
    llvm/trunk/tools/llvm-readobj/ELFDumper.cpp
    llvm/trunk/tools/llvm-readobj/ObjDumper.h
    llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp

Added: llvm/trunk/test/tools/llvm-readobj/elf-hash-histogram.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-readobj/elf-hash-histogram.test?rev=265967&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-readobj/elf-hash-histogram.test (added)
+++ llvm/trunk/test/tools/llvm-readobj/elf-hash-histogram.test Mon Apr 11 12:15:30 2016
@@ -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%

Modified: llvm/trunk/tools/llvm-readobj/ELFDumper.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-readobj/ELFDumper.cpp?rev=265967&r1=265966&r2=265967&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-readobj/ELFDumper.cpp (original)
+++ llvm/trunk/tools/llvm-readobj/ELFDumper.cpp Mon Apr 11 12:15:30 2016
@@ -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 {
@@ -121,6 +123,8 @@ public:
 
   void printStackMap() const override;
 
+  void printHashHistogram() override;
+
 private:
   std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle;
   typedef ELFFile<ELFT> ELFO;
@@ -234,6 +238,8 @@ public:
   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>
@@ -284,6 +290,7 @@ public:
                            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;
@@ -305,6 +312,7 @@ public:
   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 {
@@ -357,6 +365,7 @@ public:
   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);
@@ -1390,6 +1399,9 @@ void ELFDumper<ELFT>::printDynamicSymbol
   ELFDumperStyle->printDynamicSymbols(Obj);
 }
 
+template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
+  ELFDumperStyle->printHashHistogram(Obj);
+}
 #define LLVM_READOBJ_TYPE_CASE(name) \
   case DT_##name: return #name
 
@@ -2920,6 +2932,111 @@ void GNUStyle<ELFT>::printDynamicRelocat
   }
 }
 
+// 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();
   {
@@ -3281,3 +3398,7 @@ void LLVMStyle<ELFT>::printProgramHeader
     W.printNumber("Alignment", Phdr.p_align);
   }
 }
+template <class ELFT>
+void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
+  W.startLine() << "Hash Histogram not implemented!\n";
+}

Modified: llvm/trunk/tools/llvm-readobj/ObjDumper.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-readobj/ObjDumper.h?rev=265967&r1=265966&r2=265967&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-readobj/ObjDumper.h (original)
+++ llvm/trunk/tools/llvm-readobj/ObjDumper.h Mon Apr 11 12:15:30 2016
@@ -43,6 +43,7 @@ public:
   virtual void printLoadName() {}
   virtual void printVersionInfo() {}
   virtual void printGroupSections() {}
+  virtual void printHashHistogram() {}
 
   // Only implemented for ARM ELF at this time.
   virtual void printAttributes() { }

Modified: llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp?rev=265967&r1=265966&r2=265967&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp (original)
+++ llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp Mon Apr 11 12:15:30 2016
@@ -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"),
@@ -360,6 +365,8 @@ static void dumpObject(const ObjectFile
     }
     if (opts::SectionGroups)
       Dumper->printGroupSections();
+    if (opts::HashHistogram)
+      Dumper->printHashHistogram();
   }
   if (Obj->isCOFF()) {
     if (opts::COFFImports)




More information about the llvm-commits mailing list