gdb/contrib/exsummary.py - gdb

Global variables defined

Functions defined

Source code

  1. #   Copyright 2011-2015 Free Software Foundation, Inc.
  2. #
  3. #   This is free software: you can redistribute it and/or modify it
  4. #   under the terms of the GNU General Public License as published by
  5. #   the Free Software Foundation, either version 3 of the License, or
  6. #   (at your option) any later version.
  7. #
  8. #   This program is distributed in the hope that it will be useful, but
  9. #   WITHOUT ANY WARRANTY; without even the implied warranty of
  10. #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11. #   General Public License for more details.
  12. #
  13. #   You should have received a copy of the GNU General Public License
  14. #   along with this program.  If not, see
  15. #   <http://www.gnu.org/licenses/>.

  16. import sys
  17. import glob

  18. # Compute the summary information from the files created by
  19. # excheck.py.  Run in the build directory where you used the
  20. # excheck.py plugin.

  21. class Function:
  22.     def __init__(self, name):
  23.         self.name = name
  24.         self.location = None
  25.         self.callers = []
  26.         self.can_throw = False
  27.         self.marked_nothrow = False
  28.         self.reason = None

  29.     def log(self, message):
  30.         print "%s: note: %s" % (self.location, message)

  31.     def set_location(self, location):
  32.         self.location = location

  33.     # CALLER is an Edge.
  34.     def add_caller(self, caller):
  35.         # self.log("adding call from %s" % caller.from_fn.name)
  36.         self.callers.append(caller)
  37.         # self.log("len = %d" % len(self.callers))

  38.     def consistency_check(self):
  39.         if self.marked_nothrow and self.can_throw:
  40.             print ("%s: error: %s marked as both 'throw' and 'nothrow'"
  41.                    % (self.location, self.name))

  42.     def declare_nothrow(self):
  43.         self.marked_nothrow = True
  44.         self.consistency_check()

  45.     def declare_throw(self):
  46.         result = not self.can_throw # Return True the first time
  47.         self.can_throw = True
  48.         self.consistency_check()
  49.         return result

  50.     def print_stack(self, is_indirect):
  51.         if is_indirect:
  52.             print ("%s: error: function %s is marked nothrow but is assumed to throw due to indirect call"
  53.                    % (self.location, self.name))
  54.         else:
  55.             print ("%s: error: function %s is marked nothrow but can throw"
  56.                    % (self.location, self.name))

  57.         edge = self.reason
  58.         while edge is not None:
  59.             print ("%s: info: via call to %s"
  60.                    % (edge.location, edge.to_fn.name))
  61.             edge = edge.to_fn.reason

  62.     def mark_throw(self, edge, work_list, is_indirect):
  63.         if not self.can_throw:
  64.             # self.log("can throw")
  65.             self.can_throw = True
  66.             self.reason = edge
  67.             if self.marked_nothrow:
  68.                 self.print_stack(is_indirect)
  69.             else:
  70.                 # Do this in the 'else' to avoid extra error
  71.                 # propagation.
  72.                 work_list.append(self)

  73. class Edge:
  74.     def __init__(self, from_fn, to_fn, location):
  75.         self.from_fn = from_fn
  76.         self.to_fn = to_fn
  77.         self.location = location

  78. # Work list of known-throwing functions.
  79. work_list = []
  80. # Map from function name to Function object.
  81. function_map = {}
  82. # Work list of indirect calls.
  83. indirect_functions = []
  84. # Whether we should process cleanup functions as well.
  85. process_cleanups = False
  86. # Whether we should process indirect function calls.
  87. process_indirect = False

  88. def declare(fn_name):
  89.     global function_map
  90.     if fn_name not in function_map:
  91.         function_map[fn_name] = Function(fn_name)
  92.     return function_map[fn_name]

  93. def define_function(fn_name, location):
  94.     fn = declare(fn_name)
  95.     fn.set_location(location)

  96. def declare_throw(fn_name):
  97.     global work_list
  98.     fn = declare(fn_name)
  99.     if fn.declare_throw():
  100.         work_list.append(fn)

  101. def declare_nothrow(fn_name):
  102.     fn = declare(fn_name)
  103.     fn.declare_nothrow()

  104. def declare_cleanup(fn_name):
  105.     global process_cleanups
  106.     fn = declare(fn_name)
  107.     if process_cleanups:
  108.         fn.declare_nothrow()

  109. def function_call(to, frm, location):
  110.     to_fn = declare(to)
  111.     frm_fn = declare(frm)
  112.     to_fn.add_caller(Edge(frm_fn, to_fn, location))

  113. def has_indirect_call(fn_name, location):
  114.     global indirect_functions
  115.     fn = declare(fn_name)
  116.     phony = Function("<indirect call>")
  117.     phony.add_caller(Edge(fn, phony, location))
  118.     indirect_functions.append(phony)

  119. def mark_functions(worklist, is_indirect):
  120.     for callee in worklist:
  121.         for edge in callee.callers:
  122.             edge.from_fn.mark_throw(edge, worklist, is_indirect)

  123. def help_and_exit():
  124.     print "Usage: exsummary [OPTION]..."
  125.     print ""
  126.     print "Read the .py files from the exception checker plugin and"
  127.     print "generate an error summary."
  128.     print ""
  129.     print "  --cleanups     Include invalid behavior in cleanups"
  130.     print "  --indirect     Include assumed errors due to indirect function calls"
  131.     sys.exit(0)

  132. def main():
  133.     global work_list
  134.     global indirect_functions
  135.     global process_cleanups
  136.     global process_indirect

  137.     for arg in sys.argv:
  138.         if arg == '--cleanups':
  139.             process_cleanups = True
  140.         elif arg == '--indirect':
  141.             process_indirect = True
  142.         elif arg == '--help':
  143.             help_and_exit()

  144.     for fname in sorted(glob.glob('*.c.gdb_exc.py')):
  145.         execfile(fname)
  146.     print "================"
  147.     print "= Ordinary marking"
  148.     print "================"
  149.     mark_functions(work_list, False)
  150.     if process_indirect:
  151.         print "================"
  152.         print "= Indirect marking"
  153.         print "================"
  154.         mark_functions(indirect_functions, True)
  155.     return 0

  156. if __name__ == '__main__':
  157.     status = main()
  158.     sys.exit(status)