GDB (API)
|
00001 # Copyright 2011, 2013 Free Software Foundation, Inc. 00002 # 00003 # This is free software: you can redistribute it and/or modify it 00004 # under the terms of the GNU General Public License as published by 00005 # the Free Software Foundation, either version 3 of the License, or 00006 # (at your option) any later version. 00007 # 00008 # This program is distributed in the hope that it will be useful, but 00009 # WITHOUT ANY WARRANTY; without even the implied warranty of 00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 # General Public License for more details. 00012 # 00013 # You should have received a copy of the GNU General Public License 00014 # along with this program. If not, see 00015 # <http://www.gnu.org/licenses/>. 00016 00017 import sys 00018 import glob 00019 00020 # Compute the summary information from the files created by 00021 # excheck.py. Run in the build directory where you used the 00022 # excheck.py plugin. 00023 00024 class Function: 00025 def __init__(self, name): 00026 self.name = name 00027 self.location = None 00028 self.callers = [] 00029 self.can_throw = False 00030 self.marked_nothrow = False 00031 self.reason = None 00032 00033 def log(self, message): 00034 print "%s: note: %s" % (self.location, message) 00035 00036 def set_location(self, location): 00037 self.location = location 00038 00039 # CALLER is an Edge. 00040 def add_caller(self, caller): 00041 # self.log("adding call from %s" % caller.from_fn.name) 00042 self.callers.append(caller) 00043 # self.log("len = %d" % len(self.callers)) 00044 00045 def consistency_check(self): 00046 if self.marked_nothrow and self.can_throw: 00047 print ("%s: error: %s marked as both 'throw' and 'nothrow'" 00048 % (self.location, self.name)) 00049 00050 def declare_nothrow(self): 00051 self.marked_nothrow = True 00052 self.consistency_check() 00053 00054 def declare_throw(self): 00055 result = not self.can_throw # Return True the first time 00056 self.can_throw = True 00057 self.consistency_check() 00058 return result 00059 00060 def print_stack(self, is_indirect): 00061 if is_indirect: 00062 print ("%s: error: function %s is marked nothrow but is assumed to throw due to indirect call" 00063 % (self.location, self.name)) 00064 else: 00065 print ("%s: error: function %s is marked nothrow but can throw" 00066 % (self.location, self.name)) 00067 00068 edge = self.reason 00069 while edge is not None: 00070 print ("%s: info: via call to %s" 00071 % (edge.location, edge.to_fn.name)) 00072 edge = edge.to_fn.reason 00073 00074 def mark_throw(self, edge, work_list, is_indirect): 00075 if not self.can_throw: 00076 # self.log("can throw") 00077 self.can_throw = True 00078 self.reason = edge 00079 if self.marked_nothrow: 00080 self.print_stack(is_indirect) 00081 else: 00082 # Do this in the 'else' to avoid extra error 00083 # propagation. 00084 work_list.append(self) 00085 00086 class Edge: 00087 def __init__(self, from_fn, to_fn, location): 00088 self.from_fn = from_fn 00089 self.to_fn = to_fn 00090 self.location = location 00091 00092 # Work list of known-throwing functions. 00093 work_list = [] 00094 # Map from function name to Function object. 00095 function_map = {} 00096 # Work list of indirect calls. 00097 indirect_functions = [] 00098 # Whether we should process cleanup functions as well. 00099 process_cleanups = False 00100 # Whether we should process indirect function calls. 00101 process_indirect = False 00102 00103 def declare(fn_name): 00104 global function_map 00105 if fn_name not in function_map: 00106 function_map[fn_name] = Function(fn_name) 00107 return function_map[fn_name] 00108 00109 def define_function(fn_name, location): 00110 fn = declare(fn_name) 00111 fn.set_location(location) 00112 00113 def declare_throw(fn_name): 00114 global work_list 00115 fn = declare(fn_name) 00116 if fn.declare_throw(): 00117 work_list.append(fn) 00118 00119 def declare_nothrow(fn_name): 00120 fn = declare(fn_name) 00121 fn.declare_nothrow() 00122 00123 def declare_cleanup(fn_name): 00124 global process_cleanups 00125 fn = declare(fn_name) 00126 if process_cleanups: 00127 fn.declare_nothrow() 00128 00129 def function_call(to, frm, location): 00130 to_fn = declare(to) 00131 frm_fn = declare(frm) 00132 to_fn.add_caller(Edge(frm_fn, to_fn, location)) 00133 00134 def has_indirect_call(fn_name, location): 00135 global indirect_functions 00136 fn = declare(fn_name) 00137 phony = Function("<indirect call>") 00138 phony.add_caller(Edge(fn, phony, location)) 00139 indirect_functions.append(phony) 00140 00141 def mark_functions(worklist, is_indirect): 00142 for callee in worklist: 00143 for edge in callee.callers: 00144 edge.from_fn.mark_throw(edge, worklist, is_indirect) 00145 00146 def help_and_exit(): 00147 print "Usage: exsummary [OPTION]..." 00148 print "" 00149 print "Read the .py files from the exception checker plugin and" 00150 print "generate an error summary." 00151 print "" 00152 print " --cleanups Include invalid behavior in cleanups" 00153 print " --indirect Include assumed errors due to indirect function calls" 00154 sys.exit(0) 00155 00156 def main(): 00157 global work_list 00158 global indirect_functions 00159 global process_cleanups 00160 global process_indirect 00161 00162 for arg in sys.argv: 00163 if arg == '--cleanups': 00164 process_cleanups = True 00165 elif arg == '--indirect': 00166 process_indirect = True 00167 elif arg == '--help': 00168 help_and_exit() 00169 00170 for fname in sorted(glob.glob('*.c.gdb_exc.py')): 00171 execfile(fname) 00172 print "================" 00173 print "= Ordinary marking" 00174 print "================" 00175 mark_functions(work_list, False) 00176 if process_indirect: 00177 print "================" 00178 print "= Indirect marking" 00179 print "================" 00180 mark_functions(indirect_functions, True) 00181 return 0 00182 00183 if __name__ == '__main__': 00184 status = main() 00185 sys.exit(status)