]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: First stab at location/value stats
authorPetr Machata <pmachata@redhat.com>
Thu, 16 Sep 2010 20:41:51 +0000 (22:41 +0200)
committerPetr Machata <pmachata@redhat.com>
Thu, 16 Sep 2010 20:41:51 +0000 (22:41 +0200)
- things will change, some parts of the proposal are not yet implemented

dwarflint/Makefile.am
dwarflint/locstats.cc [new file with mode: 0644]

index c18c828f1bab55f9ead728e81cba7cc864248322..b034f5cf138c0543de0d375b24a35cf28cab30fa 100644 (file)
@@ -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 (file)
index 0000000..fe7f57f
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#include "highlevel_check.hh"
+#include "all-dies-it.hh"
+#include "option.hh"
+
+#include <sstream>
+
+using elfutils::dwarf;
+
+namespace
+{
+  class locstats
+    : public highlevel_check<locstats>
+  {
+  public:
+    static checkdescriptor const *descriptor () {
+      static checkdescriptor cd
+       (checkdescriptor::create ("locstats")
+        .groups ("@nodefault"));
+      return &cd;
+    }
+
+    locstats (checkstack &stack, dwarflint &lint);
+  };
+
+  reg<locstats> 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<tabrule>
+  {
+    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<locstats> (stack, lint)
+{
+  std::map<int, unsigned long> 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<dwarf> it = all_dies_iterator<dwarf> (dw);
+       it != all_dies_iterator<dwarf> (); ++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)
+          ? "<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 ();
+       }
+    }
+}