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 <[email protected]> --- 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 <[email protected]> + + * c++: New directory. + * c++/libdw: New header file. + * Makefile.am: Install it. + 2015-02-09 Mark Wielaard <[email protected]> * 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 <[email protected]> + + * c++: New directory. + * c++/libdwfl: New header file. + * Makefile.am: Install it. + 2015-01-26 Mark Wielaard <[email protected]> * 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
