From: Petr Machata Date: Thu, 16 Sep 2010 20:41:51 +0000 (+0200) Subject: dwarflint: First stab at location/value stats X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1d5fd212d8bf6ac52f2bf12e84a81c6d49035b46;p=thirdparty%2Felfutils.git dwarflint: First stab at location/value stats - things will change, some parts of the proposal are not yet implemented --- diff --git a/dwarflint/Makefile.am b/dwarflint/Makefile.am index c18c828f1..b034f5cf1 100644 --- a/dwarflint/Makefile.am +++ b/dwarflint/Makefile.am @@ -72,6 +72,7 @@ dwarflint_SOURCES = \ check_dups_abstract_origin.cc \ check_duplicate_DW_tag_variable.cc \ check_nodebug.cc \ + locstats.cc \ ../src/dwarfstrings.c TESTS = tests/run-debug_abbrev-duplicate-attribute.sh \ diff --git a/dwarflint/locstats.cc b/dwarflint/locstats.cc new file mode 100644 index 000000000..fe7f57f69 --- /dev/null +++ b/dwarflint/locstats.cc @@ -0,0 +1,268 @@ +/* + Copyright (C) 2010 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 "highlevel_check.hh" +#include "all-dies-it.hh" +#include "option.hh" + +#include + +using elfutils::dwarf; + +namespace +{ + class locstats + : public highlevel_check + { + public: + static checkdescriptor const *descriptor () { + static checkdescriptor cd + (checkdescriptor::create ("locstats") + .groups ("@nodefault")); + return &cd; + } + + locstats (checkstack &stack, dwarflint &lint); + }; + + reg reg_locstats; + + void_option ignore_single_addr + ("Exclude global and static variables from the statistics.", + "locstats:ignore-single-addr"); + + string_option tabulation_rule + ("Rule for sorting results into buckets.", + "start[:step][,...]", + "locstats:tabulate"); + + struct tabrule + { + int start; + int step; + tabrule (int a_start, int a_step) + : start (a_start), step (a_step) + {} + bool operator < (tabrule const &other) const { + return start < other.start; + } + }; + + struct tabrules_t + : public std::vector + { + tabrules_t (std::string const &rule) + { + std::stringstream ss; + ss << rule; + + std::string item; + while (std::getline (ss, item, ',')) + { + if (item.empty ()) + continue; + int start; + int step; + int r = std::sscanf (item.c_str (), "%d:%d", &start, &step); + if (r == EOF || r == 0) + continue; + if (r == 1) + step = 0; + push_back (tabrule (start, step)); + } + + push_back (tabrule (100, 0)); + std::sort (begin (), end ()); + } + + void next () + { + if (at (0).step == 0) + erase (begin ()); + else + { + at (0).start += at (0).step; + if (size () > 1) + { + if (at (0).start > at (1).start) + erase (begin ()); + while (size () > 1 + && at (0).start == at (1).start) + erase (begin ()); + } + } + } + + bool match (int value) const + { + return at (0).start == value; + } + }; +} + +locstats::locstats (checkstack &stack, dwarflint &lint) + : highlevel_check (stack, lint) +{ + std::map tally; + unsigned long total = 0; + for (int i = 0; i <= 100; ++i) + tally[i] = 0; + + tabrules_t tabrules (tabulation_rule.seen () + ? tabulation_rule.value () : "10:10"); + + for (all_dies_iterator it = all_dies_iterator (dw); + it != all_dies_iterator (); ++it) + { + dwarf::debug_info_entry const &die = *it; + + // We are interested in variables and formal parameters + bool is_formal_parameter = die.tag () == DW_TAG_formal_parameter; + if (!is_formal_parameter && die.tag () != DW_TAG_variable) + continue; + + dwarf::debug_info_entry const &parent = it.parent (); + + dwarf::debug_info_entry::attributes_type const &attrs + = die.attributes (); + + // ... except those that are just declarations + if (attrs.find (DW_AT_declaration) != attrs.end ()) + continue; + + // Of formal parameters we ignore those that are children of + // subprograms that are themselves declarations. + if (is_formal_parameter) + { + if (parent.attributes ().find (DW_AT_declaration) + != die.attributes ().end ()) + continue; + } + + // Unfortunately the location expression is not yet wrapped + // in c++, so we need to revert back to C code. + Dwarf_Die die_c_mem, + *die_c = dwarf_offdie (this->c_dw, die.offset (), &die_c_mem); + assert (die_c != NULL); + + Dwarf_Attribute locattr_mem, + *locattr = dwarf_attr_integrate (die_c, DW_AT_location, &locattr_mem); + + // Also ignore extern globals -- these have DW_AT_external and + // no DW_AT_location. + if (attrs.find (DW_AT_external) != attrs.end () && locattr == NULL) + continue; + + /* + Dwarf_Attribute name_attr_mem, + *name_attr = dwarf_attr_integrate (die_c, DW_AT_name, &name_attr_mem); + std::string name = name_attr != NULL + ? dwarf_formstring (name_attr) + : (dwarf_hasattr_integrate (die_c, DW_AT_artificial) + ? "" : "???"); + + std::cerr << "die=" << std::hex << die.offset () + << " '" << name << '\''; + */ + + int coverage; + Dwarf_Op *expr; + size_t len; + + // consts need no location + if (attrs.find (DW_AT_const_value) != attrs.end ()) + coverage = 100; + + // no location + else if (locattr == NULL) + coverage = 0; + + // non-list location + else if (dwarf_getlocation (locattr, &expr, &len) == 0) + { + if (len == 1 && expr[0].atom == DW_OP_addr + && ignore_single_addr) + // Globals and statics have non-list location that is a + // singleton DW_OP_addr expression. + continue; + coverage = 100; + } + + // location list + else + { + dwarf::ranges const &ranges + = die.ranges ().empty () ? parent.ranges () : die.ranges (); + if (ranges.empty ()) + // what? + continue; + + size_t length = 0; + size_t covered = 0; + for (dwarf::ranges::const_iterator rit = ranges.begin (); + rit != ranges.end (); ++rit) + { + Dwarf_Addr low = (*rit).first; + Dwarf_Addr high = (*rit).second; + length += high - low; + /*std::cerr << std::endl << " " << low << ".." << high + << std::endl;*/ + for (Dwarf_Addr addr = low; addr < high; ++addr) + if (dwarf_getlocation_addr (locattr, addr, + NULL, NULL, 0) > 0) + covered++; + } + coverage = 100 * covered / length; + } + + tally[coverage]++; + total++; + } + + unsigned long cumulative = 0; + unsigned long last = 0; + int last_pct = 0; + std::cout << "cov%\tsamples\tcumul" << std::endl; + for (int i = 0; i <= 100; ++i) + { + cumulative += tally.find (i)->second; + if (tabrules.match (i)) + { + long int samples = cumulative - last; + std::cout << std::dec << last_pct; + if (last_pct != i) + std::cout << ".." << i; + std::cout << "\t" << samples + << '/' << (100*samples / total) << '%' + << "\t" << cumulative + << '/' << (100*cumulative / total) << '%' + << std::endl; + last = cumulative; + last_pct = i + 1; + + tabrules.next (); + } + } +}