| Index: gdb/contrib/exsummary.py
|
| diff --git a/gdb/contrib/exsummary.py b/gdb/contrib/exsummary.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5c9d8c436daccf835b36a0abfe3a0317d1a50511
|
| --- /dev/null
|
| +++ b/gdb/contrib/exsummary.py
|
| @@ -0,0 +1,185 @@
|
| +# Copyright 2011, 2013 Free Software Foundation, Inc.
|
| +#
|
| +# This 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.
|
| +#
|
| +# This program 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
|
| +# <http://www.gnu.org/licenses/>.
|
| +
|
| +import sys
|
| +import glob
|
| +
|
| +# Compute the summary information from the files created by
|
| +# excheck.py. Run in the build directory where you used the
|
| +# excheck.py plugin.
|
| +
|
| +class Function:
|
| + def __init__(self, name):
|
| + self.name = name
|
| + self.location = None
|
| + self.callers = []
|
| + self.can_throw = False
|
| + self.marked_nothrow = False
|
| + self.reason = None
|
| +
|
| + def log(self, message):
|
| + print "%s: note: %s" % (self.location, message)
|
| +
|
| + def set_location(self, location):
|
| + self.location = location
|
| +
|
| + # CALLER is an Edge.
|
| + def add_caller(self, caller):
|
| + # self.log("adding call from %s" % caller.from_fn.name)
|
| + self.callers.append(caller)
|
| + # self.log("len = %d" % len(self.callers))
|
| +
|
| + def consistency_check(self):
|
| + if self.marked_nothrow and self.can_throw:
|
| + print ("%s: error: %s marked as both 'throw' and 'nothrow'"
|
| + % (self.location, self.name))
|
| +
|
| + def declare_nothrow(self):
|
| + self.marked_nothrow = True
|
| + self.consistency_check()
|
| +
|
| + def declare_throw(self):
|
| + result = not self.can_throw # Return True the first time
|
| + self.can_throw = True
|
| + self.consistency_check()
|
| + return result
|
| +
|
| + def print_stack(self, is_indirect):
|
| + if is_indirect:
|
| + print ("%s: error: function %s is marked nothrow but is assumed to throw due to indirect call"
|
| + % (self.location, self.name))
|
| + else:
|
| + print ("%s: error: function %s is marked nothrow but can throw"
|
| + % (self.location, self.name))
|
| +
|
| + edge = self.reason
|
| + while edge is not None:
|
| + print ("%s: info: via call to %s"
|
| + % (edge.location, edge.to_fn.name))
|
| + edge = edge.to_fn.reason
|
| +
|
| + def mark_throw(self, edge, work_list, is_indirect):
|
| + if not self.can_throw:
|
| + # self.log("can throw")
|
| + self.can_throw = True
|
| + self.reason = edge
|
| + if self.marked_nothrow:
|
| + self.print_stack(is_indirect)
|
| + else:
|
| + # Do this in the 'else' to avoid extra error
|
| + # propagation.
|
| + work_list.append(self)
|
| +
|
| +class Edge:
|
| + def __init__(self, from_fn, to_fn, location):
|
| + self.from_fn = from_fn
|
| + self.to_fn = to_fn
|
| + self.location = location
|
| +
|
| +# Work list of known-throwing functions.
|
| +work_list = []
|
| +# Map from function name to Function object.
|
| +function_map = {}
|
| +# Work list of indirect calls.
|
| +indirect_functions = []
|
| +# Whether we should process cleanup functions as well.
|
| +process_cleanups = False
|
| +# Whether we should process indirect function calls.
|
| +process_indirect = False
|
| +
|
| +def declare(fn_name):
|
| + global function_map
|
| + if fn_name not in function_map:
|
| + function_map[fn_name] = Function(fn_name)
|
| + return function_map[fn_name]
|
| +
|
| +def define_function(fn_name, location):
|
| + fn = declare(fn_name)
|
| + fn.set_location(location)
|
| +
|
| +def declare_throw(fn_name):
|
| + global work_list
|
| + fn = declare(fn_name)
|
| + if fn.declare_throw():
|
| + work_list.append(fn)
|
| +
|
| +def declare_nothrow(fn_name):
|
| + fn = declare(fn_name)
|
| + fn.declare_nothrow()
|
| +
|
| +def declare_cleanup(fn_name):
|
| + global process_cleanups
|
| + fn = declare(fn_name)
|
| + if process_cleanups:
|
| + fn.declare_nothrow()
|
| +
|
| +def function_call(to, frm, location):
|
| + to_fn = declare(to)
|
| + frm_fn = declare(frm)
|
| + to_fn.add_caller(Edge(frm_fn, to_fn, location))
|
| +
|
| +def has_indirect_call(fn_name, location):
|
| + global indirect_functions
|
| + fn = declare(fn_name)
|
| + phony = Function("<indirect call>")
|
| + phony.add_caller(Edge(fn, phony, location))
|
| + indirect_functions.append(phony)
|
| +
|
| +def mark_functions(worklist, is_indirect):
|
| + for callee in worklist:
|
| + for edge in callee.callers:
|
| + edge.from_fn.mark_throw(edge, worklist, is_indirect)
|
| +
|
| +def help_and_exit():
|
| + print "Usage: exsummary [OPTION]..."
|
| + print ""
|
| + print "Read the .py files from the exception checker plugin and"
|
| + print "generate an error summary."
|
| + print ""
|
| + print " --cleanups Include invalid behavior in cleanups"
|
| + print " --indirect Include assumed errors due to indirect function calls"
|
| + sys.exit(0)
|
| +
|
| +def main():
|
| + global work_list
|
| + global indirect_functions
|
| + global process_cleanups
|
| + global process_indirect
|
| +
|
| + for arg in sys.argv:
|
| + if arg == '--cleanups':
|
| + process_cleanups = True
|
| + elif arg == '--indirect':
|
| + process_indirect = True
|
| + elif arg == '--help':
|
| + help_and_exit()
|
| +
|
| + for fname in sorted(glob.glob('*.c.gdb_exc.py')):
|
| + execfile(fname)
|
| + print "================"
|
| + print "= Ordinary marking"
|
| + print "================"
|
| + mark_functions(work_list, False)
|
| + if process_indirect:
|
| + print "================"
|
| + print "= Indirect marking"
|
| + print "================"
|
| + mark_functions(indirect_functions, True)
|
| + return 0
|
| +
|
| +if __name__ == '__main__':
|
| + status = main()
|
| + sys.exit(status)
|
|
|