Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Unified Diff: gdb/contrib/excheck.py

Issue 124383005: GDB 7.6.50 (Closed) Base URL: http://git.chromium.org/native_client/nacl-gdb.git@upstream
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gdb/contrib/cleanup_check.py ('k') | gdb/contrib/expect-read1.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gdb/contrib/excheck.py
diff --git a/gdb/contrib/excheck.py b/gdb/contrib/excheck.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8f917fb1eb1082cda02cc5b3f53a9b373b76fa8
--- /dev/null
+++ b/gdb/contrib/excheck.py
@@ -0,0 +1,296 @@
+# 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/>.
+
+# 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()
« no previous file with comments | « gdb/contrib/cleanup_check.py ('k') | gdb/contrib/expect-read1.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698