gdb/contrib/cleanup_check.py - gdb
Global variables defined
Functions defined
Source code
- import gcc
- import gccutils
- import sys
- want_raii_info = False
- logging = False
- show_cfg = False
- def log(msg, indent=0):
- global logging
- if logging:
- sys.stderr.write('%s%s\n' % (' ' * indent, msg))
- sys.stderr.flush()
- def is_cleanup_type(return_type):
- if not isinstance(return_type, gcc.PointerType):
- return False
- if not isinstance(return_type.dereference, gcc.RecordType):
- return False
- if str(return_type.dereference.name) == 'cleanup':
- return True
- return False
- def is_constructor(decl):
- "Return True if the function DECL is a cleanup constructor; False otherwise"
- return is_cleanup_type(decl.type.type) and (not decl.name or str(decl.name) != 'make_final_cleanup')
- destructor_names = set(['do_cleanups', 'discard_cleanups'])
- def is_destructor(decl):
- return decl.name in destructor_names
- special_names = set(['do_final_cleanups', 'discard_final_cleanups',
- 'save_cleanups', 'save_final_cleanups',
- 'restore_cleanups', 'restore_final_cleanups',
- 'exceptions_state_mc_init',
- 'make_my_cleanup2', 'make_final_cleanup', 'all_cleanups',
- 'save_my_cleanups', 'quit_target'])
- def needs_special_treatment(decl):
- return decl.name in special_names
- class Dummy(object):
- def __init__(self, location):
- self.location = location
- class Cleanup(object):
- def __init__(self, var, location):
- self.var = var
- self.location = location
- class MasterCleanup(object):
-
-
- def __init__(self, other = None):
-
-
-
- if other is None:
- self.cleanups = []
- self.aliases = {}
- else:
- self.cleanups = other.cleanups[:]
- self.aliases = dict(other.aliases)
- def compare_vars(self, definition, argument):
- if definition == argument:
- return True
- if argument in self.aliases:
- argument = self.aliases[argument]
- if definition in self.aliases:
- definition = self.aliases[definition]
- return definition == argument
- def note_assignment(self, lhs, rhs):
- log('noting assignment %s = %s' % (lhs, rhs), 4)
- self.aliases[lhs] = rhs
-
-
- def merge(self, other):
-
-
- counter = -1
- found_named = False
- for counter in range(len(self.cleanups) - 1, -1, -1):
- var = self.cleanups[counter]
- log('merge checking %s' % var, 4)
-
- if isinstance(var, Dummy):
- log('=> merge dummy', 5)
- continue
-
- if other._find_var(var.var) >= 0:
- log ('=> merge found', 5)
- break
- log('=>merge not found', 5)
- found_named = True
- if found_named and counter < len(self.cleanups) - 1:
- log ('merging to %d' % counter, 4)
- if counter < 0:
- self.cleanups = []
- else:
- self.cleanups = self.cleanups[0:counter]
- return True
-
-
- if len(self.cleanups) == 0 and len(other.cleanups) > 0:
- log('merging non-empty other', 4)
- self.cleanups = other.cleanups[:]
- return True
- return False
-
-
-
- def push(self, location, lhs):
- if lhs is None:
- obj = Dummy(location)
- else:
- obj = Cleanup(lhs, location)
- log('pushing %s' % lhs, 4)
- idx = self._find_var(lhs)
- if idx >= 0:
- gcc.permerror(location, 'reassigning to known cleanup')
- gcc.inform(self.cleanups[idx].location,
- 'previous assignment is here')
- self.cleanups.append(obj)
-
-
- def _find_var(self, back_to):
- for i in range(len(self.cleanups) - 1, -1, -1):
- if isinstance(self.cleanups[i], Dummy):
- continue
- if self.compare_vars(self.cleanups[i].var, back_to):
- return i
- return -1
-
-
- def pop(self, location, back_to):
- log('pop:', 4)
- i = self._find_var(back_to)
- if i >= 0:
- self.cleanups = self.cleanups[0:i]
- else:
- gcc.permerror(location, 'destructor call with unknown argument')
-
-
- def verify(self, location, arg):
- log('verify %s' % arg, 4)
- return (len(self.cleanups) > 0
- and not isinstance(self.cleanups[0], Dummy)
- and self.compare_vars(self.cleanups[0].var, arg))
-
- def isempty(self):
- log('isempty: len = %d' % len(self.cleanups), 4)
- return len(self.cleanups) == 0
-
- def inform(self):
- for item in reversed(self.cleanups):
- gcc.inform(item.location, 'leaked cleanup')
- class CleanupChecker:
- def __init__(self, fun):
- self.fun = fun
- self.seen_edges = set()
- self.bad_returns = set()
-
-
- self.master_cleanups = {}
-
- def guess_bb_location(self, bb):
- if isinstance(bb.gimple, list):
- for stmt in bb.gimple:
- if stmt.loc:
- return stmt.loc
- return self.fun.end
-
-
- def compute_master(self, bb, bb_from, master_cleanup):
- if not isinstance(bb.gimple, list):
- return
- curloc = self.fun.end
- for stmt in bb.gimple:
- if stmt.loc:
- curloc = stmt.loc
- if isinstance(stmt, gcc.GimpleCall) and stmt.fndecl:
- if is_constructor(stmt.fndecl):
- log('saw constructor %s in bb=%d' % (str(stmt.fndecl), bb.index), 2)
- self.cleanup_aware = True
- master_cleanup.push(curloc, stmt.lhs)
- elif is_destructor(stmt.fndecl):
- if str(stmt.fndecl.name) != 'do_cleanups':
- self.only_do_cleanups_seen = False
- log('saw destructor %s in bb=%d, bb_from=%d, argument=%s'
- % (str(stmt.fndecl.name), bb.index, bb_from, str(stmt.args[0])),
- 2)
- master_cleanup.pop(curloc, stmt.args[0])
- elif needs_special_treatment(stmt.fndecl):
- pass
-
- elif isinstance(stmt, gcc.GimpleAssign):
- if isinstance(stmt.lhs, gcc.VarDecl) and isinstance(stmt.rhs[0], gcc.VarDecl):
- master_cleanup.note_assignment(stmt.lhs, stmt.rhs[0])
- elif isinstance(stmt, gcc.GimpleReturn):
- if self.is_constructor:
- if not master_cleanup.verify(curloc, stmt.retval):
- gcc.permerror(curloc,
- 'constructor does not return master cleanup')
- elif not self.is_special_constructor:
- if not master_cleanup.isempty():
- if curloc not in self.bad_returns:
- gcc.permerror(curloc, 'cleanup stack is not empty at return')
- self.bad_returns.add(curloc)
- master_cleanup.inform()
-
-
- def traverse_bbs(self, edge, bb, bb_from, entry_master):
- log('traverse_bbs %d from %d' % (bb.index, bb_from), 1)
-
- master_cleanup = MasterCleanup(entry_master)
- self.compute_master(bb, bb_from, master_cleanup)
- modified = False
- if bb.index in self.master_cleanups:
-
-
-
- modified = self.master_cleanups[bb.index].merge(master_cleanup)
- else:
- self.master_cleanups[bb.index] = master_cleanup
- modified = True
-
- if edge is not None:
-
-
- if edge in self.seen_edges:
-
-
-
-
- return
- self.seen_edges.add(edge)
- if not modified:
- return
-
- for edge in bb.succs:
- self.traverse_bbs(edge, edge.dest, bb.index, master_cleanup)
- def check_cleanups(self):
- if not self.fun.cfg or not self.fun.decl:
- return 'ignored'
- if is_destructor(self.fun.decl):
- return 'destructor'
- if needs_special_treatment(self.fun.decl):
- return 'special'
- self.is_constructor = is_constructor(self.fun.decl)
- self.is_special_constructor = not self.is_constructor and str(self.fun.decl.name).find('with_cleanup') > -1
-
- if str(self.fun.decl.name) == 'gdb_xml_create_parser_and_cleanup_1':
- self.is_special_constructor = True
- if self.is_special_constructor:
- gcc.inform(self.fun.start, 'function %s is a special constructor' % (self.fun.decl.name))
-
-
- self.only_do_cleanups_seen = not self.is_constructor
-
- self.cleanup_aware = False
- entry_bb = self.fun.cfg.entry
- master_cleanup = MasterCleanup()
- self.traverse_bbs(None, entry_bb, -1, master_cleanup)
- if want_raii_info and self.only_do_cleanups_seen and self.cleanup_aware:
- gcc.inform(self.fun.decl.location,
- 'function %s could be converted to RAII' % (self.fun.decl.name))
- if self.is_constructor:
- return 'constructor'
- return 'OK'
- class CheckerPass(gcc.GimplePass):
- def execute(self, fun):
- if fun.decl:
- log("Starting " + fun.decl.name)
- if show_cfg:
- dot = gccutils.cfg_to_dot(fun.cfg, fun.decl.name)
- gccutils.invoke_dot(dot, name=fun.decl.name)
- checker = CleanupChecker(fun)
- what = checker.check_cleanups()
- if fun.decl:
- log(fun.decl.name + ': ' + what, 2)
- ps = CheckerPass(name = 'check-cleanups')
- ps.register_after('cfg')