blob: 5c9d8c436daccf835b36a0abfe3a0317d1a50511 [file] [log] [blame]
# 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)