[pypy-svn] r73335 - in pypy/branch/cpython-extension/pypy: module/cpyext/test rpython/lltypesystem

xoraxax at codespeak.net xoraxax at codespeak.net
Sat Apr 3 16:28:28 CEST 2010


Author: xoraxax
Date: Sat Apr  3 16:28:27 2010
New Revision: 73335

Modified:
   pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py
   pypy/branch/cpython-extension/pypy/rpython/lltypesystem/lltype.py
Log:
Add way to detect whether there are unfreed raw allocations.

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py	Sat Apr  3 16:28:27 2010
@@ -4,7 +4,7 @@
 
 from pypy.conftest import gettestobjspace
 from pypy.interpreter.error import OperationError
-from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rpython.lltypesystem import rffi, lltype, ll2ctypes
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 from pypy.translator import platform
 from pypy.module.cpyext import api
@@ -26,6 +26,14 @@
         assert 'PyModule_Check' in api.FUNCTIONS
         assert api.FUNCTIONS['PyModule_Check'].argtypes == [api.PyObject]
 
+def set_difference(id_dict1, id_dict2):
+    d = id_dict1.copy()
+    for key in id_dict2.keys():
+        try:
+            del d[key]
+        except KeyError:
+            pass
+    return d
 
 class AppTestApi:
     def setup_class(cls):
@@ -141,7 +149,10 @@
         self.frozen_refcounts = {}
         for w_obj, obj in state.py_objects_w2r.iteritems():
             self.frozen_refcounts[w_obj] = obj.c_ob_refcnt
-        state.print_refcounts()
+        #state.print_refcounts()
+        self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values())
+        self.frozen_lltallocations = lltype.ALLOCATED.copy()
+        lltype.TRACK_ALLOCATIONS = True
 
     def check_and_print_leaks(self):
         # check for sane refcnts
@@ -179,6 +190,16 @@
         for w_obj in lost_objects_w:
             print >>sys.stderr, "Lost object %r" % (w_obj, )
             leaking = True
+        for llvalue in set(ll2ctypes.ALLOCATED.values()) - self.frozen_ll2callocations:
+            if getattr(llvalue, "_traceback", None): # this means that the allocation should be tracked
+                leaking = True
+                print >>sys.stderr, "Did not deallocate %r (ll2ctypes)" % (llvalue, )
+                print >>sys.stderr, "\t" + "\n\t".join(llvalue._traceback.splitlines())
+        for llvalue in set_difference(lltype.ALLOCATED, self.frozen_lltallocations).keys():
+            leaking = True
+            print >>sys.stderr, "Did not deallocate %r (llvalue)" % (llvalue, )
+            print >>sys.stderr, "\t" + "\n\t".join(llvalue._traceback.splitlines())
+
         return leaking
 
 

Modified: pypy/branch/cpython-extension/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/branch/cpython-extension/pypy/rpython/lltypesystem/lltype.py	Sat Apr  3 16:28:27 2010
@@ -1,3 +1,7 @@
+import StringIO
+import traceback
+import sys
+
 import py
 from pypy.rlib.rarithmetic import r_int, r_uint, intmask, r_singlefloat
 from pypy.rlib.rarithmetic import r_ulonglong, r_longlong, base_int
@@ -14,6 +18,7 @@
 log = py.log.Producer('lltype')
 
 TLS = tlsobject()
+TRACK_ALLOCATIONS = False
 
 class _uninitialized(object):
     def __init__(self, TYPE):
@@ -1315,27 +1320,49 @@
     def _was_freed(self):
         return False
 
+ALLOCATED = identity_dict()
+
 class _parentable(_container):
     _kind = "?"
 
     __slots__ = ('_TYPE',
                  '_parent_type', '_parent_index', '_keepparent',
                  '_wrparent',
-                 '__weakref__',
-                 '_storage')
+                 '__weakref__', '_traceback',
+                 '__storage')
 
-    def __init__(self, TYPE):
+    def __init__(self, TYPE, track_allocation=None):
         self._wrparent = None
         self._TYPE = TYPE
         self._storage = True    # means "use default storage", as opposed to:
                                 #    None            - container was freed
                                 #    <ctypes object> - using ctypes
                                 #                      (see ll2ctypes.py)
+        if track_allocation is not False and TRACK_ALLOCATIONS:
+            self._traceback = self._get_traceback()
+            ALLOCATED[self] = None
+        else:
+            self._traceback = None
+
+    def _get_traceback(self):
+        frame = sys._getframe().f_back.f_back.f_back.f_back
+        sio = StringIO.StringIO()
+        traceback.print_stack(frame, file=sio)
+        return sio.getvalue()
 
     def _free(self):
         self._check()   # no double-frees
         self._storage = None
 
+    def _storage_get(self):
+        return self.__storage
+
+    def _storage_set(self, value):
+        self.__storage = value
+        if value is not True and self in ALLOCATED:
+            del ALLOCATED[self]
+    _storage = property(_storage_get, _storage_set)
+
     def _was_freed(self):
         if self._storage is None:
             return True
@@ -1414,12 +1441,12 @@
 
     __slots__ = ('_hash_cache_', '_compilation_info')
 
-    def __new__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None):
+    def __new__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None, track_allocation=None):
         my_variety = _struct_variety(TYPE._names)
         return object.__new__(my_variety)
 
-    def __init__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None):
-        _parentable.__init__(self, TYPE)
+    def __init__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None, track_allocation=None):
+        _parentable.__init__(self, TYPE, track_allocation)
         if n is not None and TYPE._arrayfld is None:
             raise TypeError("%r is not variable-sized" % (TYPE,))
         if n is None and TYPE._arrayfld is not None:
@@ -1488,12 +1515,12 @@
 
     __slots__ = ('items',)
 
-    def __init__(self, TYPE, n, initialization=None, parent=None, parentindex=None):
+    def __init__(self, TYPE, n, initialization=None, parent=None, parentindex=None, track_allocation=None):
         if not isinstance(n, int):
             raise TypeError, "array length must be an int"
         if n < 0:
             raise ValueError, "negative array length"
-        _parentable.__init__(self, TYPE)
+        _parentable.__init__(self, TYPE, track_allocation)
         try:
             myrange = range(n)
         except OverflowError:
@@ -1790,9 +1817,9 @@
     else:
         initialization = 'malloc'
     if isinstance(T, Struct):
-        o = _struct(T, n, initialization=initialization)
+        o = _struct(T, n, initialization=initialization, track_allocation=flavor == "raw" and not immortal)
     elif isinstance(T, Array):
-        o = _array(T, n, initialization=initialization)
+        o = _array(T, n, initialization=initialization, track_allocation=flavor == "raw" and not immortal)
     elif isinstance(T, OpaqueType):
         assert n is None
         o = _opaque(T, initialization=initialization)



More information about the Pypy-commit mailing list