tizen 2.4 release
[external/binutils.git] / gdb / contrib / excheck.py
1 #   Copyright 2011-2014 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
17 # This is a GCC plugin that computes some exception-handling data for
18 # gdb.  This data can then be summarized and checked by the
19 # exsummary.py script.
20
21 # To use:
22 # * First, install the GCC Python plugin.  See
23 #   https://fedorahosted.org/gcc-python-plugin/
24 # * export PYTHON_PLUGIN=/full/path/to/plugin/directory
25 #   This should be the directory holding "python.so".
26 # * cd build/gdb; make mostlyclean
27 # * make CC=.../gcc-with-excheck
28 #   This will write a number of .py files in the build directory.
29 # * python .../exsummary.py
30 #   This will show the violations.
31
32 import gcc
33 import gccutils
34 import sys
35
36 # Where our output goes.
37 output_file = None
38
39 # Cleanup functions require special treatment, because they take a
40 # function argument, but in theory the function must be nothrow.
41 cleanup_functions = {
42     'make_cleanup': 1,
43     'make_cleanup_dtor': 1,
44     'make_final_cleanup': 1,
45     'make_my_cleanup2': 1,
46     'make_my_cleanup': 1
47 }
48
49 # Functions which may throw but which we want to ignore.
50 ignore_functions = {
51     # This one is super special.
52     'exceptions_state_mc': 1,
53     # gdb generally pretends that internal_error cannot throw, even
54     # though it can.
55     'internal_error': 1,
56     # do_cleanups and friends are supposedly nothrow but we don't want
57     # to run afoul of the indirect function call logic.
58     'do_cleanups': 1,
59     'do_final_cleanups': 1
60 }
61
62 # Functions which take a function argument, but which are not
63 # interesting, usually because the argument is not called in the
64 # current context.
65 non_passthrough_functions = {
66     'signal': 1,
67     'add_internal_function': 1
68 }
69
70 # Return True if the type is from Python.
71 def type_is_pythonic(t):
72     if isinstance(t, gcc.ArrayType):
73         t = t.type
74     if not isinstance(t, gcc.RecordType):
75         return False
76     # Hack.
77     return str(t).find('struct Py') == 0
78
79 # Examine all the fields of a struct.  We don't currently need any
80 # sort of recursion, so this is simple for now.
81 def examine_struct_fields(initializer):
82     global output_file
83     for idx2, value2 in initializer.elements:
84         if isinstance(idx2, gcc.Declaration):
85             if isinstance(value2, gcc.AddrExpr):
86                 value2 = value2.operand
87                 if isinstance(value2, gcc.FunctionDecl):
88                     output_file.write("declare_nothrow(%s)\n"
89                                       % repr(str(value2.name)))
90
91 # Examine all global variables looking for pointers to functions in
92 # structures whose types were defined by Python.
93 def examine_globals():
94     global output_file
95     vars = gcc.get_variables()
96     for var in vars:
97         if not isinstance(var.decl, gcc.VarDecl):
98             continue
99         output_file.write("################\n")
100         output_file.write("# Analysis for %s\n" % var.decl.name)
101         if not var.decl.initial:
102             continue
103         if not type_is_pythonic(var.decl.type):
104             continue
105
106         if isinstance(var.decl.type, gcc.ArrayType):
107             for idx, value in var.decl.initial.elements:
108                 examine_struct_fields(value)
109         else:
110             gccutils.check_isinstance(var.decl.type, gcc.RecordType)
111             examine_struct_fields(var.decl.initial)
112
113 # Called at the end of compilation to write out some data derived from
114 # globals and to close the output.
115 def close_output(*args):
116     global output_file
117     examine_globals()
118     output_file.close()
119
120 # The pass which derives some exception-checking information.  We take
121 # a two-step approach: first we get a call graph from the compiler.
122 # This is emitted by the plugin as Python code.  Then, we run a second
123 # program that reads all the generated Python and uses it to get a
124 # global view of exception routes in gdb.
125 class GdbExceptionChecker(gcc.GimplePass):
126     def __init__(self, output_file):
127         gcc.GimplePass.__init__(self, 'gdb_exception_checker')
128         self.output_file = output_file
129
130     def log(self, obj):
131         self.output_file.write("# %s\n" % str(obj))
132
133     # Return true if FN is a call to a method on a Python object.
134     # We know these cannot throw in the gdb sense.
135     def fn_is_python_ignorable(self, fn):
136         if not isinstance(fn, gcc.SsaName):
137             return False
138         stmt = fn.def_stmt
139         if not isinstance(stmt, gcc.GimpleAssign):
140             return False
141         if stmt.exprcode is not gcc.ComponentRef:
142             return False
143         rhs = stmt.rhs[0]
144         if not isinstance(rhs, gcc.ComponentRef):
145             return False
146         if not isinstance(rhs.field, gcc.FieldDecl):
147             return False
148         return rhs.field.name == 'tp_dealloc' or rhs.field.name == 'tp_free'
149
150     # Decode a function call and write something to the output.
151     # THIS_FUN is the enclosing function that we are processing.
152     # FNDECL is the call to process; it might not actually be a DECL
153     # node.
154     # LOC is the location of the call.
155     def handle_one_fndecl(self, this_fun, fndecl, loc):
156         callee_name = ''
157         if isinstance(fndecl, gcc.AddrExpr):
158             fndecl = fndecl.operand
159         if isinstance(fndecl, gcc.FunctionDecl):
160             # Ordinary call to a named function.
161             callee_name = str(fndecl.name)
162             self.output_file.write("function_call(%s, %s, %s)\n"
163                                    % (repr(callee_name),
164                                       repr(this_fun.decl.name),
165                                       repr(str(loc))))
166         elif self.fn_is_python_ignorable(fndecl):
167             # Call to tp_dealloc.
168             pass
169         elif (isinstance(fndecl, gcc.SsaName)
170               and isinstance(fndecl.var, gcc.ParmDecl)):
171             # We can ignore an indirect call via a parameter to the
172             # current function, because this is handled via the rule
173             # for passthrough functions.
174             pass
175         else:
176             # Any other indirect call.
177             self.output_file.write("has_indirect_call(%s, %s)\n"
178                                    % (repr(this_fun.decl.name),
179                                       repr(str(loc))))
180         return callee_name
181
182     # This does most of the work for examine_one_bb.
183     # THIS_FUN is the enclosing function.
184     # BB is the basic block to process.
185     # Returns True if this block is the header of a TRY_CATCH, False
186     # otherwise.
187     def examine_one_bb_inner(self, this_fun, bb):
188         if not bb.gimple:
189             return False
190         try_catch = False
191         for stmt in bb.gimple:
192             loc = stmt.loc
193             if not loc:
194                 loc = this_fun.decl.location
195             if not isinstance(stmt, gcc.GimpleCall):
196                 continue
197             callee_name = self.handle_one_fndecl(this_fun, stmt.fn, loc)
198
199             if callee_name == 'exceptions_state_mc_action_iter':
200                 try_catch = True
201
202             global non_passthrough_functions
203             if callee_name in non_passthrough_functions:
204                 continue
205
206             # We have to specially handle calls where an argument to
207             # the call is itself a function, e.g., qsort.  In general
208             # we model these as "passthrough" -- we assume that in
209             # addition to the call the qsort there is also a call to
210             # the argument function.
211             for arg in stmt.args:
212                 # We are only interested in arguments which are functions.
213                 t = arg.type
214                 if isinstance(t, gcc.PointerType):
215                     t = t.dereference
216                 if not isinstance(t, gcc.FunctionType):
217                     continue
218
219                 if isinstance(arg, gcc.AddrExpr):
220                     arg = arg.operand
221
222                 global cleanup_functions
223                 if callee_name in cleanup_functions:
224                     if not isinstance(arg, gcc.FunctionDecl):
225                         gcc.inform(loc, 'cleanup argument not a DECL: %s' % repr(arg))
226                     else:
227                         # Cleanups must be nothrow.
228                         self.output_file.write("declare_cleanup(%s)\n"
229                                                % repr(str(arg.name)))
230                 else:
231                     # Assume we have a passthrough function, like
232                     # qsort or an iterator.  We model this by
233                     # pretending there is an ordinary call at this
234                     # point.
235                     self.handle_one_fndecl(this_fun, arg, loc)
236         return try_catch
237
238     # Examine all the calls in a basic block and generate output for
239     # them.
240     # THIS_FUN is the enclosing function.
241     # BB is the basic block to examine.
242     # BB_WORKLIST is a list of basic blocks to work on; we add the
243     # appropriate successor blocks to this.
244     # SEEN_BBS is a map whose keys are basic blocks we have already
245     # processed.  We use this to ensure that we only visit a given
246     # block once.
247     def examine_one_bb(self, this_fun, bb, bb_worklist, seen_bbs):
248         try_catch = self.examine_one_bb_inner(this_fun, bb)
249         for edge in bb.succs:
250             if edge.dest in seen_bbs:
251                 continue
252             seen_bbs[edge.dest] = 1
253             if try_catch:
254                 # This is bogus, but we magically know the right
255                 # answer.
256                 if edge.false_value:
257                     bb_worklist.append(edge.dest)
258             else:
259                 bb_worklist.append(edge.dest)
260
261     # Iterate over all basic blocks in THIS_FUN.
262     def iterate_bbs(self, this_fun):
263         # Iteration must be in control-flow order, because if we see a
264         # TRY_CATCH construct we need to drop all the contained blocks.
265         bb_worklist = [this_fun.cfg.entry]
266         seen_bbs = {}
267         seen_bbs[this_fun.cfg.entry] = 1
268         for bb in bb_worklist:
269             self.examine_one_bb(this_fun, bb, bb_worklist, seen_bbs)
270
271     def execute(self, fun):
272         if fun and fun.cfg and fun.decl:
273             self.output_file.write("################\n")
274             self.output_file.write("# Analysis for %s\n" % fun.decl.name)
275             self.output_file.write("define_function(%s, %s)\n"
276                                    % (repr(fun.decl.name),
277                                       repr(str(fun.decl.location))))
278
279             global ignore_functions
280             if fun.decl.name not in ignore_functions:
281                 self.iterate_bbs(fun)
282
283 def main(**kwargs):
284     global output_file
285     output_file = open(gcc.get_dump_base_name() + '.gdb_exc.py', 'w')
286     # We used to use attributes here, but there didn't seem to be a
287     # big benefit over hard-coding.
288     output_file.write('declare_throw("throw_exception")\n')
289     output_file.write('declare_throw("throw_verror")\n')
290     output_file.write('declare_throw("throw_vfatal")\n')
291     output_file.write('declare_throw("throw_error")\n')
292     gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, close_output)
293     ps = GdbExceptionChecker(output_file)
294     ps.register_after('ssa')
295
296 main()