This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add C++ wrappers for iterating through various libdw and libdwfl objects


Hello,

these iterators originated a couple years back on the dwarflint branch.
Back then Roland added some C++ wrappers and we started writing stuff in
C++.  I've been lugging them around ever since, first to dwlocstat,
which was peeled off dwarflint (and should be coming for review after
this bunch), and then to dwgrep.  They are header-only library.

They are not completely zero-overhead, as every "++" involves a call to
the underlying C-level interface.  But they are tremendously handy and
allow one to express algorithms in a native C++ way, which is why I've
been using them.

Error conditions are communicated through exceptions.  For better or
worse, that's how things are done in C++, and there's simply no way to
communicate failures out of constructors or the likes of operator++.
Currently I'm simply throwing an std::runtime_error, but maybe it would
make sense to have a custom family of libelf, libdw, libdwfl exceptions,
all rooted in a general elfutils exception type.

One non-iterator-like concept that I included as well is a wrapper
around dwfl_report.  It's an RAII class that ends the reporting when it
goes out of scope.  As soon as one calls dwfl_report_begin, they assume
responsibility to call dwfl_report_end eventually.  But in C++ many
error conditions are communicated through exceptions, and making sure
these cleanups are not skipped is messy.  RAII as a way to express these
responsibilities in C++.

It's all C++98.  We have had "the new C++" out there on the streets
since 2009 or so, and the iterators presented here were all written with
varying degrees of C++11 in them.  But that is still sufficiently new
that I felt adhering to the C++98 subset is the prudent way forward, and
backported the code.

The headers are installed unconditionally, but a test for availability
of a C++ compiler is made anyway, and build of the corresponding test is
skipped if there is none.  The testing is rather light, including only a
couple cases that I wanted to make sure about as I reviewed the code.
Dwgrep has a fairly comprehensive test suite that covers proper function
of these iterators, if that's any consolation.

Let me know what you think.

Thanks,
Petr

--8<-----------------------------------------------------------------
Signed-off-by: Petr Machata <pmachata@redhat.com>
---
 configure.ac                |  17 +-
 libdw/ChangeLog             |   6 +
 libdw/Makefile.am           |   5 +-
 libdw/c++/libdw             | 600 ++++++++++++++++++++++++++++++++++++++++++++
 libdwfl/ChangeLog           |   6 +
 libdwfl/Makefile.am         |   5 +-
 libdwfl/c++/libdwfl         | 227 +++++++++++++++++
 tests/Makefile.am           |   7 +
 tests/run-test-iterators.sh |  29 +++
 tests/test-iterators.cc     |  50 ++++
 10 files changed, 949 insertions(+), 3 deletions(-)
 create mode 100644 libdw/c++/libdw
 create mode 100644 libdwfl/c++/libdwfl
 create mode 100755 tests/run-test-iterators.sh
 create mode 100644 tests/test-iterators.cc

diff --git a/configure.ac b/configure.ac
index 0e67a79..296584c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl Configure input file for elfutils.                     -*-autoconf-*-
 dnl
-dnl Copyright (C) 1996-2014 Red Hat, Inc.
+dnl Copyright (C) 1996-2015 Red Hat, Inc.
 dnl
 dnl This file is part of elfutils.
 dnl
@@ -71,6 +71,7 @@ AS_IF([test "$use_locks" = yes],
 AH_TEMPLATE([USE_LOCKS], [Defined if libraries should be thread-safe.])
 
 AC_PROG_CC
+AC_PROG_CXX
 AC_PROG_RANLIB
 AC_PROG_YACC
 AM_PROG_LEX
@@ -89,6 +90,20 @@ CFLAGS="$old_CFLAGS"])
 AS_IF([test "x$ac_cv_c99" != xyes],
       AC_MSG_ERROR([gcc with C99 support required]))
 
+# Above, we check for C++ compiler.  However even if none is available,
+# CXX is set to a non-empty value.  So we need to figure out whether a
+# C++ compiler is actually available.
+AC_CACHE_CHECK([for working C++ compiler], ac_cv_cxx_works,
+[AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([dnl
+struct foo {
+  int i;
+  explicit foo () : i (0) {}
+  virtual ~foo () {}
+};])], ac_cv_cxx_works=yes, ac_cv_cxx_works=no)
+ AC_LANG_POP()])
+AM_CONDITIONAL(HAVE_CXX, test "$ac_cv_cxx_works" = yes)
+
 AC_CACHE_CHECK([for __thread support], ac_cv_tls, [dnl
 # Use the same flags that we use for our DSOs, so the test is representative.
 # Some old compiler/linker/libc combinations fail some ways and not others.
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 59fda1a..9c996f0 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,9 @@
+2015-03-17  Petr Machata  <pmachata@redhat.com>
+
+	* c++: New directory.
+	* c++/libdw: New header file.
+	* Makefile.am: Install it.
+
 2015-02-09  Mark Wielaard  <mjw@redhat.com>
 
 	* dwarf.h: Add DW_LANG_Fortran03 and DW_LANG_Fortran08.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 887da6b..771db9e 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2002-2010, 2012, 2014 Red Hat, Inc.
+## Copyright (C) 2002-2010, 2012, 2014, 2015 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -41,6 +41,9 @@ noinst_PROGRAMS = $(noinst_LIBRARIES:_pic.a=.so)
 include_HEADERS = dwarf.h
 pkginclude_HEADERS = libdw.h
 
+cppdir = $(pkgincludedir)/c++
+cpp_HEADERS = c++/libdw
+
 libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_getpubnames.c dwarf_getabbrev.c dwarf_tag.c \
 		  dwarf_error.c dwarf_nextcu.c dwarf_diename.c dwarf_offdie.c \
diff --git a/libdw/c++/libdw b/libdw/c++/libdw
new file mode 100644
index 0000000..f884d2e
--- /dev/null
+++ b/libdw/c++/libdw
@@ -0,0 +1,600 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils 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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBDW_CPP
+#define _LIBDW_CPP	1
+
+#include <vector>
+#include <cassert>
+#include <algorithm>
+#include <stdexcept>
+
+#include <elfutils/libdw.h>
+#include <dwarf.h>
+
+namespace elfutils
+{
+  // Helper functions.  Not for client consumption.
+  namespace libdw_impl
+  {
+    inline void throw_libdw (int dwerr = 0);
+    inline bool dwpp_child (Dwarf_Die &die, Dwarf_Die &result);
+    inline bool dwpp_siblingof (Dwarf_Die &die, Dwarf_Die &result);
+    inline Dwarf_Die dwpp_offdie (Dwarf *dbg, Dwarf_Off offset);
+  }
+
+  // The iterators presented here have operators * and -> as non-const
+  // member functions.  The reason is that the libdw interfaces do not
+  // take, say, Dwarf_Die const *, but Dwarf_Die *, and therefore
+  // returning a const reference of some sort would not be useful.  We
+  // don't want to give out copies either, as that adds unnecessary
+  // overhead.  And we simply don't care much if anyone does end up
+  // changing the internal copy of the current CU, DIE, or whatever.
+
+
+  // An iterator that goes over compile units (full or partial) in a
+  // given DWARF file.  The type that it points to (yields on
+  // dereference) is CU_INFO (see below).
+  //
+  // Example usage:
+  // {
+  //   std::vector <Dwarf_Off> v
+  //   for (elfutils::cu_iterator jt (dw);
+  //        jt != elfutils::cu_iterator::end (); ++jt)
+  //     v.push_back (dwarf_dieoffset (&jt->cudie));
+  // }
+  class cu_iterator;
+
+  // Helper structure with data about each compile unit.
+  struct cu_info
+  {
+    Dwarf_Die cudie;
+    Dwarf_Off abbrev_offset;
+    uint8_t address_size;
+    uint8_t offset_size;
+  };
+
+  class cu_iterator
+    : public std::iterator <std::input_iterator_tag, cu_info>
+  {
+    friend class die_tree_iterator;
+    Dwarf *m_dw;
+    Dwarf_Off m_offset;
+    Dwarf_Off m_old_offset;
+    cu_info m_info;
+
+    explicit cu_iterator (Dwarf_Off off)
+      : m_dw (NULL)
+      , m_offset (off)
+      , m_old_offset (0)
+    {}
+
+    void
+    move ()
+    {
+      assert (*this != end ());
+      m_old_offset = m_offset;
+      size_t hsize;
+      if (dwarf_nextcu (m_dw, m_offset, &m_offset, &hsize,
+			&m_info.abbrev_offset,
+			&m_info.address_size, &m_info.offset_size) != 0)
+	done ();
+      else
+	m_info.cudie
+	  = elfutils::libdw_impl::dwpp_offdie (m_dw, m_old_offset + hsize);
+    }
+
+    void
+    done ()
+    {
+      *this = end ();
+    }
+
+  public:
+    explicit cu_iterator (Dwarf *dw)
+      : m_dw (dw)
+      , m_offset (0)
+      , m_old_offset (0)
+    {
+      move ();
+    }
+
+    // Construct a CU iterator for DW such that it points to a compile
+    // unit represented by CUDIE.
+    cu_iterator (Dwarf *dw, Dwarf_Die cudie)
+      : m_dw (dw)
+      , m_offset (dwarf_dieoffset (&cudie) - dwarf_cuoffset (&cudie))
+      , m_old_offset (0)
+    {
+      move ();
+    }
+
+    cu_iterator (cu_iterator const &that)
+      : m_dw (that.m_dw)
+      , m_offset (that.m_offset)
+      , m_old_offset (that.m_old_offset)
+      , m_info (that.m_info)
+    {}
+
+    // Return a cu_iterator pointing one after the last actual CU.
+    static cu_iterator
+    end ()
+    {
+      return cu_iterator ((Dwarf_Off) -1);
+    }
+
+    bool
+    operator== (cu_iterator const &that) const
+    {
+      return m_offset == that.m_offset;
+    }
+
+    bool
+    operator!= (cu_iterator const &that) const
+    {
+      return ! (*this == that);
+    }
+
+    cu_iterator
+    operator++ ()
+    {
+      move ();
+      return *this;
+    }
+
+    cu_iterator
+    operator++ (int)
+    {
+      cu_iterator tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    Dwarf_Off
+    offset () const
+    {
+      return m_old_offset;
+    }
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    cu_info &
+    operator* ()
+    {
+      return m_info;
+    }
+
+    cu_info *
+    operator-> ()
+    {
+      return &**this;
+    }
+  };
+
+  // An iterator that goes through children of a given DIE.
+  // Example usage:
+  // {
+  //    size_t nchildren = std::distance (elfutils::child_iterator (type_die),
+  //                                      elfutils::child_iterator::end ());
+  // }
+
+  class child_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Die>
+  {
+    Dwarf_Die m_die;
+
+    child_iterator ()
+      : m_die ({.addr = (void *) -1})
+    {}
+
+  public:
+    explicit child_iterator (Dwarf_Die parent)
+    {
+      if (! elfutils::libdw_impl::dwpp_child (parent, m_die))
+	*this = end ();
+    }
+
+    child_iterator (child_iterator const &that)
+      : m_die (that.m_die)
+    {}
+
+    // Return a child_iterator pointing one after the last actual
+    // child.
+    static child_iterator
+    end ()
+    {
+      return child_iterator ();
+    }
+
+    bool
+    operator== (child_iterator const &that) const
+    {
+      return m_die.addr == that.m_die.addr;
+    }
+
+    bool
+    operator!= (child_iterator const &that) const
+    {
+      return ! (*this == that);
+    }
+
+    child_iterator
+    operator++ ()
+    {
+      assert (*this != end ());
+      if (! elfutils::libdw_impl::dwpp_siblingof (m_die, m_die))
+	*this = end ();
+      return *this;
+    }
+
+    child_iterator
+    operator++ (int)
+    {
+      child_iterator ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Die &
+    operator* ()
+    {
+      assert (*this != end ());
+      return m_die;
+    }
+
+    Dwarf_Die *
+    operator-> ()
+    {
+      return &**this;
+    }
+  };
+
+  // Tree flattening iterator.  It pre-order iterates all DIEs in
+  // given DWARF file, optionally starting from a given CU iterator.
+  // It keeps track of path from CU root to the current DIE, and that
+  // can be requested through stack() member function.
+  //
+  // Example usage:
+  // {
+  //   for (elfutils::die_tree_iterator it (dw);
+  //        it != elfutils::die_tree_iterator::end (); ++it)
+  //     {
+  //       typedef elfutils::die_tree_iterator::stack_type stack_type;
+  //       stack_type const &stack = it.stack ();
+  //       for (stack_type::const_iterator jt = stack.begin ();
+  //            jt != stack.end (); ++jt)
+  //         ...;
+  //     }
+  // }
+  class die_tree_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Die>
+  {
+  public:
+    typedef std::vector <Dwarf_Die> stack_type;
+
+  private:
+    cu_iterator m_cuit;
+    // Internally, only offsets are kept on the stack.
+    std::vector <Dwarf_Off> m_stack;
+    Dwarf_Die m_die;
+
+    die_tree_iterator (Dwarf_Off offset)
+      : m_cuit (cu_iterator::end ())
+    {}
+
+  public:
+    explicit die_tree_iterator (Dwarf *dw)
+      : die_tree_iterator (cu_iterator (dw))
+    {}
+
+    explicit die_tree_iterator (cu_iterator const &cuit)
+      : m_cuit (cuit)
+      , m_die (m_cuit->cudie)
+    {}
+
+    die_tree_iterator (die_tree_iterator const &that)
+      : m_cuit (that.m_cuit)
+      , m_stack (that.m_stack)
+      , m_die (that.m_die)
+    {}
+
+    // Return a die_tree_iterator pointing one after the last actual
+    // DIE.
+    static die_tree_iterator
+    end ()
+    {
+      return die_tree_iterator ((Dwarf_Off) -1);
+    }
+
+    bool
+    operator== (die_tree_iterator const &that) const
+    {
+      return m_cuit == that.m_cuit
+	&& m_stack == that.m_stack
+	&& (m_cuit == cu_iterator::end ()
+	    || m_die.addr == that.m_die.addr);
+    }
+
+    bool
+    operator!= (die_tree_iterator const &that) const
+    {
+      return ! (*this == that);
+    }
+
+    die_tree_iterator
+    operator++ ()
+    {
+      Dwarf_Die child;
+      if (elfutils::libdw_impl::dwpp_child (m_die, child))
+	{
+	  m_stack.push_back (dwarf_dieoffset (&m_die));
+	  m_die = child;
+	  return *this;
+	}
+
+      do
+	if (elfutils::libdw_impl::dwpp_siblingof (m_die, m_die))
+	  return *this;
+	else
+	  // No sibling found.  Go a level up and retry, unless this
+	  // was a sole, childless CU DIE.
+	  if (! m_stack.empty ())
+	    {
+	      m_die = elfutils::libdw_impl::dwpp_offdie (m_cuit.m_dw,
+							 m_stack.back ());
+	      m_stack.pop_back ();
+	    }
+      while (! m_stack.empty ());
+
+      m_die = (++m_cuit)->cudie;
+      return *this;
+    }
+
+    die_tree_iterator
+    operator++ (int)
+    {
+      die_tree_iterator prev = *this;
+      ++*this;
+      return prev;
+    }
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Die &
+    operator* ()
+    {
+      return m_die;
+    }
+
+    Dwarf_Die *
+    operator-> ()
+    {
+      return &**this;
+    }
+
+    // Return a stack of DIE's representing the path from CU DIE to
+    // the current DIE (both ends inclusive).  The first element of
+    // the returned stack is the CU DIE, the last one the current DIE.
+    stack_type
+    stack () const
+    {
+      stack_type ret;
+      for (die_tree_iterator it = *this; it != end (); it = it.parent ())
+	ret.push_back (*it);
+      std::reverse (ret.begin (), ret.end ());
+      return ret;
+    }
+
+    // Return a die_tree_iterator pointing at a parent of a DIE that
+    // this iterator points at.  Returns an end iterator if there is
+    // no parent.
+    die_tree_iterator
+    parent () const
+    {
+      assert (*this != end ());
+      if (m_stack.empty ())
+	return end ();
+
+      die_tree_iterator ret = *this;
+      ret.m_die = elfutils::libdw_impl::dwpp_offdie (m_cuit.m_dw,
+						     m_stack.back ());
+      ret.m_stack.pop_back ();
+      return ret;
+    }
+
+    // Return a CU iterator representing a CU that the current DIE
+    // comes from.
+    cu_iterator
+    cu () const
+    {
+      return m_cuit;
+    }
+  };
+
+  // An attribute iterator goes through attributes of a given DIE.
+  class attr_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Attribute>
+  {
+    Dwarf_Die *m_die;
+    Dwarf_Attribute m_at;
+    ptrdiff_t m_offset;
+
+    struct cb_data
+    {
+      Dwarf_Attribute *at;
+      bool been;
+    };
+
+    static int
+    callback (Dwarf_Attribute *at, void *data)
+    {
+      cb_data *d = static_cast <cb_data *> (data);
+      if (d->been)
+	return DWARF_CB_ABORT;
+
+      *d->at = *at;
+      d->been = true;
+
+      // Do a second iteration to find the next offset.
+      return DWARF_CB_OK;
+    }
+
+    void
+    move ()
+    {
+      // If m_offset is already 1, we are done iterating.
+      if (m_offset == 1)
+	{
+	  *this = end ();
+	  return;
+	}
+
+      cb_data data = {&m_at, false};
+      m_offset = dwarf_getattrs (m_die, &callback, &data, m_offset);
+      if (m_offset == -1)
+	elfutils::libdw_impl::throw_libdw ();
+    }
+
+    attr_iterator (ptrdiff_t offset)
+      : m_die (NULL)
+      , m_at ({0})
+      , m_offset (offset)
+    {}
+
+  public:
+    attr_iterator (Dwarf_Die *die)
+      : m_die (die)
+      , m_at ({0})
+      , m_offset (0)
+    {
+      move ();
+    }
+
+    bool
+    operator== (attr_iterator const &other) const
+    {
+      return m_offset == other.m_offset
+	&& m_at.code == other.m_at.code;
+    }
+
+    bool
+    operator!= (attr_iterator const &other) const
+    {
+      return ! (*this == other);
+    }
+
+    attr_iterator &
+    operator++ ()
+    {
+      assert (*this != end ());
+      move ();
+      return *this;
+    }
+
+    attr_iterator
+    operator++ (int)
+    {
+      attr_iterator tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Attribute &
+    operator* ()
+    {
+      return m_at;
+    }
+
+    Dwarf_Attribute *
+    operator-> ()
+    {
+      return &**this;
+    }
+
+    // Return an attr_iterator pointing one after the last actual
+    // attribute.
+    static attr_iterator
+    end ()
+    {
+      return attr_iterator ((ptrdiff_t) 1);
+    }
+  };
+}
+
+inline void
+elfutils::libdw_impl::throw_libdw (int dwerr)
+{
+  if (dwerr == 0)
+    dwerr = dwarf_errno ();
+  assert (dwerr != 0);
+  throw std::runtime_error (dwarf_errmsg (dwerr));
+}
+
+inline bool
+elfutils::libdw_impl::dwpp_child (Dwarf_Die &die, Dwarf_Die &result)
+{
+  int ret = dwarf_child (&die, &result);
+  if (ret < 0)
+    elfutils::libdw_impl::throw_libdw ();
+  return ret == 0;
+}
+
+inline bool
+elfutils::libdw_impl::dwpp_siblingof (Dwarf_Die &die, Dwarf_Die &result)
+{
+  switch (dwarf_siblingof (&die, &result))
+    {
+    case -1:
+      elfutils::libdw_impl::throw_libdw ();
+    case 0:
+      return true;
+    case 1:
+      return false;
+    default:
+      abort ();
+    }
+}
+
+inline Dwarf_Die
+elfutils::libdw_impl::dwpp_offdie (Dwarf *dbg, Dwarf_Off offset)
+{
+  Dwarf_Die result;
+  if (dwarf_offdie (dbg, offset, &result) == NULL)
+    throw_libdw ();
+  return result;
+}
+
+#endif
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index d40dbae..45772b1 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,9 @@
+2015-03-17  Petr Machata  <pmachata@redhat.com>
+
+	* c++: New directory.
+	* c++/libdwfl: New header file.
+	* Makefile.am: Install it.
+
 2015-01-26  Mark Wielaard  <mjw@redhat.com>
 
 	* dwfl_module_getdwarf.c (find_symtab): Explicitly clear symdata,
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index 72c980b..8d4a0f1 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -2,7 +2,7 @@
 ##
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+## Copyright (C) 2005-2010, 2013, 2015 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -39,6 +39,9 @@ noinst_LIBRARIES += libdwfl_pic.a
 
 pkginclude_HEADERS = libdwfl.h
 
+cppdir = $(pkgincludedir)/c++
+cpp_HEADERS = c++/libdwfl
+
 libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
 		    dwfl_module.c dwfl_report_elf.c relocate.c \
 		    dwfl_module_build_id.c dwfl_module_report_build_id.c \
diff --git a/libdwfl/c++/libdwfl b/libdwfl/c++/libdwfl
new file mode 100644
index 0000000..9243f14
--- /dev/null
+++ b/libdwfl/c++/libdwfl
@@ -0,0 +1,227 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils 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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBDWFL_CPP
+#define _LIBDWFL_CPP	1
+
+#include <stdexcept>
+#include <cassert>
+#include <string>
+#include <elfutils/libdwfl.h>
+
+namespace elfutils
+{
+  // Helper functions.  Not for client consumption.
+  namespace libdwfl_impl
+  {
+    inline void throw_libdwfl (int dwerr = 0);
+  }
+
+  // Given a Dwfl, iterates through its Dwfl_Module's.
+  //
+  // Example usage:
+  // std::vector <Dwfl_Module *> mods (elfutils::dwfl_module_iterator (dwfl),
+  //				       elfutils::dwfl_module_iterator::end ());
+
+  class dwfl_module_iterator
+    : public std::iterator <std::input_iterator_tag, Dwfl_Module *>
+  {
+    Dwfl *m_dwfl;
+    ptrdiff_t m_offset;
+    Dwfl_Module *m_module;
+
+    static int
+    module_cb (Dwfl_Module *mod, void **data, const char *name,
+	       Dwarf_Addr addr, void *arg)
+    {
+      elfutils::dwfl_module_iterator *self
+	= static_cast <elfutils::dwfl_module_iterator *> (arg);
+      self->m_module = mod;
+      return DWARF_CB_ABORT;
+    }
+
+    void
+    move ()
+    {
+      m_offset = dwfl_getmodules (m_dwfl, module_cb, this, m_offset);
+      if (m_offset == -1)
+	elfutils::libdwfl_impl::throw_libdwfl ();
+    }
+
+    explicit dwfl_module_iterator (ptrdiff_t off)
+      : m_dwfl (NULL)
+      , m_offset (off)
+    {}
+
+  public:
+    dwfl_module_iterator (Dwfl *dwfl)
+      : m_dwfl (dwfl)
+      , m_offset (0)
+    {
+      move ();
+    }
+
+    dwfl_module_iterator (const dwfl_module_iterator &that)
+      : m_dwfl (that.m_dwfl)
+      , m_offset (that.m_offset)
+      , m_module (that.m_module)
+    {}
+
+    static dwfl_module_iterator
+    end ()
+    {
+      return dwfl_module_iterator ((ptrdiff_t) 0);
+    }
+
+    dwfl_module_iterator &
+    operator++ ()
+    {
+      assert (*this != end ());
+      move ();
+      return *this;
+    }
+
+    dwfl_module_iterator
+    operator++ (int)
+    {
+      dwfl_module_iterator ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    Dwfl_Module &
+    operator* () const
+    {
+      return *m_module;
+    }
+
+    Dwfl_Module *
+    operator-> () const
+    {
+      return &**this;
+    }
+
+    bool
+    operator== (const dwfl_module_iterator &that) const
+    {
+      assert (m_dwfl == NULL || that.m_dwfl == NULL || m_dwfl == that.m_dwfl);
+      return m_offset == that.m_offset;
+    }
+
+    bool
+    operator!= (const dwfl_module_iterator &that) const
+    {
+      return !(*this == that);
+    }
+  };
+
+
+  // RAII-style wrapper around dwfl_report_* calls.
+  //
+  // Example usage:
+  // {
+  //   elfutils::dwfl_report r (dwfl);
+  //   r.report_offline (fn, fn, fd);
+  // }
+
+  class dwfl_report
+  {
+    Dwfl *m_dwfl;
+
+    Dwfl_Module *
+    reported (Dwfl_Module *mod)
+    {
+      if (mod == NULL)
+	elfutils::libdwfl_impl::throw_libdwfl ();
+      return mod;
+    }
+
+  public:
+    explicit dwfl_report (Dwfl *dwfl)
+      : m_dwfl ((assert (dwfl != NULL), dwfl))
+    {
+      dwfl_report_begin_add (m_dwfl);
+    }
+
+    ~dwfl_report ()
+    {
+      dwfl_report_end (m_dwfl, NULL, NULL);
+    }
+
+    Dwfl_Module *
+    report_module (const char *name, Dwarf_Addr start, Dwarf_Addr end)
+    {
+      return reported (dwfl_report_module (m_dwfl, name, start, end));
+    }
+
+    Dwfl_Module *
+    report_module (const std::string &name, Dwarf_Addr start, Dwarf_Addr end)
+    {
+      return report_module (name.c_str (), start, end);
+    }
+
+    Dwfl_Module *
+    report_elf (const char *name, const char *file_name, int fd,
+		GElf_Addr base, bool add_p_vaddr)
+    {
+      return reported (dwfl_report_elf (m_dwfl, name, file_name, fd,
+					base, add_p_vaddr));
+    }
+
+    Dwfl_Module *
+    report_elf (const std::string &name, const std::string &file_name, int fd,
+		GElf_Addr base, bool add_p_vaddr)
+    {
+      return report_elf (name.c_str (), file_name.c_str (), fd,
+			 base, add_p_vaddr);
+    }
+
+    Dwfl_Module *
+    report_offline (const char *name, const char *file_name, int fd)
+    {
+      return reported (dwfl_report_offline (m_dwfl, name, file_name, fd));
+    }
+
+    Dwfl_Module *
+    report_offline (const std::string &name, const std::string &file_name, int fd)
+    {
+      return report_offline (name.c_str (), file_name.c_str (), fd);
+    }
+  };
+}
+
+inline void
+elfutils::libdwfl_impl::throw_libdwfl (int dwerr)
+{
+  if (dwerr == 0)
+    dwerr = dwfl_errno ();
+  assert (dwerr != 0);
+  throw std::runtime_error (dwfl_errmsg (dwerr));
+}
+
+#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b03f62e..514b8a2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -141,6 +141,11 @@ check_PROGRAMS += $(asm_TESTS)
 TESTS += $(asm_TESTS)
 endif
 
+if HAVE_CXX
+check_PROGRAMS += test-iterators
+TESTS += run-test-iterators.sh
+endif
+
 EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     run-show-die-info.sh run-get-files.sh run-get-lines.sh \
 	     run-get-pubnames.sh run-get-aranges.sh \
@@ -429,6 +434,8 @@ getsrc_die_LDADD = $(libdw) $(libelf)
 strptr_LDADD = $(libelf)
 newdata_LDADD = $(libelf)
 elfstrtab_LDADD = $(libelf)
+test_iterators_SOURCES = test-iterators.cc
+test_iterators_LDADD = $(libdw) $(libelf)
 
 if GCOV
 check: check-am coverage
diff --git a/tests/run-test-iterators.sh b/tests/run-test-iterators.sh
new file mode 100755
index 0000000..983fbd9
--- /dev/null
+++ b/tests/run-test-iterators.sh
@@ -0,0 +1,29 @@
+#! /bin/sh
+# Copyright (C) 2015 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file 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.
+#
+# elfutils 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile39
+
+testrun_compare ${abs_top_builddir}/tests/test-iterators testfile39 <<\EOF
+0
+0x93
+0x12a
+0x1bd
+EOF
+
+exit 0
diff --git a/tests/test-iterators.cc b/tests/test-iterators.cc
new file mode 100644
index 0000000..732ea78
--- /dev/null
+++ b/tests/test-iterators.cc
@@ -0,0 +1,50 @@
+/* Copyright (C) 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file 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.
+
+   elfutils 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <iostream>
+#include <cassert>
+
+#include <elfutils/c++/libdw>
+
+int
+main (int argc, char *argv[])
+{
+  int fd = open (argv[1], O_RDONLY);
+  if (fd < 0)
+    throw std::runtime_error (strerror (errno));
+
+  Dwarf *dw = dwarf_begin (fd, DWARF_C_READ);
+  std::vector <std::pair <elfutils::cu_iterator, Dwarf_Die> > cudies;
+  for (elfutils::cu_iterator it (dw); it != elfutils::cu_iterator::end (); ++it)
+    cudies.push_back (std::make_pair (it, it->cudie));
+
+  for (size_t i = 0; i < cudies.size (); ++i)
+    {
+      elfutils::cu_iterator jt (dw, cudies[i].second);
+      std::cerr << std::hex << std::showbase << jt.offset () << std::endl;
+      size_t j = i;
+      for (; jt != elfutils::cu_iterator::end (); ++jt, ++j)
+	assert (jt == cudies[j].first);
+    }
+
+  assert (elfutils::die_tree_iterator (elfutils::cu_iterator::end ())
+	  == elfutils::die_tree_iterator::end ());
+}
-- 
2.1.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]