/* Pedantic checking of DWARF files Copyright (C) 2009-2011 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 . */ #include "messages.hh" #include "coverage.hh" #include "option.hh" #include #include #include #include #include unsigned error_count = 0; bool message_context::_m_last_emitted; bool message_accept (struct message_criteria const *cri, unsigned long cat) { for (size_t i = 0; i < cri->size (); ++i) { message_term const &t = cri->at (i); if ((t.positive & cat) == t.positive && (t.negative & cat) == 0) return true; } return false; } namespace { struct cat_to_str : public std::vector { cat_to_str () { int count = 0; #define MC(CAT, ID) if (ID > count) count = ID; MESSAGE_CATEGORIES #undef MC resize (count + 1); #define MC(CAT, ID) (*this)[ID] = #CAT; MESSAGE_CATEGORIES #undef MC } } cat_names; size_t cat_max = cat_names.size (); } message_category operator | (message_category a, message_category b) { return static_cast ((unsigned long)a | b); } message_category & operator |= (message_category &a, message_category b) { a = a | b; return a; } std::string message_term::str () const { std::ostringstream os; os << '('; bool got = false; for (size_t i = 0; i <= cat_max; ++i) { size_t mask = 1u << i; if ((positive & mask) != 0 || (negative & mask) != 0) { if (got) os << " & "; if ((negative & (1u << i)) != 0) os << '~'; os << cat_names[i]; got = true; } } if (!got) os << '1'; os << ')'; return os.str (); } std::string message_criteria::str () const { std::ostringstream os; for (size_t i = 0; i < size (); ++i) { message_term const &t = at (i); if (i > 0) os << " | "; os << t.str (); } return os.str (); } void message_criteria::operator &= (message_term const &term) { assert ((term.positive & term.negative) == 0); for (size_t i = 0; i < size (); ) { message_term &t = at (i); t.positive = t.positive | term.positive; t.negative = t.negative | term.negative; if ((t.positive & t.negative) != 0) /* A ^ ~A -> drop the term. */ erase (begin () + i); else ++i; } } void message_criteria::operator |= (message_term const &term) { assert ((term.positive & term.negative) == 0); push_back (term); } // xxx this one is inaccessible from the outside. Make it like &=, |= // above /* NEG(a&b&~c) -> (~a + ~b + c) */ message_criteria operator ! (message_term const &term) { assert ((term.positive & term.negative) == 0); message_criteria ret; for (size_t i = 0; i < cat_max; ++i) { unsigned mask = 1u << i; if ((term.positive & mask) != 0) ret |= message_term ((message_category)(1u << i), mc_none); else if ((term.negative & mask) != 0) ret |= message_term (mc_none, (message_category)(1u << i)); } return ret; } std::ostream & operator<< (std::ostream &o, message_category cat) { o << '('; bool got = false; for (size_t i = 0; i <= cat_max; ++i) { size_t mask = 1u << i; if ((cat & mask) != 0) { if (got) o << ","; o << cat_names[i]; got = true; } } if (!got) o << "none"; return o << ')'; } std::ostream & operator<< (std::ostream &o, message_term const &term) { return o << term.str (); } std::ostream & operator<< (std::ostream &o, __attribute__ ((unused)) message_criteria const &criteria) { return o << criteria.str (); } /* MUL((a&b + c&d), (e&f + g&h)) -> (a&b&e&f + a&b&g&h + c&d&e&f + c&d&g&h) */ void message_criteria::operator *= (message_criteria const &rhs) { message_criteria ret; for (size_t i = 0; i < size (); ++i) for (size_t j = 0; j < rhs.size (); ++j) { message_term t1 = at (i); message_term const &t2 = rhs.at (j); t1.positive |= t2.positive; t1.negative |= t2.negative; if (t1.positive & t1.negative) /* A ^ ~A -> drop the term. */ continue; ret |= t1; } *this = ret; } // xxx this one is inaccessible from the outside. Bind it properly /* Reject message if TERM passes. */ void message_criteria::and_not (message_term const &term) { // xxxxx really?? "!"?? message_criteria tmp = !message_term (term.negative, term.positive); *this *= tmp; } static void wr_verror (locus const &loc, const char *format, va_list ap) { printf ("error: %s", loc.format ().c_str ()); vprintf (format, ap); ++error_count; } static void wr_vwarning (locus const &loc, const char *format, va_list ap) { printf ("%s", loc.format ().c_str ()); vprintf (format, ap); ++error_count; } void wr_error (locus const *loc, const char *format, ...) { va_list ap; va_start (ap, format); wr_verror (*loc, format, ap); va_end (ap); } void wr_message (unsigned long category, locus const *loc, const char *format, ...) { va_list ap; va_start (ap, format); // Clumsy duplicate filtering. Use format as key. bool whether = false; message_category cat = (message_category) category; wr_message (cat).id (format, whether); if (whether && message_accept (&warning_criteria, category)) { if (message_accept (&error_criteria, category)) wr_verror (*loc, format, ap); else wr_vwarning (*loc, format, ap); } va_end (ap); } namespace { class nostream: public std::ostream {}; static nostream nostream; std::ostream & get_stream () { return std::cout; } } global_opt dup_threshold_opt ("Threshold for duplicate messages." " Defaults to 16." " Use zero for no limit.", "count", "dups"); namespace { unsigned dup_threshold () { static unsigned t = dup_threshold_opt.value (16); if (t == 0) t = -1; return t; } } int message_count_filter::should_emit (void const *key) { unsigned count = ++_m_counters[key]; if (count > dup_threshold ()) return 0; else if (count == dup_threshold ()) return -1; else return 1; } message_context::message_context (message_count_filter *filter, locus const *loc, char const *prefix) : _m_filter (filter) , _m_loc (loc) , _m_prefix (prefix) {} std::ostream & message_context::when (bool whether) const { _m_last_emitted = false; if (whether) { ++error_count; _m_last_emitted = true; std::ostream &ret = get_stream (); ret << _m_prefix; if (_m_loc != NULL) ret << _m_loc->format () << ": "; return ret; } else return nostream; } std::ostream & message_context::when_prev () const { return when (wr_prev_emitted ()); } std::ostream & message_context::id (void const *key, bool &whether) { if (_m_filter == NULL) return nostream; else if (int status = _m_filter->should_emit (key)) { if (status == -1) get_stream () << "(threshold [--dups=" << dup_threshold () << "] reached for the following message)" << std::endl; whether = true; return when (true); } else return nostream; } std::ostream & message_context::id (void const *key) { bool whether; return id (key, whether); } std::ostream & message_context::operator << (char const *message) { return id (message) << message; } std::ostream & message_context::operator << (std::string const &message) { return *this << message.c_str (); } std::ostream & wr_error () { ++error_count; return get_stream () << gettext ("error: "); } std::ostream & wr_error (locus const &loc) { std::string fmt = loc.format (); return wr_error () << fmt << ": "; } message_context message_context::filter_message (locus const *loc, message_category category) { if (!message_accept (&warning_criteria, category)) return message_context (NULL, NULL, NULL); else if (message_accept (&error_criteria, category)) return message_context (message_count_filter::inst (), loc, "error: "); else return message_context (message_count_filter::inst (), loc, "warning: "); } message_context wr_message (message_category category) { return message_context::filter_message (NULL, category); } message_context wr_message (locus const &loc, message_category category) { return message_context::filter_message (&loc, category); } void wr_format_padding_message (message_category category, locus const &loc, uint64_t start, uint64_t end, char const *kind) { char msg[128]; wr_message (loc, category) << range_fmt (msg, sizeof msg, start, end) << ": " << kind << "." << std::endl; } void wr_format_leb128_message (locus const &loc, const char *what, const char *purpose, const unsigned char *begin, const unsigned char *end) { message_category category = mc_leb128 | mc_acc_bloat | mc_impact_3; char buf[(end - begin) * 3 + 1]; // 2 hexa digits+" " per byte, and term. 0 char *ptr = buf; for (; begin < end; ++begin) ptr += sprintf (ptr, " %02x", *begin); wr_message (loc, category) << what << ": value " << purpose << " encoded as `" << (buf + 1) << "'." << std::endl; } void wr_message_padding_0 (message_category category, locus const &loc, uint64_t start, uint64_t end) { wr_format_padding_message (category | mc_acc_bloat | mc_impact_1, loc, start, end, "unnecessary padding with zero bytes"); } void wr_message_padding_n0 (message_category category, locus const &loc, uint64_t start, uint64_t end) { wr_format_padding_message (category | mc_acc_bloat | mc_impact_1, loc, start, end, "unreferenced non-zero bytes"); } void wr_reset_counters () { error_count = 0; message_count_filter::inst ()->clear (); } bool wr_prev_emitted () { return message_context::_m_last_emitted; }