+2019-01-05 Tom Tromey <tom@tromey.com>
+
+ * contrib/cleanup_check.py: Remove.
+ * contrib/gcc-with-excheck: Remove.
+ * contrib/exsummary.py: Remove.
+ * contrib/excheck.py: Remove.
+
2019-01-05 Joel Brobecker <brobecker@adacore.com>
* thread.c (delete_thread_1): Add gdb_assert that THR is not
+++ /dev/null
-# Copyright 2013-2019 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 gcc
-import gccutils
-import sys
-
-want_raii_info = False
-
-logging = False
-show_cfg = False
-
-def log(msg, indent=0):
- global logging
- if logging:
- sys.stderr.write('%s%s\n' % (' ' * indent, msg))
- sys.stderr.flush()
-
-def is_cleanup_type(return_type):
- if not isinstance(return_type, gcc.PointerType):
- return False
- if not isinstance(return_type.dereference, gcc.RecordType):
- return False
- if str(return_type.dereference.name) == 'cleanup':
- return True
- return False
-
-def is_constructor(decl):
- "Return True if the function DECL is a cleanup constructor; False otherwise"
- return is_cleanup_type(decl.type.type) and (not decl.name or str(decl.name) != 'make_final_cleanup')
-
-destructor_names = set(['do_cleanups', 'discard_cleanups'])
-
-def is_destructor(decl):
- return decl.name in destructor_names
-
-# This list is just much too long... we should probably have an
-# attribute instead.
-special_names = set(['do_final_cleanups', 'discard_final_cleanups',
- 'save_cleanups', 'save_final_cleanups',
- 'restore_cleanups', 'restore_final_cleanups',
- 'exceptions_state_mc_init',
- 'make_my_cleanup2', 'make_final_cleanup', 'all_cleanups',
- 'save_my_cleanups', 'quit_target'])
-
-def needs_special_treatment(decl):
- return decl.name in special_names
-
-# Sometimes we need a new placeholder object that isn't the same as
-# anything else.
-class Dummy(object):
- def __init__(self, location):
- self.location = location
-
-# A wrapper for a cleanup which has been assigned to a variable.
-# This holds the variable and the location.
-class Cleanup(object):
- def __init__(self, var, location):
- self.var = var
- self.location = location
-
-# A class representing a master cleanup. This holds a stack of
-# cleanup objects and supports a merging operation.
-class MasterCleanup(object):
- # Create a new MasterCleanup object. OTHER, if given, is a
- # MasterCleanup object to copy.
- def __init__(self, other = None):
- # 'cleanups' is a list of cleanups. Each element is either a
- # Dummy, for an anonymous cleanup, or a Cleanup, for a cleanup
- # which was assigned to a variable.
- if other is None:
- self.cleanups = []
- self.aliases = {}
- else:
- self.cleanups = other.cleanups[:]
- self.aliases = dict(other.aliases)
-
- def compare_vars(self, definition, argument):
- if definition == argument:
- return True
- if argument in self.aliases:
- argument = self.aliases[argument]
- if definition in self.aliases:
- definition = self.aliases[definition]
- return definition == argument
-
- def note_assignment(self, lhs, rhs):
- log('noting assignment %s = %s' % (lhs, rhs), 4)
- self.aliases[lhs] = rhs
-
- # Merge with another MasterCleanup.
- # Returns True if this resulted in a change to our state.
- def merge(self, other):
- # We do explicit iteration like this so we can easily
- # update the list after the loop.
- counter = -1
- found_named = False
- for counter in range(len(self.cleanups) - 1, -1, -1):
- var = self.cleanups[counter]
- log('merge checking %s' % var, 4)
- # Only interested in named cleanups.
- if isinstance(var, Dummy):
- log('=> merge dummy', 5)
- continue
- # Now see if VAR is found in OTHER.
- if other._find_var(var.var) >= 0:
- log ('=> merge found', 5)
- break
- log('=>merge not found', 5)
- found_named = True
- if found_named and counter < len(self.cleanups) - 1:
- log ('merging to %d' % counter, 4)
- if counter < 0:
- self.cleanups = []
- else:
- self.cleanups = self.cleanups[0:counter]
- return True
- # If SELF is empty but OTHER has some cleanups, then consider
- # that a change as well.
- if len(self.cleanups) == 0 and len(other.cleanups) > 0:
- log('merging non-empty other', 4)
- self.cleanups = other.cleanups[:]
- return True
- return False
-
- # Push a new constructor onto our stack. LHS is the
- # left-hand-side of the GimpleCall statement. It may be None,
- # meaning that this constructor's value wasn't used.
- def push(self, location, lhs):
- if lhs is None:
- obj = Dummy(location)
- else:
- obj = Cleanup(lhs, location)
- log('pushing %s' % lhs, 4)
- idx = self._find_var(lhs)
- if idx >= 0:
- gcc.permerror(location, 'reassigning to known cleanup')
- gcc.inform(self.cleanups[idx].location,
- 'previous assignment is here')
- self.cleanups.append(obj)
-
- # A helper for merge and pop that finds BACK_TO in self.cleanups,
- # and returns the index, or -1 if not found.
- def _find_var(self, back_to):
- for i in range(len(self.cleanups) - 1, -1, -1):
- if isinstance(self.cleanups[i], Dummy):
- continue
- if self.compare_vars(self.cleanups[i].var, back_to):
- return i
- return -1
-
- # Pop constructors until we find one matching BACK_TO.
- # This is invoked when we see a do_cleanups call.
- def pop(self, location, back_to):
- log('pop:', 4)
- i = self._find_var(back_to)
- if i >= 0:
- self.cleanups = self.cleanups[0:i]
- else:
- gcc.permerror(location, 'destructor call with unknown argument')
-
- # Check whether ARG is the current master cleanup. Return True if
- # all is well.
- def verify(self, location, arg):
- log('verify %s' % arg, 4)
- return (len(self.cleanups) > 0
- and not isinstance(self.cleanups[0], Dummy)
- and self.compare_vars(self.cleanups[0].var, arg))
-
- # Check whether SELF is empty.
- def isempty(self):
- log('isempty: len = %d' % len(self.cleanups), 4)
- return len(self.cleanups) == 0
-
- # Emit informational warnings about the cleanup stack.
- def inform(self):
- for item in reversed(self.cleanups):
- gcc.inform(item.location, 'leaked cleanup')
-
-class CleanupChecker:
- def __init__(self, fun):
- self.fun = fun
- self.seen_edges = set()
- self.bad_returns = set()
-
- # This maps BB indices to a list of master cleanups for the
- # BB.
- self.master_cleanups = {}
-
- # Pick a reasonable location for the basic block BB.
- def guess_bb_location(self, bb):
- if isinstance(bb.gimple, list):
- for stmt in bb.gimple:
- if stmt.loc:
- return stmt.loc
- return self.fun.end
-
- # Compute the master cleanup list for BB.
- # Modifies MASTER_CLEANUP in place.
- def compute_master(self, bb, bb_from, master_cleanup):
- if not isinstance(bb.gimple, list):
- return
- curloc = self.fun.end
- for stmt in bb.gimple:
- if stmt.loc:
- curloc = stmt.loc
- if isinstance(stmt, gcc.GimpleCall) and stmt.fndecl:
- if is_constructor(stmt.fndecl):
- log('saw constructor %s in bb=%d' % (str(stmt.fndecl), bb.index), 2)
- self.cleanup_aware = True
- master_cleanup.push(curloc, stmt.lhs)
- elif is_destructor(stmt.fndecl):
- if str(stmt.fndecl.name) != 'do_cleanups':
- self.only_do_cleanups_seen = False
- log('saw destructor %s in bb=%d, bb_from=%d, argument=%s'
- % (str(stmt.fndecl.name), bb.index, bb_from, str(stmt.args[0])),
- 2)
- master_cleanup.pop(curloc, stmt.args[0])
- elif needs_special_treatment(stmt.fndecl):
- pass
- # gcc.permerror(curloc, 'function needs special treatment')
- elif isinstance(stmt, gcc.GimpleAssign):
- if isinstance(stmt.lhs, gcc.VarDecl) and isinstance(stmt.rhs[0], gcc.VarDecl):
- master_cleanup.note_assignment(stmt.lhs, stmt.rhs[0])
- elif isinstance(stmt, gcc.GimpleReturn):
- if self.is_constructor:
- if not master_cleanup.verify(curloc, stmt.retval):
- gcc.permerror(curloc,
- 'constructor does not return master cleanup')
- elif not self.is_special_constructor:
- if not master_cleanup.isempty():
- if curloc not in self.bad_returns:
- gcc.permerror(curloc, 'cleanup stack is not empty at return')
- self.bad_returns.add(curloc)
- master_cleanup.inform()
-
- # Traverse a basic block, updating the master cleanup information
- # and propagating to other blocks.
- def traverse_bbs(self, edge, bb, bb_from, entry_master):
- log('traverse_bbs %d from %d' % (bb.index, bb_from), 1)
-
- # Propagate the entry MasterCleanup though this block.
- master_cleanup = MasterCleanup(entry_master)
- self.compute_master(bb, bb_from, master_cleanup)
-
- modified = False
- if bb.index in self.master_cleanups:
- # Merge the newly-computed MasterCleanup into the one we
- # have already computed. If this resulted in a
- # significant change, then we need to re-propagate.
- modified = self.master_cleanups[bb.index].merge(master_cleanup)
- else:
- self.master_cleanups[bb.index] = master_cleanup
- modified = True
-
- # EDGE is None for the entry BB.
- if edge is not None:
- # If merging cleanups caused a change, check to see if we
- # have a bad loop.
- if edge in self.seen_edges:
- # This error doesn't really help.
- # if modified:
- # gcc.permerror(self.guess_bb_location(bb),
- # 'invalid cleanup use in loop')
- return
- self.seen_edges.add(edge)
-
- if not modified:
- return
-
- # Now propagate to successor nodes.
- for edge in bb.succs:
- self.traverse_bbs(edge, edge.dest, bb.index, master_cleanup)
-
- def check_cleanups(self):
- if not self.fun.cfg or not self.fun.decl:
- return 'ignored'
- if is_destructor(self.fun.decl):
- return 'destructor'
- if needs_special_treatment(self.fun.decl):
- return 'special'
-
- self.is_constructor = is_constructor(self.fun.decl)
- self.is_special_constructor = not self.is_constructor and str(self.fun.decl.name).find('with_cleanup') > -1
- # Yuck.
- if str(self.fun.decl.name) == 'gdb_xml_create_parser_and_cleanup_1':
- self.is_special_constructor = True
-
- if self.is_special_constructor:
- gcc.inform(self.fun.start, 'function %s is a special constructor' % (self.fun.decl.name))
-
- # If we only see do_cleanups calls, and this function is not
- # itself a constructor, then we can convert it easily to RAII.
- self.only_do_cleanups_seen = not self.is_constructor
- # If we ever call a constructor, then we are "cleanup-aware".
- self.cleanup_aware = False
-
- entry_bb = self.fun.cfg.entry
- master_cleanup = MasterCleanup()
- self.traverse_bbs(None, entry_bb, -1, master_cleanup)
- if want_raii_info and self.only_do_cleanups_seen and self.cleanup_aware:
- gcc.inform(self.fun.decl.location,
- 'function %s could be converted to RAII' % (self.fun.decl.name))
- if self.is_constructor:
- return 'constructor'
- return 'OK'
-
-class CheckerPass(gcc.GimplePass):
- def execute(self, fun):
- if fun.decl:
- log("Starting " + fun.decl.name)
- if show_cfg:
- dot = gccutils.cfg_to_dot(fun.cfg, fun.decl.name)
- gccutils.invoke_dot(dot, name=fun.decl.name)
- checker = CleanupChecker(fun)
- what = checker.check_cleanups()
- if fun.decl:
- log(fun.decl.name + ': ' + what, 2)
-
-ps = CheckerPass(name = 'check-cleanups')
-# We need the cfg, but we want a relatively high-level Gimple.
-ps.register_after('cfg')
+++ /dev/null
-# Copyright 2011-2019 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/>.
-
-# This is a GCC plugin that computes some exception-handling data for
-# gdb. This data can then be summarized and checked by the
-# exsummary.py script.
-
-# To use:
-# * First, install the GCC Python plugin. See
-# https://fedorahosted.org/gcc-python-plugin/
-# * export PYTHON_PLUGIN=/full/path/to/plugin/directory
-# This should be the directory holding "python.so".
-# * cd build/gdb; make mostlyclean
-# * make CC=.../gcc-with-excheck
-# This will write a number of .py files in the build directory.
-# * python .../exsummary.py
-# This will show the violations.
-
-import gcc
-import gccutils
-import sys
-
-# Where our output goes.
-output_file = None
-
-# Cleanup functions require special treatment, because they take a
-# function argument, but in theory the function must be nothrow.
-cleanup_functions = {
- 'make_cleanup': 1,
- 'make_cleanup_dtor': 1,
- 'make_final_cleanup': 1,
- 'make_my_cleanup2': 1,
- 'make_my_cleanup': 1
-}
-
-# Functions which may throw but which we want to ignore.
-ignore_functions = {
- # This one is super special.
- 'exceptions_state_mc': 1,
- # gdb generally pretends that internal_error cannot throw, even
- # though it can.
- 'internal_error': 1,
- # do_cleanups and friends are supposedly nothrow but we don't want
- # to run afoul of the indirect function call logic.
- 'do_cleanups': 1,
- 'do_final_cleanups': 1
-}
-
-# Functions which take a function argument, but which are not
-# interesting, usually because the argument is not called in the
-# current context.
-non_passthrough_functions = {
- 'signal': 1,
- 'add_internal_function': 1
-}
-
-# Return True if the type is from Python.
-def type_is_pythonic(t):
- if isinstance(t, gcc.ArrayType):
- t = t.type
- if not isinstance(t, gcc.RecordType):
- return False
- # Hack.
- return str(t).find('struct Py') == 0
-
-# Examine all the fields of a struct. We don't currently need any
-# sort of recursion, so this is simple for now.
-def examine_struct_fields(initializer):
- global output_file
- for idx2, value2 in initializer.elements:
- if isinstance(idx2, gcc.Declaration):
- if isinstance(value2, gcc.AddrExpr):
- value2 = value2.operand
- if isinstance(value2, gcc.FunctionDecl):
- output_file.write("declare_nothrow(%s)\n"
- % repr(str(value2.name)))
-
-# Examine all global variables looking for pointers to functions in
-# structures whose types were defined by Python.
-def examine_globals():
- global output_file
- vars = gcc.get_variables()
- for var in vars:
- if not isinstance(var.decl, gcc.VarDecl):
- continue
- output_file.write("################\n")
- output_file.write("# Analysis for %s\n" % var.decl.name)
- if not var.decl.initial:
- continue
- if not type_is_pythonic(var.decl.type):
- continue
-
- if isinstance(var.decl.type, gcc.ArrayType):
- for idx, value in var.decl.initial.elements:
- examine_struct_fields(value)
- else:
- gccutils.check_isinstance(var.decl.type, gcc.RecordType)
- examine_struct_fields(var.decl.initial)
-
-# Called at the end of compilation to write out some data derived from
-# globals and to close the output.
-def close_output(*args):
- global output_file
- examine_globals()
- output_file.close()
-
-# The pass which derives some exception-checking information. We take
-# a two-step approach: first we get a call graph from the compiler.
-# This is emitted by the plugin as Python code. Then, we run a second
-# program that reads all the generated Python and uses it to get a
-# global view of exception routes in gdb.
-class GdbExceptionChecker(gcc.GimplePass):
- def __init__(self, output_file):
- gcc.GimplePass.__init__(self, 'gdb_exception_checker')
- self.output_file = output_file
-
- def log(self, obj):
- self.output_file.write("# %s\n" % str(obj))
-
- # Return true if FN is a call to a method on a Python object.
- # We know these cannot throw in the gdb sense.
- def fn_is_python_ignorable(self, fn):
- if not isinstance(fn, gcc.SsaName):
- return False
- stmt = fn.def_stmt
- if not isinstance(stmt, gcc.GimpleAssign):
- return False
- if stmt.exprcode is not gcc.ComponentRef:
- return False
- rhs = stmt.rhs[0]
- if not isinstance(rhs, gcc.ComponentRef):
- return False
- if not isinstance(rhs.field, gcc.FieldDecl):
- return False
- return rhs.field.name == 'tp_dealloc' or rhs.field.name == 'tp_free'
-
- # Decode a function call and write something to the output.
- # THIS_FUN is the enclosing function that we are processing.
- # FNDECL is the call to process; it might not actually be a DECL
- # node.
- # LOC is the location of the call.
- def handle_one_fndecl(self, this_fun, fndecl, loc):
- callee_name = ''
- if isinstance(fndecl, gcc.AddrExpr):
- fndecl = fndecl.operand
- if isinstance(fndecl, gcc.FunctionDecl):
- # Ordinary call to a named function.
- callee_name = str(fndecl.name)
- self.output_file.write("function_call(%s, %s, %s)\n"
- % (repr(callee_name),
- repr(this_fun.decl.name),
- repr(str(loc))))
- elif self.fn_is_python_ignorable(fndecl):
- # Call to tp_dealloc.
- pass
- elif (isinstance(fndecl, gcc.SsaName)
- and isinstance(fndecl.var, gcc.ParmDecl)):
- # We can ignore an indirect call via a parameter to the
- # current function, because this is handled via the rule
- # for passthrough functions.
- pass
- else:
- # Any other indirect call.
- self.output_file.write("has_indirect_call(%s, %s)\n"
- % (repr(this_fun.decl.name),
- repr(str(loc))))
- return callee_name
-
- # This does most of the work for examine_one_bb.
- # THIS_FUN is the enclosing function.
- # BB is the basic block to process.
- # Returns True if this block is the header of a TRY_CATCH, False
- # otherwise.
- def examine_one_bb_inner(self, this_fun, bb):
- if not bb.gimple:
- return False
- try_catch = False
- for stmt in bb.gimple:
- loc = stmt.loc
- if not loc:
- loc = this_fun.decl.location
- if not isinstance(stmt, gcc.GimpleCall):
- continue
- callee_name = self.handle_one_fndecl(this_fun, stmt.fn, loc)
-
- if callee_name == 'exceptions_state_mc_action_iter':
- try_catch = True
-
- global non_passthrough_functions
- if callee_name in non_passthrough_functions:
- continue
-
- # We have to specially handle calls where an argument to
- # the call is itself a function, e.g., qsort. In general
- # we model these as "passthrough" -- we assume that in
- # addition to the call the qsort there is also a call to
- # the argument function.
- for arg in stmt.args:
- # We are only interested in arguments which are functions.
- t = arg.type
- if isinstance(t, gcc.PointerType):
- t = t.dereference
- if not isinstance(t, gcc.FunctionType):
- continue
-
- if isinstance(arg, gcc.AddrExpr):
- arg = arg.operand
-
- global cleanup_functions
- if callee_name in cleanup_functions:
- if not isinstance(arg, gcc.FunctionDecl):
- gcc.inform(loc, 'cleanup argument not a DECL: %s' % repr(arg))
- else:
- # Cleanups must be nothrow.
- self.output_file.write("declare_cleanup(%s)\n"
- % repr(str(arg.name)))
- else:
- # Assume we have a passthrough function, like
- # qsort or an iterator. We model this by
- # pretending there is an ordinary call at this
- # point.
- self.handle_one_fndecl(this_fun, arg, loc)
- return try_catch
-
- # Examine all the calls in a basic block and generate output for
- # them.
- # THIS_FUN is the enclosing function.
- # BB is the basic block to examine.
- # BB_WORKLIST is a list of basic blocks to work on; we add the
- # appropriate successor blocks to this.
- # SEEN_BBS is a map whose keys are basic blocks we have already
- # processed. We use this to ensure that we only visit a given
- # block once.
- def examine_one_bb(self, this_fun, bb, bb_worklist, seen_bbs):
- try_catch = self.examine_one_bb_inner(this_fun, bb)
- for edge in bb.succs:
- if edge.dest in seen_bbs:
- continue
- seen_bbs[edge.dest] = 1
- if try_catch:
- # This is bogus, but we magically know the right
- # answer.
- if edge.false_value:
- bb_worklist.append(edge.dest)
- else:
- bb_worklist.append(edge.dest)
-
- # Iterate over all basic blocks in THIS_FUN.
- def iterate_bbs(self, this_fun):
- # Iteration must be in control-flow order, because if we see a
- # TRY_CATCH construct we need to drop all the contained blocks.
- bb_worklist = [this_fun.cfg.entry]
- seen_bbs = {}
- seen_bbs[this_fun.cfg.entry] = 1
- for bb in bb_worklist:
- self.examine_one_bb(this_fun, bb, bb_worklist, seen_bbs)
-
- def execute(self, fun):
- if fun and fun.cfg and fun.decl:
- self.output_file.write("################\n")
- self.output_file.write("# Analysis for %s\n" % fun.decl.name)
- self.output_file.write("define_function(%s, %s)\n"
- % (repr(fun.decl.name),
- repr(str(fun.decl.location))))
-
- global ignore_functions
- if fun.decl.name not in ignore_functions:
- self.iterate_bbs(fun)
-
-def main(**kwargs):
- global output_file
- output_file = open(gcc.get_dump_base_name() + '.gdb_exc.py', 'w')
- # We used to use attributes here, but there didn't seem to be a
- # big benefit over hard-coding.
- output_file.write('declare_throw("throw_exception")\n')
- output_file.write('declare_throw("throw_verror")\n')
- output_file.write('declare_throw("throw_vfatal")\n')
- output_file.write('declare_throw("throw_error")\n')
- gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, close_output)
- ps = GdbExceptionChecker(output_file)
- ps.register_after('ssa')
-
-main()
+++ /dev/null
-# Copyright 2011-2019 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)
+++ /dev/null
-#!/bin/sh
-# Copyright 2011-2019 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/>.
-
-# You must set PYTHON_PLUGIN in the environment.
-# It should be the directory holding the "python.so" file.
-# Usage: gcc-with-excheck [-Xx|-Xc] [--] ARGS
-# -Xx means to invoke the exception checker.
-# -Xc means to invoke the cleanup checker.
-# -- means stop processing -X options.
-# ARGS are passed to gcc.
-
-GCC=${GCC:-gcc}
-exdir=`dirname $0`
-
-pargs=
-while true; do
- case "$1" in
- -Xc)
- pargs="$pargs -fplugin-arg-python-script=$exdir/cleanup_check.py"
- ;;
- -Xx)
- pargs="$pargs -fplugin-arg-python-script=$exdir/excheck.py"
- ;;
- -X*)
- echo "unrecognized argument $1" 1>&2
- exit 1
- ;;
- --)
- shift
- break
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-# Recent versions of the Python plugin build two .so files in
-# different directories, so we have to set this. This will be fixed
-# upstream at some point.
-export LD_LIBRARY_PATH=$PYTHON_PLUGIN:$PYTHON_PLUGIN/gcc-c-api
-
-gcc -fplugin=$PYTHON_PLUGIN/python.so $pargs "$@"