Automatic date update in version.in
[platform/upstream/binutils.git] / gdb / contrib / exsummary.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 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)