From be5c892b3a92406b749d07a7653e1520eb452f8f Mon Sep 17 00:00:00 2001
From: Marcel Loose <loose@astron.nl>
Date: Fri, 14 Jan 2011 16:01:11 +0000
Subject: [PATCH] Bug 1592: Merged task branch into the trunk.

---
 LCS/Common/include/Common/AddressTranslator.h |  60 +++++++---
 LCS/Common/include/Common/Backtrace.h         |   4 +-
 LCS/Common/include/Common/SymbolTable.h       |  40 +++----
 LCS/Common/src/AddressTranslator.cc           | 103 +++++++++++++++---
 LCS/Common/src/Backtrace.cc                   |   4 +-
 LCS/Common/src/SymbolTable.cc                 |  38 +++----
 LCS/Common/test/tBacktrace.cc                 |   4 +-
 LCS/Common/test/tSymbolTable.cc               |  37 -------
 8 files changed, 166 insertions(+), 124 deletions(-)
 delete mode 100644 LCS/Common/test/tSymbolTable.cc

diff --git a/LCS/Common/include/Common/AddressTranslator.h b/LCS/Common/include/Common/AddressTranslator.h
index 32054dae0dc..ec2cc2d82ee 100644
--- a/LCS/Common/include/Common/AddressTranslator.h
+++ b/LCS/Common/include/Common/AddressTranslator.h
@@ -1,6 +1,6 @@
 //# AddressTranslator.h: Translate return addresses to function, file, line.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
@@ -23,20 +23,18 @@
 #ifndef LOFAR_COMMON_ADDRESSTRANSLATOR_H
 #define LOFAR_COMMON_ADDRESSTRANSLATOR_H
 
-//# Never #include <config.h> or #include <lofar_config.h> in a header file!
-
 // \file
 // Translate return addresses to function, file, line.
 
 //# Includes
 #include <Common/Backtrace.h>
-#include <vector>
 
 #ifndef HAVE_BFD
 # warning Binary file descriptor (bfd) package is missing, \
 please install the GNU binutils.
 #else
 # include <bfd.h>
+# include <link.h>
 # ifdef USE_THREADS
 #  include <pthread.h>
 # endif
@@ -46,12 +44,18 @@ namespace LOFAR
 {
   // \ingroup Common
   // @{
-
+  
   // Functor class for translating return addresses to function name,
   // filename, and line number. 
   //
   // \note The code is based on the utility addr2line.c in the GNU binutils
   // 2.16.
+  // \note For details on handling of shared libraries, please refer to
+  // - the Linux man page \c dl_iterate_phdr(3)
+  // - \c util/backtrace-symbols.c in cairo (http://cairographics.org/)
+  // - \c src/dwarf/Gfind_proc_info-lsb.c:callback() in libunwind
+  //   (http://www.nongnu.org/libunwind/)
+
   class AddressTranslator {
   public:
     AddressTranslator();
@@ -81,25 +85,45 @@ namespace LOFAR
       static pthread_mutex_t mutex;
     };
 # endif
-    // Helper function to "convert" the member function
-    // do_find_addresss_in_section() to a void(*), which can be passed as
-    // argument to bfd_map_over_sections().
+    // Helper function to pass the member function #do_find_matching_file() as
+    // argument to dl_iterate_phdr().
+    // \see The Linux man page <tt>dl_iterate_phdr(3)</tt>.
+    static int find_matching_file(dl_phdr_info*, size_t, void*);
+
+    // Look for the address #pc in the application's shared objects. If found,
+    // set the member variables #bfdFile and #base_addr.
+    int do_find_matching_file(dl_phdr_info*);
+
+    // Helper function to pass the member function
+    // #do_find_address_in_section() as argument to bfd_map_over_sections().
     // \see BFD documentation for details (<tt>info bfd</tt>).
     static void find_address_in_section(bfd*, asection*, void*);
 
-    // Find the source code line nearest to 
-    void do_find_address_in_section(bfd*, asection*);
+    // Look for the address #pc in the section \a section of the symbol table
+    // associated with the BFD \a abfd. If found, set the member variables
+    // #filename, #line, and #functionname.
+    void do_find_address_in_section(bfd* abfd, asection* section);
 
-    // Local variables used by translate_addresses() and
-    // do_find_address_in_section().
+    // @name Local variables set by operator()
     // @{
-    bfd_vma pc;
-    const char* filename;
-    const char* functionname;
-    unsigned int line;
-    bool found;
+    asymbol** syms;  ///< BFD symbol table information
+    bfd_vma pc;      ///< Virtual memory address
     // @}
-#endif
+
+    // @name Local variables set by do_find_matching_file()
+    // @{
+    const char* bfdFile;  ///< Filename of the matching shared object
+    bfd_vma base_addr;    ///< Base address of the shared object
+    // @}
+
+    // @name Local variables set by do_find_address_in_section()
+    // @{
+    const char* filename;      ///< Name of matching source file
+    const char* functionname;  ///< Name of matching function
+    unsigned int line;         ///< Line number in matching source file
+    bool found;                ///< Indicates whether a match was found
+    // @}
+#endif /* HAVE_BFD */
   };
 
   // @}
diff --git a/LCS/Common/include/Common/Backtrace.h b/LCS/Common/include/Common/Backtrace.h
index b953a6d6399..dc8e2e49992 100644
--- a/LCS/Common/include/Common/Backtrace.h
+++ b/LCS/Common/include/Common/Backtrace.h
@@ -1,6 +1,6 @@
 //# Backtrace.h: Store stack frame return addresses for self-debugging.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
@@ -30,7 +30,7 @@
 # error Backtrace is not supported.
 #endif
 
-// \def BACKTRACE_MAX_RETURN_ADDRESSES Maximum number of stack frame return
+// Maximum number of stack frame return
 // addresses that will be stored by the Backtrace class (default=50).
 #ifndef BACKTRACE_MAX_RETURN_ADDRESSES
 # define BACKTRACE_MAX_RETURN_ADDRESSES 50
diff --git a/LCS/Common/include/Common/SymbolTable.h b/LCS/Common/include/Common/SymbolTable.h
index a54ad8d6d37..fcf4c528e85 100644
--- a/LCS/Common/include/Common/SymbolTable.h
+++ b/LCS/Common/include/Common/SymbolTable.h
@@ -1,6 +1,6 @@
-//# SymbolTable.h: one line description
+//# SymbolTable.h: Class holding the symbol table of an object file.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
@@ -39,57 +39,51 @@ namespace LOFAR
   // \addtogroup Common
   // @{
 
-  // Class holding the symbol table of the current executable program. It is
-  // implemented as a Singleton. So, any overhead will only be paid when this
-  // class is instantiated, which will normally happen when the first
-  // Backtrace is printed (\e not when the first exception is thrown).
+  // Class holding the symbol table of an object file. 
   //
   // \note The code is based on the utility addr2line.c in GNU binutils 2.16.
   class SymbolTable {
 
   public:
+    // Default constructor. Doesn't read any symbol table into memory.
+    SymbolTable();
+
+    // Read the symbol table from \a filename into memory.
+    SymbolTable(const char* filename);
+
     // Destructor.
     ~SymbolTable();
 
-    // Return an instance of SymbolTable. When called for the first time
-    // an instance of SymbolTable is created. Subsequent calls return the
-    // previously created instance.
-    static SymbolTable& instance();
-
-    // Return a pointer to the symbol table
+    // Return a pointer to the symbol table.
     asymbol** getSyms() const
     { return itsSymbols; }
 
-    // Return a pointer to the bfd
+    // Return a pointer to the bfd.
     bfd* getBfd() const
     { return itsBfd; }
 
   private:
-    // Default constructor is called from the static member function
-    // SymbolTable::instance().
-    SymbolTable();
-
     // Disallow copy constructor.
     SymbolTable(const SymbolTable&);
 
     // Disallow assignment operator.
     SymbolTable& operator=(const SymbolTable&);
 
-    // Open the bfd-file and check its format.
+    // Open the object file \c filename and check its format.
     // @return True on success.
-    bool init();
+    bool init(const char* filename);
 
-    // Read the symbol table from the bfd-file.
+    // Read the symbol table from the object file.
     // @return True when read was successful.
     bool read();
 
-    // Free dynamically allocated memory; close bfd-file.
+    // Free dynamically allocated memory; close object file.
     void cleanup();
 
-    // Pointer to the bfd
+    // Pointer to the binary file descriptor.
     bfd* itsBfd;
 
-    // Pointer to the symbol table.
+    // Pointer to the internal representation of the symbol table.
     asymbol** itsSymbols;
   };
 
diff --git a/LCS/Common/src/AddressTranslator.cc b/LCS/Common/src/AddressTranslator.cc
index 41c197a61ad..81559d7255a 100644
--- a/LCS/Common/src/AddressTranslator.cc
+++ b/LCS/Common/src/AddressTranslator.cc
@@ -1,6 +1,6 @@
-//# AddressTranslator.cc: one line description
+//# AddressTranslator.cc:  Translate return addresses to function, file, line.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
@@ -27,16 +27,37 @@
 #include <Common/AddressTranslator.h>
 #include <Common/Backtrace.h>
 #include <Common/SymbolTable.h>
+#include <map>
 #include <cstdlib>
 #include <cstring>
+#include <boost/shared_ptr.hpp>
 
 #ifdef __GNUG__
 # include <cxxabi.h>
 #endif
 
+#include <link.h>
+
 namespace LOFAR
 {
 
+#ifdef HAVE_BFD
+  namespace
+  {
+    // Map of symbol tables.
+    // Use the load address of the shared object or executable as key.
+    typedef std::map< bfd_vma, boost::shared_ptr<SymbolTable> > SymbolTableMap;
+
+    // The map of symbol tables is implemented as a Meyers singleton.
+    // Use a lock to make access thread-safe.
+    SymbolTableMap& theSymbolTableMap()
+    {
+      static SymbolTableMap symTabMap;
+      return symTabMap;
+    }
+  }
+#endif
+
   AddressTranslator::AddressTranslator()
   {
   }
@@ -55,15 +76,40 @@ namespace LOFAR
 # ifdef USE_THREADS
     ScopedLock sc;
 # endif
-    bfd* abfd = SymbolTable::instance().getBfd();
-    if (!abfd) return;
-
     for (int i = 0; i < size; i++) {
+
       pc = reinterpret_cast<bfd_vma>(addr[i]);
+
+      // Walk through list of shared objects (ref. man dl_iterate_phdr(3)).
+      // The callback function #find_matching_file() will set #bfdFile and
+      // #base_addr.
+      dl_iterate_phdr(find_matching_file, this);
+
+      // Lookup the SymbolTable using #base_addr as key. Create a new entry
+      // if the symbol table of #bfdFile is not yet present in the map.
+      SymbolTableMap::iterator it = theSymbolTableMap().find(base_addr);
+      if(it == theSymbolTableMap().end()) {
+        it = theSymbolTableMap().insert
+          (std::make_pair(base_addr, new SymbolTable(bfdFile))).first;
+      }
+
+      // Get the BFD-handle of the matching SymbolTable object.
+      bfd* abfd = it->second->getBfd();
+      if (!abfd) continue;
+
+      // Get the handle to the symbols of the matching SymbolTable.
+      if (!syms) continue;
+      syms = it->second->getSyms();
+
+      // Calculate offset address inside the matching shared object.
+      pc -= base_addr;
       found = false;
 
+      // Call the function #find_address_in_section() for each section
+      // attached to the BFD \a abfd. This function will set #filename,
+      // #functionname, #line and #found.
       bfd_map_over_sections(abfd, find_address_in_section, this);
-    
+
       if (found) {
         if (functionname && *functionname) {
 # ifdef __GNUG__
@@ -88,8 +134,6 @@ namespace LOFAR
         }
         trace[i].line = line;
       }
-      //     if (trace[i].function == "main")
-      //       break;
     }
 #else
     (void) addr;  // suppress `unused parameter' warning
@@ -97,7 +141,6 @@ namespace LOFAR
     return;
   }
 
-
   //##----  P r i v a t e   f u n c t i o n s  ----##//
 
 #ifdef HAVE_BFD
@@ -107,6 +150,38 @@ namespace LOFAR
   AddressTranslator::ScopedLock::mutex = PTHREAD_MUTEX_INITIALIZER;
 # endif
 
+  int AddressTranslator::find_matching_file(dl_phdr_info* info,
+                                            size_t        size,
+                                            void*         data)
+  {
+    AddressTranslator* obj = static_cast<AddressTranslator*>(data);
+    return obj->do_find_matching_file(info);
+  }
+
+  int AddressTranslator::do_find_matching_file(dl_phdr_info* info)
+  {
+    // This code is based on util/backtrace-symbols.c from Cairo
+    // (http://cairographics.org).
+    const ElfW(Phdr) *phdr = info->dlpi_phdr;
+    for (int n = info->dlpi_phnum; --n >= 0; phdr++) {
+      if (phdr->p_type == PT_LOAD) {
+        ElfW(Addr) vaddr = phdr->p_vaddr + info->dlpi_addr;
+        if (pc >= vaddr && pc < vaddr + phdr->p_memsz) {
+          // We found a match
+          if(info->dlpi_name && *info->dlpi_name) {
+            bfdFile = info->dlpi_name;
+          } else {
+            bfdFile = "/proc/self/exe";
+          }
+          base_addr = info->dlpi_addr;
+          return 1;
+        }
+      }
+    }
+    return 0;
+  }
+    
+
   void AddressTranslator::find_address_in_section(bfd*      abfd,
 						  asection* section,
 						  void*     data)
@@ -120,18 +195,14 @@ namespace LOFAR
   {
     if (found)
       return;
-      
-    asymbol** syms = SymbolTable::instance().getSyms();
-    if (!syms)
-      return;
 
     if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0)
       return;
-      
+
     bfd_vma vma = bfd_get_section_vma (abfd, section);
     if (pc < vma)
       return;
-      
+
     bfd_size_type size = bfd_get_section_size (section);
     if (pc >= vma + size)
       return;
@@ -139,7 +210,7 @@ namespace LOFAR
     found = bfd_find_nearest_line (abfd, section, syms, pc - vma, 
 				   &filename, &functionname, &line);
   }
-    
+
 #endif
 
 } // namespace LOFAR
diff --git a/LCS/Common/src/Backtrace.cc b/LCS/Common/src/Backtrace.cc
index a0e369d1fd2..581c2548e48 100644
--- a/LCS/Common/src/Backtrace.cc
+++ b/LCS/Common/src/Backtrace.cc
@@ -1,6 +1,6 @@
-//# Backtrace.cc: one line description
+//# Backtrace.cc: Store stack frame return addresses for self-debugging.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
diff --git a/LCS/Common/src/SymbolTable.cc b/LCS/Common/src/SymbolTable.cc
index b8f2f877eff..f4b345f71ff 100644
--- a/LCS/Common/src/SymbolTable.cc
+++ b/LCS/Common/src/SymbolTable.cc
@@ -1,6 +1,6 @@
-//# SymbolTable.cc: one line description
+//# SymbolTable.cc: Class holding the symbol table of an object file.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
@@ -32,46 +32,36 @@
 namespace LOFAR
 {
 
-#if defined(__linux__)
-  static const char* bfdFile = "/proc/self/exe";
-#elif defined(__sun__)
-  static const char* bfdFile = "/proc/self/object/a.out";
-#else
-# error "Alias for process's executable file, like /proc/self/exe \
-on linux, must be present."
-#endif
-
-
   SymbolTable::SymbolTable() :
     itsBfd(0),
     itsSymbols(0)
   {
-    init() && read();
   }
 
 
-  SymbolTable::~SymbolTable()
+  SymbolTable::SymbolTable(const char* filename) :
+    itsBfd(0),
+    itsSymbols(0)
   {
-    cleanup();
+    init(filename) && read();
   }
 
 
-  SymbolTable& SymbolTable::instance()
+  SymbolTable::~SymbolTable()
   {
-    static SymbolTable symTab;
-    return symTab;
+    cleanup();
   }
 
 
-  bool SymbolTable::init()
+  bool SymbolTable::init(const char* filename)
   {
     bfd_init();
-    if ((itsBfd = bfd_openr(bfdFile,0)) == 0) {
-      bfd_perror(bfdFile);
+    if ((itsBfd = bfd_openr(filename,0)) == 0) {
+      bfd_perror(filename);
       return false;
     }
     if (!bfd_check_format(itsBfd, bfd_object)) {
-      bfd_perror(bfdFile);
+      bfd_perror(filename);
       return false;
     }
     return true;
@@ -81,7 +71,7 @@ on linux, must be present."
   bool SymbolTable::read()
   {
     if ((bfd_get_file_flags(itsBfd) & HAS_SYMS) == 0) {
-      bfd_perror(bfdFile);
+      bfd_perror(itsBfd->filename);
       return true;
     }
     unsigned int size;
@@ -98,7 +88,7 @@ on linux, must be present."
       symcount = bfd_read_minisymbols(itsBfd, true, symbolUnion.dest, &size);
     }
     if (symcount < 0) {
-      bfd_perror(bfdFile);
+      bfd_perror(itsBfd->filename);
       return false;
     }
     return true;
diff --git a/LCS/Common/test/tBacktrace.cc b/LCS/Common/test/tBacktrace.cc
index 7699761e9bd..2633d68b027 100644
--- a/LCS/Common/test/tBacktrace.cc
+++ b/LCS/Common/test/tBacktrace.cc
@@ -1,6 +1,6 @@
-//# tBacktrace.cc: one line description
+//# tBacktrace.cc: Unit test program for the Backtrace/Exception classes.
 //#
-//# Copyright (C) 2002-2008
+//# Copyright (C) 2002-2011
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 //#
diff --git a/LCS/Common/test/tSymbolTable.cc b/LCS/Common/test/tSymbolTable.cc
deleted file mode 100644
index 190ca216751..00000000000
--- a/LCS/Common/test/tSymbolTable.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-//# tSymbolTable.cc: one line description
-//#
-//# Copyright (C) 2002-2008
-//# ASTRON (Netherlands Institute for Radio Astronomy)
-//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
-//#
-//# This file is part of the LOFAR software suite.
-//# The LOFAR software suite is free software: you can redistribute it and/or
-//# modify it under the terms of the GNU General Public License as published
-//# by the Free Software Foundation, either version 3 of the License, or
-//# (at your option) any later version.
-//#
-//# The LOFAR software suite is distributed in the hope that it will be useful,
-//# but WITHOUT ANY WARRANTY; without even the implied warranty of
-//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//# GNU General Public License for more details.
-//#
-//# You should have received a copy of the GNU General Public License along
-//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
-//#
-//# $Id$
-
-//# Always #include <lofar_config.h> first!
-#include <lofar_config.h>
-
-//# Includes
-#include <Common/SymbolTable.h>
-#include <Common/LofarLogger.h>
-
-using namespace LOFAR;
-
-int main()
-{
-  INIT_LOGGER("tSymbolTable");
-  SymbolTable::instance();
-  return 0;
-}
-- 
GitLab