Commit | Line | Data |
---|---|---|
618f726f | 1 | # Copyright 2011-2016 Free Software Foundation, Inc. |
c4a9e8b4 TT |
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 | import sys | |
18 | import glob | |
19 | ||
20 | # Compute the summary information from the files created by | |
21 | # excheck.py. Run in the build directory where you used the | |
22 | # excheck.py plugin. | |
23 | ||
24 | class Function: | |
25 | def __init__(self, name): | |
26 | self.name = name | |
27 | self.location = None | |
28 | self.callers = [] | |
29 | self.can_throw = False | |
30 | self.marked_nothrow = False | |
31 | self.reason = None | |
32 | ||
33 | def log(self, message): | |
34 | print "%s: note: %s" % (self.location, message) | |
35 | ||
36 | def set_location(self, location): | |
37 | self.location = location | |
38 | ||
39 | # CALLER is an Edge. | |
40 | def add_caller(self, caller): | |
41 | # self.log("adding call from %s" % caller.from_fn.name) | |
42 | self.callers.append(caller) | |
43 | # self.log("len = %d" % len(self.callers)) | |
44 | ||
45 | def consistency_check(self): | |
46 | if self.marked_nothrow and self.can_throw: | |
47 | print ("%s: error: %s marked as both 'throw' and 'nothrow'" | |
48 | % (self.location, self.name)) | |
49 | ||
50 | def declare_nothrow(self): | |
51 | self.marked_nothrow = True | |
52 | self.consistency_check() | |
53 | ||
54 | def declare_throw(self): | |
55 | result = not self.can_throw # Return True the first time | |
56 | self.can_throw = True | |
57 | self.consistency_check() | |
58 | return result | |
59 | ||
60 | def print_stack(self, is_indirect): | |
61 | if is_indirect: | |
62 | print ("%s: error: function %s is marked nothrow but is assumed to throw due to indirect call" | |
63 | % (self.location, self.name)) | |
64 | else: | |
65 | print ("%s: error: function %s is marked nothrow but can throw" | |
66 | % (self.location, self.name)) | |
67 | ||
68 | edge = self.reason | |
69 | while edge is not None: | |
70 | print ("%s: info: via call to %s" | |
71 | % (edge.location, edge.to_fn.name)) | |
72 | edge = edge.to_fn.reason | |
73 | ||
74 | def mark_throw(self, edge, work_list, is_indirect): | |
75 | if not self.can_throw: | |
76 | # self.log("can throw") | |
77 | self.can_throw = True | |
78 | self.reason = edge | |
79 | if self.marked_nothrow: | |
80 | self.print_stack(is_indirect) | |
81 | else: | |
82 | # Do this in the 'else' to avoid extra error | |
83 | # propagation. | |
84 | work_list.append(self) | |
85 | ||
86 | class Edge: | |
87 | def __init__(self, from_fn, to_fn, location): | |
88 | self.from_fn = from_fn | |
89 | self.to_fn = to_fn | |
90 | self.location = location | |
91 | ||
92 | # Work list of known-throwing functions. | |
93 | work_list = [] | |
94 | # Map from function name to Function object. | |
95 | function_map = {} | |
96 | # Work list of indirect calls. | |
97 | indirect_functions = [] | |
98 | # Whether we should process cleanup functions as well. | |
99 | process_cleanups = False | |
100 | # Whether we should process indirect function calls. | |
101 | process_indirect = False | |
102 | ||
103 | def declare(fn_name): | |
104 | global function_map | |
105 | if fn_name not in function_map: | |
106 | function_map[fn_name] = Function(fn_name) | |
107 | return function_map[fn_name] | |
108 | ||
109 | def define_function(fn_name, location): | |
110 | fn = declare(fn_name) | |
111 | fn.set_location(location) | |
112 | ||
113 | def declare_throw(fn_name): | |
114 | global work_list | |
115 | fn = declare(fn_name) | |
116 | if fn.declare_throw(): | |
117 | work_list.append(fn) | |
118 | ||
119 | def declare_nothrow(fn_name): | |
120 | fn = declare(fn_name) | |
121 | fn.declare_nothrow() | |
122 | ||
123 | def declare_cleanup(fn_name): | |
124 | global process_cleanups | |
125 | fn = declare(fn_name) | |
126 | if process_cleanups: | |
127 | fn.declare_nothrow() | |
128 | ||
129 | def function_call(to, frm, location): | |
130 | to_fn = declare(to) | |
131 | frm_fn = declare(frm) | |
132 | to_fn.add_caller(Edge(frm_fn, to_fn, location)) | |
133 | ||
134 | def has_indirect_call(fn_name, location): | |
135 | global indirect_functions | |
136 | fn = declare(fn_name) | |
137 | phony = Function("<indirect call>") | |
138 | phony.add_caller(Edge(fn, phony, location)) | |
139 | indirect_functions.append(phony) | |
140 | ||
141 | def mark_functions(worklist, is_indirect): | |
142 | for callee in worklist: | |
143 | for edge in callee.callers: | |
144 | edge.from_fn.mark_throw(edge, worklist, is_indirect) | |
145 | ||
146 | def help_and_exit(): | |
147 | print "Usage: exsummary [OPTION]..." | |
148 | print "" | |
149 | print "Read the .py files from the exception checker plugin and" | |
150 | print "generate an error summary." | |
151 | print "" | |
152 | print " --cleanups Include invalid behavior in cleanups" | |
153 | print " --indirect Include assumed errors due to indirect function calls" | |
154 | sys.exit(0) | |
155 | ||
156 | def main(): | |
157 | global work_list | |
158 | global indirect_functions | |
159 | global process_cleanups | |
160 | global process_indirect | |
161 | ||
162 | for arg in sys.argv: | |
163 | if arg == '--cleanups': | |
164 | process_cleanups = True | |
165 | elif arg == '--indirect': | |
166 | process_indirect = True | |
167 | elif arg == '--help': | |
168 | help_and_exit() | |
169 | ||
170 | for fname in sorted(glob.glob('*.c.gdb_exc.py')): | |
171 | execfile(fname) | |
172 | print "================" | |
173 | print "= Ordinary marking" | |
174 | print "================" | |
175 | mark_functions(work_list, False) | |
176 | if process_indirect: | |
177 | print "================" | |
178 | print "= Indirect marking" | |
179 | print "================" | |
180 | mark_functions(indirect_functions, True) | |
181 | return 0 | |
182 | ||
183 | if __name__ == '__main__': | |
184 | status = main() | |
185 | sys.exit(status) |