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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « gdb/contrib/cleanup_check.py ('k') | gdb/contrib/expect-read1.c » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2011, 2013 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()
OLDNEW
« 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