From: Petr Machata Date: Thu, 24 Mar 2011 16:44:12 +0000 (+0100) Subject: dwarflint: Add check_die_tree pass for generic per-DIE checks X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cb50be35ea31a28a98640b33e1629138749ffd83;p=thirdparty%2Felfutils.git dwarflint: Add check_die_tree pass for generic per-DIE checks - add reg_die_check for registering per-DIE check passes - turn check_registrar into a template to allow reuse among reg<> and reg_die_check<> - the per-DIE checking magic is in check_die_tree.hh - hide most of checks.hh into dwarflint.cc. That has little to do with per-DIE checks as such, but was a necessary cleanup --- diff --git a/dwarflint/Makefile.am b/dwarflint/Makefile.am index 2247d0e94..b4164ddf0 100644 --- a/dwarflint/Makefile.am +++ b/dwarflint/Makefile.am @@ -41,10 +41,11 @@ noinst_PROGRAMS = tests/test-coverage tests/test-wrap tests/test-all-dies-it dwarflint_SOURCES = \ addr-record.cc addr-record.hh \ all-dies-it.hh \ + check_registrar.cc check_registrar.hh check_registrar.ii \ checkdescriptor.cc checkdescriptor.hh checkdescriptor.ii \ checked_read.cc checked_read.hh \ checkrule.cc checkrule.hh \ - checks.cc checks.hh checks.ii \ + checks.hh checks.ii \ coverage.cc coverage.hh \ cu_coverage.cc cu_coverage.hh cu_coverage.ii \ dwarf_2.cc dwarf_2.hh \ @@ -76,6 +77,7 @@ dwarflint_SOURCES = \ check_debug_line.cc check_debug_line.hh check_debug_line.ii \ check_debug_loc_range.cc check_debug_loc_range.hh check_debug_loc_range.ii \ check_debug_pub.cc check_debug_pub.hh \ + check_die_tree.cc check_die_tree.hh check_die_tree.ii \ check_duplicate_DW_tag_variable.cc \ check_dups_abstract_origin.cc \ check_expected_trees.cc \ diff --git a/dwarflint/all-dies-it.hh b/dwarflint/all-dies-it.hh index 7bcc36f2b..7af7316fe 100644 --- a/dwarflint/all-dies-it.hh +++ b/dwarflint/all-dies-it.hh @@ -23,6 +23,10 @@ Network licensing program, please visit www.openinventionnetwork.com . */ +#ifdef HAVE_CONFIG_H +# include +#endif + #include #include diff --git a/dwarflint/check_die_tree.cc b/dwarflint/check_die_tree.cc new file mode 100644 index 000000000..e37fb8934 --- /dev/null +++ b/dwarflint/check_die_tree.cc @@ -0,0 +1,89 @@ +/* Pedantic checking of DWARF files + Copyright (C) 2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include "messages.hh" +#include "highlevel_check.hh" +#include "check_die_tree.hh" + +using namespace elfutils; + +namespace +{ + reg reg; +} + +class die_check_context + : protected std::vector +{ + typedef std::vector _super_t; + +public: + die_check_context (checkdescriptor const *cd, dwarflint &lint, + die_check_registrar const ®istrar) + { + // For per-DIE runs, we are only interested in limited context: + // the main iteration check, and the per-DIE check. This should + // be enough to decide whether to run the per-DIE check or not. + // We cannot use the original stack as a criterion, because the + // original check that tricked us into running is here, and the + // logic in should_check would then assume that we need to run + // everything. + checkstack stack; + stack.push_back (cd); + + for (die_check_registrar::const_iterator it = registrar.begin (); + it != registrar.end (); ++it) + { + stack.push_back ((*it)->descriptor ()); + popper p (stack); + if (lint.rules ().should_check (stack)) + push_back ((*it)->create (stack, lint)); + } + } + + void + die (all_dies_iterator const &a_d_it) + { + for (iterator it = begin (); it != end (); ++it) + (*it)->die (a_d_it); + } + + ~die_check_context () + { + for (iterator it = begin (); it != end (); ++it) + delete *it; + } +}; + +check_die_tree::check_die_tree (checkstack &stack, dwarflint &lint) + : highlevel_check (stack, lint) +{ + //std::cout << "check_die_tree" << std::endl; + die_check_context ctx (descriptor (), lint, *dwarflint::die_registrar ()); + + for (all_dies_iterator it = all_dies_iterator (dw); + it != all_dies_iterator (); ++it) + ctx.die (it); +} diff --git a/dwarflint/check_die_tree.hh b/dwarflint/check_die_tree.hh new file mode 100644 index 000000000..cd0a14ed3 --- /dev/null +++ b/dwarflint/check_die_tree.hh @@ -0,0 +1,111 @@ +/* Pedantic checking of DWARF files + Copyright (C) 2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifndef _CHECK_DIE_TREE_H_ +#define _CHECK_DIE_TREE_H_ + +#include "all-dies-it.hh" +#include "highlevel_check.hh" +#include "check_die_tree.ii" + +#include + +/// Top-level check that iterates over all DIEs in a file and +/// dispatches per-DIE checks on each one. Per-DIE checks are written +/// as subclasses of die_check (see below) and registered using +/// reg_die_check (see further below). +class check_die_tree + : public highlevel_check +{ +public: + static checkdescriptor const *descriptor () + { + static checkdescriptor cd + (checkdescriptor::create ("check_die_tree") + .inherit > () + .hidden () + .description ("A pass over the DIE tree that dispatches to various per-DIE checks.\n")); + return &cd; + } + + check_die_tree (checkstack &stack, dwarflint &lint); +}; + +class die_check +{ +public: + virtual ~die_check () {} + virtual void die (all_dies_iterator const &it) = 0; +}; + +template +struct reg_die_check + : public die_check_item +{ + reg_die_check () + { + dwarflint::die_registrar ()->push_back (this); + } + + virtual die_check *create (checkstack &stack, dwarflint &lint) + { + return new T (stack, lint); + } + + virtual checkdescriptor const *descriptor () const + { + return T::descriptor (); + } + +private: + /// The top-level scheduler needs to see per-DIE checks as real + /// checks, which they are not. So the per-DIE registrar creates + /// this check stub that's here only to trick the check_die_tree to + /// run. check_die_tree then does the per-DIE check scheduling + /// itself, down in die_check_context. + class check_stub + : public highlevel_check + { + check_die_tree *_m_die_tree_check; + public: + static checkdescriptor const *descriptor () + { + static checkdescriptor cd + (checkdescriptor::create (*T::descriptor ()) + .prereq () + .inherit > ()); + return &cd; + } + + check_stub (checkstack &stack, dwarflint &lint) + : highlevel_check (stack, lint) + , _m_die_tree_check (lint.check (stack, _m_die_tree_check)) + {} + }; + + ::reg _m_reg_stub; +}; + +#endif /* _CHECK_DIE_TREE_H_ */ diff --git a/dwarflint/check_die_tree.ii b/dwarflint/check_die_tree.ii new file mode 100644 index 000000000..95b98e68e --- /dev/null +++ b/dwarflint/check_die_tree.ii @@ -0,0 +1 @@ +class check_die_tree; diff --git a/dwarflint/check_registrar.cc b/dwarflint/check_registrar.cc new file mode 100644 index 000000000..35198482f --- /dev/null +++ b/dwarflint/check_registrar.cc @@ -0,0 +1,101 @@ +/* Pedantic checking of DWARF files + Copyright (C) 2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include "check_registrar.hh" +#include "checkdescriptor.hh" +#include "dwarflint.hh" +#include "main.hh" +#include "wrap.hh" + +void +check_registrar_aux::add_deps (std::set &to, + checkdescriptor const *cd) +{ + for (std::set::const_iterator it + = cd->prereq ().begin (); it != cd->prereq ().end (); ++it) + include (to, *it); +} + +void +check_registrar_aux::include (std::set &to, + checkdescriptor const *cd) +{ + if (cd->hidden ()) + add_deps (to, cd); + else + to.insert (cd); +} + +bool +check_registrar_aux::be_verbose () +{ + // We can hopefully assume that the option doesn't change during + // execution, so we can simply cache it this was. + static bool be_verbose = opt_list_checks.value () == "full"; + return be_verbose; +} + +void +check_registrar_aux::list_one_check (checkdescriptor const &cd) +{ + const size_t columns = 70; + + if (be_verbose ()) + std::cout << "=== " << cd.name () << " ==="; + else + std::cout << cd.name (); + + checkgroups const &groups = cd.groups (); + if (!groups.empty ()) + { + if (be_verbose ()) + std::cout << std::endl << "groups: "; + else + std::cout << ' '; + std::cout << groups; + } + std::cout << std::endl; + + if (be_verbose ()) + { + prereqs const &prereq = cd.prereq (); + if (!prereq.empty ()) + std::cout << "prerequisites: " << prereq << std::endl; + + char const *desc = cd.description (); + if (desc != NULL) + std::cout << wrap_str (desc, columns).join (); + + options const &opts = cd.opts (); + if (!opts.empty ()) + { + std::cout << "recognized options:" << std::endl; + argp a = opts.build_argp (); + argp_help (&a, stdout, ARGP_HELP_LONG, NULL); + } + + std::cout << std::endl; + } +} diff --git a/dwarflint/checks.cc b/dwarflint/check_registrar.hh similarity index 50% rename from dwarflint/checks.cc rename to dwarflint/check_registrar.hh index 4561f8c87..7f3064ea0 100644 --- a/dwarflint/checks.cc +++ b/dwarflint/check_registrar.hh @@ -1,5 +1,5 @@ -/* - Copyright (C) 2010 Red Hat, Inc. +/* Pedantic checking of DWARF files + Copyright (C) 2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -23,32 +23,49 @@ Network licensing program, please visit www.openinventionnetwork.com . */ -#include "checks.hh" -#include "option.hh" +#ifndef _CHECK_REGISTRAR_H_ +#define _CHECK_REGISTRAR_H_ -static global_opt show_progress - ("Print out checks as they are performed, their context and result.", - "show-progress"); +#include "dwarflint.ii" +#include "checkdescriptor.ii" -reporter::reporter (checkstack const &s, checkdescriptor const &a_cd) - : stack (s) - , cd (a_cd) +#include +#include +#include + +namespace check_registrar_aux { - (*this) ("...", true); + bool be_verbose (); + void list_one_check (checkdescriptor const &cd); + + void include (std::set &to, + checkdescriptor const *cd); + void add_deps (std::set &to, + checkdescriptor const *cd); } -void -reporter::operator () (char const *what, bool ext) +template +class check_registrar_T + : protected std::vector { - if (!show_progress) - return; + typedef std::vector _super_t; +public: - if (false) - for (size_t i = 0; i < stack.size (); ++i) - std::cout << ' '; + using _super_t::push_back; + using _super_t::const_iterator; + using _super_t::begin; + using _super_t::end; - std::cout << cd.name () << ' ' << what; - if (ext) - std::cout << ' ' << cd.groups () << ' ' << stack; - std::cout << std::endl; -} + typedef std::vector checkdescriptors_t; + + checkdescriptors_t + get_descriptors () const + { + std::set descriptors; + for (typename _super_t::const_iterator it = begin (); it != end (); ++it) + check_registrar_aux::include (descriptors, (*it)->descriptor ()); + return checkdescriptors_t (descriptors.begin (), descriptors.end ()); + } +}; + +#endif /* _CHECK_REGISTRAR_H_ */ diff --git a/dwarflint/check_registrar.ii b/dwarflint/check_registrar.ii new file mode 100644 index 000000000..6e84c9118 --- /dev/null +++ b/dwarflint/check_registrar.ii @@ -0,0 +1,2 @@ +//-*-c++-*- +struct check_registrar; diff --git a/dwarflint/checkdescriptor.cc b/dwarflint/checkdescriptor.cc index a5cecc6dd..a74d69c01 100644 --- a/dwarflint/checkdescriptor.cc +++ b/dwarflint/checkdescriptor.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2010 Red Hat, Inc. + Copyright (C) 2010, 2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -62,6 +62,15 @@ checkdescriptor::create::create (char const *name) , _m_hidden (false) {} +checkdescriptor::create::create (checkdescriptor const &base) + : _m_groups (base.groups ()) + , _m_prereq (base.prereq ()) + , _m_name (base.name ()) + , _m_description (base.description ()) + , _m_hidden (base.hidden ()) + , _m_opts (base.opts ()) +{} + checkdescriptor::create & checkdescriptor::create::groups (char const *a_groups) { diff --git a/dwarflint/checkdescriptor.hh b/dwarflint/checkdescriptor.hh index c36eb0674..2127d5f54 100644 --- a/dwarflint/checkdescriptor.hh +++ b/dwarflint/checkdescriptor.hh @@ -1,5 +1,5 @@ /* - Copyright (C) 2010 Red Hat, Inc. + Copyright (C) 2010, 2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -59,6 +59,7 @@ struct checkdescriptor public: create (char const *name = NULL); + create (checkdescriptor const &base); ///< For construction of overrides. create &groups (char const *name); create &description (char const *d) diff --git a/dwarflint/checks.hh b/dwarflint/checks.hh index a29f83cbc..08b152959 100644 --- a/dwarflint/checks.hh +++ b/dwarflint/checks.hh @@ -30,6 +30,8 @@ #include "dwarflint.hh" #include "checkdescriptor.hh" #include "messages.hh" +#include "check_registrar.hh" + #include #include @@ -53,97 +55,24 @@ private: } }; -struct reporter +class popper { - checkstack const &stack; - checkdescriptor const &cd; - - reporter (checkstack const &s, checkdescriptor const &a_cd); - void operator () (char const *what, bool ext = false); -}; + checkstack &_m_guard_stack; -template -T * -dwarflint::check (checkstack &stack) -{ - void const *key = T::key (); - T *c = static_cast (find_check (key)); +public: + popper (checkstack &guard_stack) + : _m_guard_stack (guard_stack) + {} - if (c == NULL) - { - checkdescriptor const &cd = *T::descriptor (); - - struct popper { - checkstack &guard_stack; - popper (checkstack &a_guard_stack) : guard_stack (a_guard_stack) {} - ~popper () { guard_stack.pop_back (); } - }; - - // Put a marker there indicating that we are trying to satisfy - // that dependency. - bool inserted - = _m_checks.insert (std::make_pair (key, (T *)marker)).second; - assert (inserted || !"duplicate key"); - -#define FAIL \ - /* Put the anchor in the table. */ \ - _m_checks[key] = NULL; \ - report ("FAIL") - - reporter report (stack, cd); - try - { - stack.push_back (&cd); - popper p (stack); - - if (!_m_rules.should_check (stack)) - throw check_base::unscheduled (); - - // Now do the check. - c = new T (stack, *this); - } - catch (check_base::unscheduled &e) - { - report ("skipped"); - _m_checks.erase (key); - throw; - } - catch (check_base::failed &e) - { - // We can assume that the check emitted error message. - FAIL; - throw; - } - catch (std::exception &e) - { - wr_error () << "A check failed: " << (cd.name () ?: "(nil)") << ": " - << e.what () << std::endl; - FAIL; - throw check_base::failed (); - } - catch (...) - { - wr_error () << "A check failed: " << (cd.name () ?: "(nil)") << "." - << std::endl; - FAIL; - throw check_base::failed (); - } - -#undef FAIL - - report ("done"); - - // On success, put the actual check object there instead of the - // marker. - _m_checks[key] = c; - } - return c; -} + ~popper () + { + _m_guard_stack.pop_back (); + } +}; template inline T * -dwarflint::toplev_check (checkstack &stack, - __attribute__ ((unused)) T *tag) +dwarflint::toplev_check (checkstack &stack, T *) { try { @@ -157,11 +86,10 @@ dwarflint::toplev_check (checkstack &stack, template struct reg - : public dwarflint::check_registrar::item + : public main_check_item { - reg () - { - dwarflint::check_registrar::inst ()->add (this); + reg () { + dwarflint::main_registrar ()->push_back (this); } virtual void run (checkstack &stack, dwarflint &lint) diff --git a/dwarflint/checks.ii b/dwarflint/checks.ii index b9ca68df7..2d405382e 100644 --- a/dwarflint/checks.ii +++ b/dwarflint/checks.ii @@ -1 +1,2 @@ struct check_base; +class die_check; diff --git a/dwarflint/dwarflint.cc b/dwarflint/dwarflint.cc index f00c1c175..d2c22b0de 100644 --- a/dwarflint/dwarflint.cc +++ b/dwarflint/dwarflint.cc @@ -1,4 +1,4 @@ -/* Dwarflint check scheduler. +/* Pedantic checking of DWARF files Copyright (C) 2008,2009,2010 Red Hat, Inc. This file is part of Red Hat elfutils. @@ -26,8 +26,7 @@ #include "dwarflint.hh" #include "messages.hh" #include "checks.hh" -#include "main.hh" -#include "wrap.hh" +#include "check_registrar.hh" #include #include @@ -68,12 +67,22 @@ namespace } } -dwarflint::dwarflint (char const *a_fname, checkrules const &rules) +void +main_check_registrar::run (dwarflint &lint) +{ + for (const_iterator it = begin (); it != end (); ++it) + { + checkstack stack; + (*it)->run (stack, lint); + } +} + +dwarflint::dwarflint (char const *a_fname, checkrules const &a_rules) : _m_fname (a_fname) , _m_fd (get_fd (_m_fname)) - , _m_rules (rules) + , _m_rules (a_rules) { - check_registrar::inst ()->enroll (*this); + main_registrar ()->run (*this); } dwarflint::~dwarflint () @@ -87,123 +96,167 @@ dwarflint::~dwarflint () delete it->second; } -void -dwarflint::check_registrar::enroll (dwarflint &lint) +void *const dwarflint::marker = (void *)-1; + +void * +dwarflint::find_check (void const *key) { - for (std::vector ::iterator it = _m_items.begin (); - it != _m_items.end (); ++it) + check_map::const_iterator it = _m_checks.find (key); + + if (it != _m_checks.end ()) { - checkstack stack; - (*it)->run (stack, lint); + void *c = it->second; + + // We already tried to do the check, but failed. + if (c == NULL) + throw check_base::failed (); + else + // Recursive dependency! + assert (c != marker); + + return c; } + + return NULL; } -namespace +main_check_registrar * +dwarflint::main_registrar () { - template void include (T &to, checkdescriptor const *cd); + static main_check_registrar inst; + return &inst; +} - template - void add_deps (T &to, checkdescriptor const *cd) - { - for (typename T::const_iterator it = cd->prereq ().begin (); - it != cd->prereq ().end (); ++it) - include (to, *it); - } +die_check_registrar * +dwarflint::die_registrar () +{ + static die_check_registrar inst; + return &inst; +} +namespace +{ template - void include (T &to, checkdescriptor const *cd) + void + list_part_checks (T const &descriptors) { - if (cd->hidden ()) - add_deps (to, cd); - else - to.insert (cd); + for (typename T::const_iterator it = descriptors.begin (); + it != descriptors.end (); ++it) + check_registrar_aux::list_one_check (**it); } } -dwarflint::check_registrar::checkdescriptors_t -dwarflint::check_registrar::get_descriptors () const -{ - std::set descriptors; - for (std::vector ::const_iterator it = _m_items.begin (); - it != _m_items.end (); ++it) - include (descriptors, (*it)->descriptor ()); - return checkdescriptors_t (descriptors.begin (), descriptors.end ()); -} - void -dwarflint::check_registrar::list_checks () const +dwarflint::list_checks () { - bool be_verbose = opt_list_checks.value () == "full"; - checkdescriptors_t descriptors = get_descriptors (); - const size_t columns = 70; + list_part_checks (dwarflint::main_registrar ()->get_descriptors ()); - for (checkdescriptors_t::const_iterator it = descriptors.begin (); - it != descriptors.end (); ++it) - { - checkdescriptor const &cd = **it; - if (be_verbose) - std::cout << "=== " << cd.name () << " ==="; - else - std::cout << cd.name (); - - checkgroups const &groups = cd.groups (); - if (!groups.empty ()) - { - if (be_verbose) - std::cout << std::endl << "groups: "; - else - std::cout << ' '; - std::cout << groups; - } - std::cout << std::endl; - - if (be_verbose) - { - prereqs const &prereq = cd.prereq (); - if (!prereq.empty ()) - std::cout << "prerequisites: " << prereq << std::endl; - - char const *desc = cd.description (); - if (desc != NULL) - std::cout << wrap_str (desc, columns).join (); - - options const &opts = cd.opts (); - if (!opts.empty ()) - { - std::cout << "recognized options:" << std::endl; - argp a = opts.build_argp (); - argp_help (&a, stdout, ARGP_HELP_LONG, NULL); - } - - std::cout << std::endl; - } - } - if (!be_verbose) + if (!check_registrar_aux::be_verbose ()) std::cout << "Use --list-checks=full to get more detailed description." << std::endl; } -void *const dwarflint::marker = (void *)-1; +static global_opt show_progress + ("Print out checks as they are performed, their context and result.", + "show-progress"); + +namespace +{ + struct reporter + { + checkstack const &stack; + checkdescriptor const &cd; + + reporter (checkstack const &s, checkdescriptor const &a_cd); + void operator () (char const *what, bool ext = false); + }; + + reporter::reporter (checkstack const &s, checkdescriptor const &a_cd) + : stack (s) + , cd (a_cd) + { + (*this) ("...", true); + } + + void + reporter::operator () (char const *what, bool ext) + { + if (!show_progress) + return; + + if (false) + for (size_t i = 0; i < stack.size (); ++i) + std::cout << ' '; + + std::cout << cd.name () << ' ' << what; + if (ext) + std::cout << ' ' << cd.groups () << ' ' << stack; + std::cout << std::endl; + } +} void * -dwarflint::find_check (void const *key) +dwarflint::dispatch_check (checkstack &stack, + checkdescriptor const &cd, + void const *key, + check_base *(* create) (checkstack &, dwarflint &)) { - check_map::const_iterator it = _m_checks.find (key); + // Put a marker there indicating that we are trying to satisfy + // that dependency. + bool inserted + = _m_checks.insert (std::make_pair (key, (check_base *)marker)).second; + assert (inserted || !"duplicate key"); - if (it != _m_checks.end ()) +#define FAIL \ + /* Put the anchor in the table. */ \ + _m_checks[key] = NULL; \ + report ("FAIL") + + reporter report (stack, cd); + try { - void *c = it->second; + stack.push_back (&cd); + popper p (stack); - // We already tried to do the check, but failed. - if (c == NULL) - throw check_base::failed (); - else - // Recursive dependency! - assert (c != marker); + if (!_m_rules.should_check (stack)) + throw check_base::unscheduled (); + // Now do the check. + check_base *c = create (stack, *this); + + // On success, put the actual check object there instead of the + // marker. + _m_checks[key] = c; + report ("done"); return c; } + catch (check_base::unscheduled &e) + { + report ("skipped"); + _m_checks.erase (key); + throw; + } + catch (check_base::failed &e) + { + // We can assume that the check emitted error message. + FAIL; + throw; + } + catch (std::exception &e) + { + wr_error () << "A check failed: " << (cd.name () ?: "(nil)") << ": " + << e.what () << std::endl; + FAIL; + throw check_base::failed (); + } + catch (...) + { + wr_error () << "A check failed: " << (cd.name () ?: "(nil)") << "." + << std::endl; + FAIL; + throw check_base::failed (); + } - return NULL; +#undef FAIL } diff --git a/dwarflint/dwarflint.hh b/dwarflint/dwarflint.hh index 51e65f539..6ee64943d 100644 --- a/dwarflint/dwarflint.hh +++ b/dwarflint/dwarflint.hh @@ -1,5 +1,5 @@ /* Pedantic checking of DWARF files - Copyright (C) 2009,2010 Red Hat, Inc. + Copyright (C) 2009,2010,2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -35,12 +35,48 @@ #include "checks.ii" #include "checkdescriptor.ii" #include "checkrule.hh" +#include "check_registrar.hh" +#include "dwarflint.ii" + +// Classes for full-blown check passes. +struct main_check_item +{ + virtual checkdescriptor const *descriptor () const = 0; + virtual ~main_check_item () {} + virtual void run (checkstack &stack, dwarflint &lint) = 0; +}; + +class main_check_registrar + : public check_registrar_T +{ +public: + friend class dwarflint; + void run (dwarflint &lint); +}; + +// Classes for simplified single-die passes. +struct die_check_item +{ + virtual checkdescriptor const *descriptor () const = 0; + virtual ~die_check_item () {} + virtual die_check *create (checkstack &stack, dwarflint &lint) = 0; +}; + +class die_check_registrar + : public check_registrar_T +{ +public: + friend class dwarflint; + void run (checkstack &stack, dwarflint &lint); +}; + class checkstack : public std::vector {}; std::ostream &operator << (std::ostream &o, checkstack const &stack); + class dwarflint { typedef std::map check_map; @@ -57,56 +93,47 @@ class dwarflint // was detected. void *find_check (void const *key); -public: - struct check_registrar + template + static check_base * + create_check_object (checkstack &stack, dwarflint &lint) { - struct item - { - virtual void run (checkstack &stack, dwarflint &lint) = 0; - virtual checkdescriptor const *descriptor () const = 0; - }; - - static check_registrar *inst () - { - static check_registrar inst; - return &inst; - } - - void add (item *i) - { - _m_items.push_back (i); - } - - void list_checks () const; - - typedef std::vector checkdescriptors_t; - checkdescriptors_t get_descriptors () const; - - private: - friend class dwarflint; - void enroll (dwarflint &lint); + return new T (stack, lint); + } - std::vector _m_items; - }; + void *dispatch_check (checkstack &stack, + checkdescriptor const &cd, + void const *key, + check_base *(* create) (checkstack &, dwarflint &)); +public: dwarflint (char const *fname, checkrules const &rules); ~dwarflint (); int fd () { return _m_fd; } char const *fname () { return _m_fname; } - template T *check (checkstack &stack); + template + T * + check (checkstack &stack) + { + void const *key = T::key (); + T *c = static_cast (find_check (key)); + checkdescriptor const &cd = *T::descriptor (); + + if (c == NULL) + c = (T *)dispatch_check (stack, cd, key, &create_check_object); + + return c; + } template T * - check (checkstack &stack, - __attribute__ ((unused)) T *fake) + check (checkstack &stack, T *) { return check (stack); } template - T *toplev_check (checkstack &stack, - __attribute__ ((unused)) T *fake = NULL); + T *toplev_check (checkstack &stack, T *fake = NULL); template T * @@ -118,6 +145,17 @@ public: else return NULL; } + + checkrules const & + rules () const + { + return _m_rules; + } + + static main_check_registrar *main_registrar (); + static die_check_registrar *die_registrar (); + + static void list_checks (); }; #endif//DWARFLINT_HH diff --git a/dwarflint/dwarflint.ii b/dwarflint/dwarflint.ii index e574a5ffa..9769d6bd9 100644 --- a/dwarflint/dwarflint.ii +++ b/dwarflint/dwarflint.ii @@ -1,2 +1,3 @@ class checkstack; class dwarflint; +class main_check_registrar; diff --git a/dwarflint/highlevel_check.hh b/dwarflint/highlevel_check.hh index d4d61d916..3db9f5c76 100644 --- a/dwarflint/highlevel_check.hh +++ b/dwarflint/highlevel_check.hh @@ -1,5 +1,5 @@ /* Pedantic checking of DWARF files. - Copyright (C) 2009, 2010 Red Hat, Inc. + Copyright (C) 2009, 2010, 2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -40,7 +40,9 @@ class open_highlevel_dwarf Dwfl *const _m_dwfl; public: static checkdescriptor const *descriptor () { - static checkdescriptor cd ("open_highlevel_dwarf"); + static checkdescriptor cd + (checkdescriptor::create ("open_highlevel_dwarf") + .hidden ()); return &cd; } diff --git a/dwarflint/main.cc b/dwarflint/main.cc index eb0252e0a..f11849d80 100644 --- a/dwarflint/main.cc +++ b/dwarflint/main.cc @@ -159,7 +159,7 @@ main (int argc, char *argv[]) if (opt_list_checks.seen ()) { - dwarflint::check_registrar::inst ()->list_checks (); + dwarflint::list_checks (); std::exit (0); } else if (remaining == argc) @@ -210,7 +210,6 @@ main (int argc, char *argv[]) try { char const *fname = argv[remaining]; - /* Create an `Elf' descriptor. */ unsigned int prev_error_count = error_count; if (!only_one) std::cout << std::endl << fname << ":" << std::endl; diff --git a/dwarflint/option.cc b/dwarflint/option.cc index 99277270e..420493eee 100644 --- a/dwarflint/option.cc +++ b/dwarflint/option.cc @@ -1,5 +1,5 @@ /* Pedantic checking of DWARF files - Copyright (C) 2009,2010 Red Hat, Inc. + Copyright (C) 2009,2010,2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -30,6 +30,7 @@ #include "option.hh" #include "dwarflint.hh" #include "checkdescriptor.hh" +#include "check_registrar.hh" #include #include #include @@ -91,8 +92,7 @@ argppp & argppp::inst () { static argppp my - (global_opts, - dwarflint::check_registrar::inst ()->get_descriptors ()); + (global_opts, dwarflint::main_registrar ()->get_descriptors ()); return my; } @@ -102,7 +102,7 @@ argppp::argppp (options const &global, { argp main = global.build_argp (true); - typedef dwarflint::check_registrar::checkdescriptors_t checkdescriptors_t; + typedef main_check_registrar::checkdescriptors_t checkdescriptors_t; for (checkdescriptors_t::const_iterator it = checkdescriptors.begin (); it != checkdescriptors.end (); ++it) if (!(*it)->opts ().empty ())