[pypy-svn] r77979 - in pypy/branch/leak-finder/pypy: . annotation jit/backend/llsupport jit/backend/test jit/backend/x86 jit/backend/x86/test jit/metainterp jit/metainterp/test module/cpyext/test rlib/test rpython rpython/lltypesystem rpython/lltypesystem/test rpython/memory rpython/memory/gctransform rpython/test tool tool/test translator/c/test
arigo at codespeak.net
arigo at codespeak.net
Fri Oct 15 14:37:19 CEST 2010
Author: arigo
Date: Fri Oct 15 14:37:16 2010
New Revision: 77979
Added:
pypy/branch/leak-finder/pypy/tool/leakfinder.py
pypy/branch/leak-finder/pypy/tool/test/test_leakfinder.py
Modified:
pypy/branch/leak-finder/pypy/annotation/builtin.py
pypy/branch/leak-finder/pypy/conftest.py
pypy/branch/leak-finder/pypy/jit/backend/llsupport/gc.py
pypy/branch/leak-finder/pypy/jit/backend/llsupport/llmodel.py
pypy/branch/leak-finder/pypy/jit/backend/test/runner_test.py
pypy/branch/leak-finder/pypy/jit/backend/x86/assembler.py
pypy/branch/leak-finder/pypy/jit/backend/x86/regalloc.py
pypy/branch/leak-finder/pypy/jit/backend/x86/runner.py
pypy/branch/leak-finder/pypy/jit/backend/x86/test/test_assembler.py
pypy/branch/leak-finder/pypy/jit/metainterp/test/test_basic.py
pypy/branch/leak-finder/pypy/jit/metainterp/virtualref.py
pypy/branch/leak-finder/pypy/module/cpyext/test/test_cpyext.py
pypy/branch/leak-finder/pypy/rlib/test/test_rdynload.py
pypy/branch/leak-finder/pypy/rlib/test/test_rzlib.py
pypy/branch/leak-finder/pypy/rpython/llinterp.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/llmemory.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/lltype.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/rclass.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_llmemory.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_lltype.py
pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_rffi.py
pypy/branch/leak-finder/pypy/rpython/memory/gctransform/transform.py
pypy/branch/leak-finder/pypy/rpython/memory/gcwrapper.py
pypy/branch/leak-finder/pypy/rpython/memory/support.py
pypy/branch/leak-finder/pypy/rpython/rbuiltin.py
pypy/branch/leak-finder/pypy/rpython/test/test_llinterp.py
pypy/branch/leak-finder/pypy/rpython/test/test_nongc.py
pypy/branch/leak-finder/pypy/rpython/test/test_rptr.py
pypy/branch/leak-finder/pypy/translator/c/test/test_lltyped.py
Log:
In-progress: remove the 'self.mallocs' from llinterp, and instead
use systematically the one from lltype.py introduced around r73335.
Modified: pypy/branch/leak-finder/pypy/annotation/builtin.py
==============================================================================
--- pypy/branch/leak-finder/pypy/annotation/builtin.py (original)
+++ pypy/branch/leak-finder/pypy/annotation/builtin.py Fri Oct 15 14:37:16 2010
@@ -423,7 +423,7 @@
from pypy.annotation.model import SomePtr
from pypy.rpython.lltypesystem import lltype
-def malloc(s_T, s_n=None, s_flavor=None, s_zero=None):
+def malloc(s_T, s_n=None, s_flavor=None, s_zero=None, s_track_allocation=None):
assert (s_n is None or s_n.knowntype == int
or issubclass(s_n.knowntype, pypy.rlib.rarithmetic.base_int))
assert s_T.is_constant()
@@ -438,13 +438,15 @@
r = SomePtr(lltype.typeOf(p))
else:
assert s_flavor.is_constant()
+ assert s_track_allocation is None or s_track_allocation.is_constant()
# not sure how to call malloc() for the example 'p' in the
# presence of s_extraargs
r = SomePtr(lltype.Ptr(s_T.const))
return r
-def free(s_p, s_flavor):
+def free(s_p, s_flavor, s_track_allocation=None):
assert s_flavor.is_constant()
+ assert s_track_allocation is None or s_track_allocation.is_constant()
# same problem as in malloc(): some flavors are not easy to
# malloc-by-example
#T = s_p.ll_ptrtype.TO
Modified: pypy/branch/leak-finder/pypy/conftest.py
==============================================================================
--- pypy/branch/leak-finder/pypy/conftest.py (original)
+++ pypy/branch/leak-finder/pypy/conftest.py Fri Oct 15 14:37:16 2010
@@ -7,6 +7,7 @@
from inspect import isclass, getmro
from pypy.tool.udir import udir
from pypy.tool.autopath import pypydir
+from pypy.tool import leakfinder
# pytest settings
pytest_plugins = "resultlog",
@@ -354,7 +355,14 @@
def runtest(self):
try:
- super(IntTestFunction, self).runtest()
+ leakfinder.start_tracking_allocations()
+ try:
+ super(IntTestFunction, self).runtest()
+ finally:
+ if leakfinder.TRACK_ALLOCATIONS:
+ leaks = leakfinder.stop_tracking_allocations(False)
+ else:
+ leaks = None # stop_tracking_allocations() already called
except OperationError, e:
check_keyboard_interrupt(e)
raise
@@ -373,6 +381,8 @@
_pygame_imported = True
assert option.view, ("should not invoke Pygame "
"if conftest.option.view is False")
+ if leaks: # check for leaks, but only if the test passed so far
+ raise leakfinder.MallocMismatch(leaks)
class AppTestFunction(PyPyTestFunction):
def _prunetraceback(self, traceback):
Modified: pypy/branch/leak-finder/pypy/jit/backend/llsupport/gc.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/llsupport/gc.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/llsupport/gc.py Fri Oct 15 14:37:16 2010
@@ -158,7 +158,7 @@
# used to avoid too many duplications in the GCREF_LISTs.
self.hashtable = lltype.malloc(self.HASHTABLE,
self.HASHTABLE_SIZE+1,
- flavor='raw')
+ flavor='raw', track_allocation=False)
dummy = lltype.direct_ptradd(lltype.direct_arrayitems(self.hashtable),
self.HASHTABLE_SIZE)
dummy = llmemory.cast_ptr_to_adr(dummy)
@@ -252,14 +252,15 @@
def _enlarge_gcmap(self):
newlength = 250 + self._gcmap_maxlength * 2
- newgcmap = lltype.malloc(self.GCMAP_ARRAY, newlength, flavor='raw')
+ newgcmap = lltype.malloc(self.GCMAP_ARRAY, newlength, flavor='raw',
+ track_allocation=False)
oldgcmap = self._gcmap
for i in range(self._gcmap_curlength):
newgcmap[i] = oldgcmap[i]
self._gcmap = newgcmap
self._gcmap_maxlength = newlength
if oldgcmap:
- lltype.free(oldgcmap, flavor='raw')
+ lltype.free(oldgcmap, flavor='raw', track_allocation=False)
def get_basic_shape(self, is_64_bit=False):
# XXX: Should this code even really know about stack frame layout of
@@ -308,7 +309,8 @@
# them inside bigger arrays) and we never try to share them.
length = len(shape)
compressed = lltype.malloc(self.CALLSHAPE_ARRAY, length,
- flavor='raw')
+ flavor='raw',
+ track_allocation=False) # memory leak
for i in range(length):
compressed[length-1-i] = rffi.cast(rffi.UCHAR, shape[i])
return llmemory.cast_ptr_to_adr(compressed)
Modified: pypy/branch/leak-finder/pypy/jit/backend/llsupport/llmodel.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/llsupport/llmodel.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/llsupport/llmodel.py Fri Oct 15 14:37:16 2010
@@ -82,7 +82,8 @@
# read back by the machine code reading at the address given by
# pos_exception() and pos_exc_value().
_exception_emulator = lltype.malloc(rffi.CArray(lltype.Signed), 2,
- zero=True, flavor='raw')
+ zero=True, flavor='raw',
+ immortal=True)
self._exception_emulator = _exception_emulator
def _store_exception(lle):
Modified: pypy/branch/leak-finder/pypy/jit/backend/test/runner_test.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/test/runner_test.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/test/runner_test.py Fri Oct 15 14:37:16 2010
@@ -1304,6 +1304,7 @@
descr=fd)
res = self.execute_operation(get_op, [s_box], 'int', descr=fd)
assert res.getint() == 32
+ lltype.free(s, flavor='raw')
def test_new_with_vtable(self):
cpu = self.cpu
Modified: pypy/branch/leak-finder/pypy/jit/backend/x86/assembler.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/x86/assembler.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/x86/assembler.py Fri Oct 15 14:37:16 2010
@@ -249,7 +249,8 @@
def _build_float_constants(self):
# 44 bytes: 32 bytes for the data, and up to 12 bytes for alignment
- addr = lltype.malloc(rffi.CArray(lltype.Char), 44, flavor='raw')
+ addr = lltype.malloc(rffi.CArray(lltype.Char), 44, flavor='raw',
+ track_allocation=False)
if not we_are_translated():
self._keepalive_malloced_float_consts = addr
float_constants = rffi.cast(lltype.Signed, addr)
@@ -399,7 +400,8 @@
funcname = "<loop %d>" % len(self.loop_run_counters)
# invent the counter, so we don't get too confused
if self._debug:
- struct = lltype.malloc(DEBUG_COUNTER, flavor='raw')
+ struct = lltype.malloc(DEBUG_COUNTER, flavor='raw',
+ track_allocation=False) # known to leak
struct.i = 0
self.loop_run_counters.append((len(self.loop_run_counters), struct))
return funcname
Modified: pypy/branch/leak-finder/pypy/jit/backend/x86/regalloc.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/x86/regalloc.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/x86/regalloc.py Fri Oct 15 14:37:16 2010
@@ -70,8 +70,9 @@
def _get_new_array(self):
n = self.BASE_CONSTANT_SIZE
+ # known to leak
self.cur_array = lltype.malloc(rffi.CArray(lltype.Float), n,
- flavor='raw')
+ flavor='raw', track_allocation=False)
self.cur_array_free = n
_get_new_array._dont_inline_ = True
Modified: pypy/branch/leak-finder/pypy/jit/backend/x86/runner.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/x86/runner.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/x86/runner.py Fri Oct 15 14:37:16 2010
@@ -113,7 +113,8 @@
return CPU386.cast_adr_to_int(adr)
all_null_registers = lltype.malloc(rffi.LONGP.TO, 24,
- flavor='raw', zero=True)
+ flavor='raw', zero=True,
+ immortal=True)
def force(self, addr_of_force_index):
TP = rffi.CArrayPtr(lltype.Signed)
Modified: pypy/branch/leak-finder/pypy/jit/backend/x86/test/test_assembler.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/backend/x86/test/test_assembler.py (original)
+++ pypy/branch/leak-finder/pypy/jit/backend/x86/test/test_assembler.py Fri Oct 15 14:37:16 2010
@@ -81,7 +81,8 @@
# also test rebuild_faillocs_from_descr(), which should not
# reproduce the holes at all
- bytecode = lltype.malloc(rffi.UCHARP.TO, len(mc.content), flavor='raw')
+ bytecode = lltype.malloc(rffi.UCHARP.TO, len(mc.content), flavor='raw',
+ immortal=True)
for i in range(len(mc.content)):
assert 0 <= mc.content[i] <= 255
bytecode[i] = rffi.cast(rffi.UCHAR, mc.content[i])
@@ -115,7 +116,8 @@
assert withfloats
value = random.random() - 0.5
# make sure it fits into 64 bits
- tmp = lltype.malloc(rffi.LONGP.TO, 2, flavor='raw')
+ tmp = lltype.malloc(rffi.LONGP.TO, 2, flavor='raw',
+ track_allocation=False)
rffi.cast(rffi.DOUBLEP, tmp)[0] = value
return rffi.cast(rffi.DOUBLEP, tmp)[0], tmp[0], tmp[1]
@@ -152,10 +154,12 @@
# prepare the expected target arrays, the descr_bytecode,
# the 'registers' and the 'stack' arrays according to 'content'
- xmmregisters = lltype.malloc(rffi.LONGP.TO, 16+ACTUAL_CPU.NUM_REGS+1, flavor='raw')
+ xmmregisters = lltype.malloc(rffi.LONGP.TO, 16+ACTUAL_CPU.NUM_REGS+1,
+ flavor='raw', immortal=True)
registers = rffi.ptradd(xmmregisters, 16)
stacklen = baseloc + 10
- stack = lltype.malloc(rffi.LONGP.TO, stacklen, flavor='raw')
+ stack = lltype.malloc(rffi.LONGP.TO, stacklen, flavor='raw',
+ immortal=True)
expected_ints = [0] * len(content)
expected_ptrs = [lltype.nullptr(llmemory.GCREF.TO)] * len(content)
expected_floats = [0.0] * len(content)
@@ -221,7 +225,7 @@
descr_bytecode.append(0x00)
descr_bytecode.append(0xCC) # end marker
descr_bytes = lltype.malloc(rffi.UCHARP.TO, len(descr_bytecode),
- flavor='raw')
+ flavor='raw', immortal=True)
for i in range(len(descr_bytecode)):
assert 0 <= descr_bytecode[i] <= 255
descr_bytes[i] = rffi.cast(rffi.UCHAR, descr_bytecode[i])
Modified: pypy/branch/leak-finder/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/metainterp/test/test_basic.py (original)
+++ pypy/branch/leak-finder/pypy/jit/metainterp/test/test_basic.py Fri Oct 15 14:37:16 2010
@@ -1751,7 +1751,7 @@
c = bool(p1)
d = not bool(p2)
return 1000*a + 100*b + 10*c + d
- prebuilt = [lltype.malloc(TP, flavor='raw')] * 2
+ prebuilt = [lltype.malloc(TP, flavor='raw', immortal=True)] * 2
expected = f(0, 1)
assert self.interp_operations(f, [0, 1]) == expected
Modified: pypy/branch/leak-finder/pypy/jit/metainterp/virtualref.py
==============================================================================
--- pypy/branch/leak-finder/pypy/jit/metainterp/virtualref.py (original)
+++ pypy/branch/leak-finder/pypy/jit/metainterp/virtualref.py Fri Oct 15 14:37:16 2010
@@ -16,7 +16,8 @@
('virtualref_index', lltype.Signed),
('forced', rclass.OBJECTPTR))
self.jit_virtual_ref_vtable = lltype.malloc(rclass.OBJECT_VTABLE,
- zero=True, flavor='raw')
+ zero=True, flavor='raw',
+ immortal=True)
self.jit_virtual_ref_vtable.name = rclass.alloc_array_name(
'jit_virtual_ref')
# build some constants
Modified: pypy/branch/leak-finder/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/branch/leak-finder/pypy/module/cpyext/test/test_cpyext.py (original)
+++ pypy/branch/leak-finder/pypy/module/cpyext/test/test_cpyext.py Fri Oct 15 14:37:16 2010
@@ -126,17 +126,17 @@
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 lltype.ALLOCATED.keys():
+## for llvalue in set(ll2ctypes.ALLOCATED.values()) - self.frozen_ll2callocations:
+## if llvalue in lltype.ALLOCATED:
+## leaking = True
+## print >>sys.stderr, "Did not deallocate %r (ll2ctypes)" % (llvalue, )
+## print >>sys.stderr, "\t" + "\n\t".join(llvalue._traceback.splitlines())
+ for llvalue, traceback in lltype.ALLOCATED.items():
leaking = True
print >>sys.stderr, "Did not deallocate %r (llvalue)" % (llvalue, )
- print >>sys.stderr, "\t" + "\n\t".join(llvalue._traceback.splitlines())
+ print >>sys.stderr, "\t" + "\n\t".join(traceback.splitlines())
- lltype.stop_tracking_allocations()
+ lltype.stop_tracking_allocations(check=False)
return leaking
class AppTestCpythonExtensionBase(LeakCheckingTest):
Modified: pypy/branch/leak-finder/pypy/rlib/test/test_rdynload.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rlib/test/test_rdynload.py (original)
+++ pypy/branch/leak-finder/pypy/rlib/test/test_rdynload.py Fri Oct 15 14:37:16 2010
@@ -5,11 +5,18 @@
class TestDLOperations:
def test_dlopen(self):
- py.test.raises(DLOpenError, "dlopen(rffi.str2charp('xxxxxxxxxxxx'))")
- assert dlopen(rffi.str2charp(get_libc_name()))
+ s = rffi.str2charp('xxxxxxxxxxxx')
+ py.test.raises(DLOpenError, "dlopen(s)")
+ rffi.free_charp(s)
+ #
+ s = rffi.str2charp(get_libc_name())
+ assert dlopen(s)
+ rffi.free_charp(s)
def test_dlsym(self):
- lib = dlopen(rffi.str2charp(get_libc_name()))
+ s = rffi.str2charp(get_libc_name())
+ lib = dlopen(s)
+ rffi.free_charp(s)
handle = rffi.cast(lltype.Ptr(lltype.FuncType([lltype.Signed],
lltype.Signed)), dlsym(lib, 'abs'))
assert 1 == handle(1)
Modified: pypy/branch/leak-finder/pypy/rlib/test/test_rzlib.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rlib/test/test_rzlib.py (original)
+++ pypy/branch/leak-finder/pypy/rlib/test/test_rzlib.py Fri Oct 15 14:37:16 2010
@@ -189,6 +189,8 @@
assert unused3 == len('more_garbage')
assert data3 == ''
+ rzlib.deflateEnd(stream)
+
def test_decompress_max_length():
"""
@@ -205,6 +207,8 @@
assert finished2 is True
assert unused2 == 0
+ rzlib.deflateEnd(stream)
+
def test_cornercases():
"""
@@ -215,6 +219,7 @@
bytes += rzlib.compress(stream, "")
bytes += rzlib.compress(stream, "", rzlib.Z_FINISH)
assert zlib.decompress(bytes) == ""
+ rzlib.deflateEnd(stream)
stream = rzlib.inflateInit()
data, finished, unused = rzlib.decompress(stream, "")
@@ -228,3 +233,4 @@
assert finished is False
assert unused > 0
buf = buf[-unused:]
+ rzlib.deflateEnd(stream)
Modified: pypy/branch/leak-finder/pypy/rpython/llinterp.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/llinterp.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/llinterp.py Fri Oct 15 14:37:16 2010
@@ -48,8 +48,7 @@
current_interpreter = None
- def __init__(self, typer, tracing=True, exc_data_ptr=None,
- malloc_check=True):
+ def __init__(self, typer, tracing=True, exc_data_ptr=None):
self.bindings = {}
self.typer = typer
# 'heap' is module or object that provides malloc, etc for lltype ops
@@ -57,9 +56,7 @@
self.exc_data_ptr = exc_data_ptr
self.frame_stack = []
self.tracer = None
- self.malloc_check = malloc_check
self.frame_class = LLFrame
- self.mallocs = {}
if tracing:
self.tracer = Tracer()
@@ -163,24 +160,6 @@
return self.exc_data_ptr
return None
- def remember_malloc(self, ptr, llframe):
- # err....
- self.mallocs[ptr._obj] = llframe
-
- def remember_free(self, ptr):
- try:
- del self.mallocs[ptr._obj]
- except KeyError:
- self._rehash_mallocs()
- del self.mallocs[ptr._obj]
-
- def _rehash_mallocs(self):
- # rehashing is needed because some objects' hash may change
- # when being turned to <C object>
- items = self.mallocs.items()
- self.mallocs = {}
- self.mallocs.update(items)
-
def _store_exception(self, exc):
raise PleaseOverwriteStoreException("You just invoked ll2ctypes callback without overwriting _store_exception on llinterpreter")
@@ -726,13 +705,13 @@
def op_malloc(self, obj, flags):
flavor = flags['flavor']
zero = flags.get('zero', False)
+ track_allocation = flags.get('track_allocation', True)
if flavor == "stack":
result = self.heap.malloc(obj, zero=zero, flavor='raw')
self.alloca_objects.append(result)
return result
- ptr = self.heap.malloc(obj, zero=zero, flavor=flavor)
- if flavor == 'raw' and self.llinterpreter.malloc_check:
- self.llinterpreter.remember_malloc(ptr, self)
+ ptr = self.heap.malloc(obj, zero=zero, flavor=flavor,
+ track_allocation=track_allocation)
return ptr
def op_malloc_varsize(self, obj, flags, size):
@@ -741,8 +720,6 @@
assert flavor in ('gc', 'raw')
try:
ptr = self.heap.malloc(obj, size, zero=zero, flavor=flavor)
- if flavor == 'raw' and self.llinterpreter.malloc_check:
- self.llinterpreter.remember_malloc(ptr, self)
return ptr
except MemoryError:
self.make_llexception()
@@ -759,11 +736,10 @@
zero = flags.get('zero', False)
return self.heap.malloc_nonmovable(TYPE, size, zero=zero)
- def op_free(self, obj, flavor):
- assert isinstance(flavor, str)
- if flavor == 'raw' and self.llinterpreter.malloc_check:
- self.llinterpreter.remember_free(obj)
- self.heap.free(obj, flavor=flavor)
+ def op_free(self, obj, flags):
+ assert flags['flavor'] == 'raw'
+ track_allocation = flags.get('track_allocation', True)
+ self.heap.free(obj, flavor='raw', track_allocation=track_allocation)
def op_shrink_array(self, obj, smallersize):
return self.heap.shrink_array(obj, smallersize)
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/llmemory.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/llmemory.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/llmemory.py Fri Oct 15 14:37:16 2010
@@ -105,11 +105,13 @@
if (isinstance(self.TYPE, lltype.ContainerType)
and self.TYPE._gckind == 'gc'):
assert self.repeat == 1
- p = lltype.malloc(self.TYPE, flavor='raw', zero=zero)
+ p = lltype.malloc(self.TYPE, flavor='raw', zero=zero,
+ track_allocation=False)
return cast_ptr_to_adr(p)
else:
T = lltype.FixedSizeArray(self.TYPE, self.repeat)
- p = lltype.malloc(T, flavor='raw', zero=zero)
+ p = lltype.malloc(T, flavor='raw', zero=zero,
+ track_allocation=False)
array_adr = cast_ptr_to_adr(p)
return array_adr + ArrayItemsOffset(T)
@@ -288,7 +290,8 @@
count = 0
p = lltype.malloc(parenttype or self.TYPE, count,
immortal = self.TYPE._gckind == 'raw',
- zero = zero)
+ zero = zero,
+ track_allocation = False)
return cast_ptr_to_adr(p)
def raw_memcopy(self, srcadr, dstadr):
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/lltype.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/lltype.py Fri Oct 15 14:37:16 2010
@@ -1,7 +1,3 @@
-import StringIO
-import traceback
-import sys
-
import py
from pypy.rlib.rarithmetic import (r_int, r_uint, intmask, r_singlefloat,
r_ulonglong, r_longlong, base_int,
@@ -10,25 +6,13 @@
from pypy.tool.uid import Hashable
from pypy.tool.tls import tlsobject
from pypy.tool.identity_dict import identity_dict
+from pypy.tool import leakfinder
from types import NoneType
from sys import maxint
import weakref
TLS = tlsobject()
-# Track allocations to detect memory leaks
-# Don't track 'gc' and immortal mallocs
-TRACK_ALLOCATIONS = False
-ALLOCATED = identity_dict()
-
-def start_tracking_allocations():
- global TRACK_ALLOCATIONS
- TRACK_ALLOCATIONS = True
- ALLOCATED.clear()
-
-def stop_tracking_allocations():
- global TRACK_ALLOCATIONS
- TRACK_ALLOCATIONS = False
class _uninitialized(object):
def __init__(self, TYPE):
@@ -1380,41 +1364,21 @@
__slots__ = ('_TYPE',
'_parent_type', '_parent_index', '_keepparent',
'_wrparent',
- '__weakref__', '_traceback',
- '__storage')
+ '__weakref__',
+ '_storage')
- def __init__(self, TYPE, track_allocation=None):
+ def __init__(self, TYPE):
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
@@ -1493,12 +1457,12 @@
__slots__ = ('_hash_cache_', '_compilation_info')
- def __new__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None, track_allocation=None):
+ def __new__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None):
my_variety = _struct_variety(TYPE._names)
return object.__new__(my_variety)
- def __init__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None, track_allocation=None):
- _parentable.__init__(self, TYPE, track_allocation)
+ def __init__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None):
+ _parentable.__init__(self, TYPE)
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:
@@ -1506,8 +1470,7 @@
first, FIRSTTYPE = TYPE._first_struct()
for fld, typ in TYPE._flds.items():
if fld == TYPE._arrayfld:
- value = _array(typ, n, initialization=initialization, parent=self, parentindex=fld,
- track_allocation=track_allocation)
+ value = _array(typ, n, initialization=initialization, parent=self, parentindex=fld)
else:
value = typ._allocate(initialization=initialization, parent=self, parentindex=fld)
setattr(self, fld, value)
@@ -1568,12 +1531,12 @@
__slots__ = ('items',)
- def __init__(self, TYPE, n, initialization=None, parent=None, parentindex=None, track_allocation=None):
+ def __init__(self, TYPE, n, initialization=None, parent=None, parentindex=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, track_allocation)
+ _parentable.__init__(self, TYPE)
try:
myrange = range(n)
except OverflowError:
@@ -1640,7 +1603,7 @@
_cache = weakref.WeakKeyDictionary() # parentarray -> {subarrays}
def __init__(self, TYPE, parent, baseoffset_or_fieldname):
- _parentable.__init__(self, TYPE, track_allocation=False)
+ _parentable.__init__(self, TYPE)
self._setparentstructure(parent, baseoffset_or_fieldname)
# Keep the parent array alive, we share the same allocation.
# Don't do it if we are inside a GC object, though -- it's someone
@@ -1648,6 +1611,13 @@
if typeOf(top_container(parent))._gckind == 'raw':
self._keepparent = parent
+ def __str__(self):
+ parent = self._wrparent()
+ if parent is None:
+ return '_subarray at %s in already freed' % (self._parent_index,)
+ return '_subarray at %r in %s' % (self._parent_index,
+ parent._TYPE)
+
def __repr__(self):
parent = self._wrparent()
if parent is None:
@@ -1861,8 +1831,9 @@
return id(self.value)
-def malloc(T, n=None, flavor='gc', immortal=False, zero=False):
- assert flavor != 'cpy'
+def malloc(T, n=None, flavor='gc', immortal=False, zero=False,
+ track_allocation=True):
+ assert flavor in ('gc', 'raw')
if zero or immortal:
initialization = 'example'
elif flavor == 'raw':
@@ -1870,9 +1841,9 @@
else:
initialization = 'malloc'
if isinstance(T, Struct):
- o = _struct(T, n, initialization=initialization, track_allocation=flavor == "raw" and not immortal)
+ o = _struct(T, n, initialization=initialization)
elif isinstance(T, Array):
- o = _array(T, n, initialization=initialization, track_allocation=flavor == "raw" and not immortal)
+ o = _array(T, n, initialization=initialization)
elif isinstance(T, OpaqueType):
assert n is None
o = _opaque(T, initialization=initialization)
@@ -1880,15 +1851,19 @@
raise TypeError, "malloc for Structs and Arrays only"
if T._gckind != 'gc' and not immortal and flavor.startswith('gc'):
raise TypeError, "gc flavor malloc of a non-GC non-immortal structure"
+ if flavor == "raw" and not immortal and track_allocation:
+ leakfinder.remember_malloc(o, framedepth=2)
solid = immortal or not flavor.startswith('gc') # immortal or non-gc case
return _ptr(Ptr(T), o, solid)
-def free(p, flavor):
+def free(p, flavor, track_allocation=True):
if flavor.startswith('gc'):
raise TypeError, "gc flavor free"
T = typeOf(p)
if not isinstance(T, Ptr) or p._togckind() != 'raw':
raise TypeError, "free(): only for pointers to non-gc containers"
+ if track_allocation:
+ leakfinder.remember_free(p._obj0)
p._obj0._free()
def functionptr(TYPE, name, **attrs):
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/rclass.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/rclass.py Fri Oct 15 14:37:16 2010
@@ -420,7 +420,7 @@
return cast_pointer(self.lowleveltype, result)
def create_instance(self):
- return malloc(self.object_type, flavor=self.gcflavor)
+ return malloc(self.object_type, flavor=self.gcflavor, immortal=True)
def initialize_prebuilt_data(self, value, classdef, result):
if self.classdef is not None:
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_ll2ctypes.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_ll2ctypes.py Fri Oct 15 14:37:16 2010
@@ -765,6 +765,7 @@
assert abs(float(b[1]) - 1.1) < 1E-6
assert isinstance(b[2], rffi.r_singlefloat)
assert abs(float(b[2]) - 2.2) < 1E-6
+ lltype.free(a, flavor='raw')
def test_different_signatures(self):
if sys.platform=='win32':
@@ -879,6 +880,7 @@
qsort(rffi.cast(rffi.VOIDP, a), 5, rffi.sizeof(rffi.INT), compare)
for i in range(5):
assert a[i] == i + 1
+ lltype.free(a, flavor='raw')
def test_array_type_bug(self):
A = lltype.Array(lltype.Signed)
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_llmemory.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_llmemory.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_llmemory.py Fri Oct 15 14:37:16 2010
@@ -324,12 +324,14 @@
p_t = lltype.malloc(T)
assert p_t.s == lltype.nullptr(S)
# raw malloc does not
- p_raw_t = lltype.malloc(T, flavor="raw")
- py.test.raises(lltype.UninitializedMemoryAccess, "p_raw_t.s")
+ U = lltype.Struct("U", ('x', lltype.Signed))
+ p_raw_t = lltype.malloc(U, flavor="raw")
+ py.test.raises(lltype.UninitializedMemoryAccess, "p_raw_t.x")
+ lltype.free(p_raw_t, flavor="raw")
# this sort of raw_malloc too
- p_raw_t = cast_adr_to_ptr(raw_malloc(sizeof(T)), lltype.Ptr(T))
- py.test.raises(lltype.UninitializedMemoryAccess, "p_raw_t.s")
-
+ p_raw_t = cast_adr_to_ptr(raw_malloc(sizeof(U)), lltype.Ptr(U))
+ py.test.raises(lltype.UninitializedMemoryAccess, "p_raw_t.x")
+
def test_raw_malloc_signed_bunch():
adr = raw_malloc(sizeof(lltype.Signed) * 50)
@@ -601,7 +603,8 @@
a = lltype.malloc(A, flavor='raw')
src = cast_ptr_to_adr(a) + itemoffsetof(A, 0)
raw_memclear(src, sizeof(lltype.Signed) * 0)
-
+ lltype.free(a, flavor="raw")
+
def test_nonneg():
S1 = lltype.GcStruct('S1', ('x', lltype.Float))
A1 = lltype.GcArray(lltype.Float)
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_lltype.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_lltype.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_lltype.py Fri Oct 15 14:37:16 2010
@@ -2,6 +2,7 @@
from pypy.rpython.lltypesystem.lltype import *
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.tool.identity_dict import identity_dict
+from pypy.tool import leakfinder
def isweak(p, T):
try:
@@ -804,22 +805,20 @@
class TestTrackAllocation:
- def setup_method(self, func):
- start_tracking_allocations()
-
- def teardown_method(self, func):
- assert not lltype.ALLOCATED, "Memory was not correctly freed"
- stop_tracking_allocations()
+ def test_automatic_tracking(self):
+ # calls to start_tracking_allocations/stop_tracking_allocations
+ # should occur automatically from pypy/conftest.py. Check that.
+ assert leakfinder.TRACK_ALLOCATIONS
def test_track_allocation(self):
"""A malloc'd buffer fills the ALLOCATED dictionary"""
- assert lltype.TRACK_ALLOCATIONS
- assert not lltype.ALLOCATED
+ assert leakfinder.TRACK_ALLOCATIONS
+ assert not leakfinder.ALLOCATED
buf = malloc(Array(Signed), 1, flavor="raw")
- assert len(lltype.ALLOCATED) == 1
- assert lltype.ALLOCATED.keys() == [buf._obj]
+ assert len(leakfinder.ALLOCATED) == 1
+ assert leakfinder.ALLOCATED.keys() == [buf._obj]
free(buf, flavor="raw")
- assert not lltype.ALLOCATED
+ assert not leakfinder.ALLOCATED
def test_str_from_buffer(self):
"""gc-managed memory does not need to be freed"""
@@ -828,16 +827,22 @@
for i in range(size): raw_buf[i] = 'a'
rstr = rffi.str_from_buffer(raw_buf, gc_buf, size, size)
rffi.keep_buffer_alive_until_here(raw_buf, gc_buf)
- assert not lltype.ALLOCATED
+ assert not leakfinder.ALLOCATED
def test_leak_traceback(self):
"""Test info stored for allocated items"""
buf = malloc(Array(Signed), 1, flavor="raw")
- traceback = lltype.ALLOCATED.keys()[0]._traceback
+ traceback = leakfinder.ALLOCATED.values()[0]
lines = traceback.splitlines()
assert 'malloc(' in lines[-1] and 'flavor="raw")' in lines[-1]
- # XXX The traceback should not be too long
+ # The traceback should not be too long
print traceback
free(buf, flavor="raw")
+
+ def test_no_tracking(self):
+ p1 = malloc(Array(Signed), 1, flavor='raw', track_allocation=False)
+ p2 = malloc(Array(Signed), 1, flavor='raw', track_allocation=False)
+ free(p2, flavor='raw', track_allocation=False)
+ # p1 is not freed
Modified: pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_rffi.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_rffi.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/lltypesystem/test/test_rffi.py Fri Oct 15 14:37:16 2010
@@ -9,7 +9,7 @@
from pypy.rpython.lltypesystem.rstr import STR
from pypy.rpython.lltypesystem import lltype
from pypy.tool.udir import udir
-from pypy.rpython.test.test_llinterp import interpret, MallocMismatch
+from pypy.rpython.test.test_llinterp import interpret
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
from pypy.annotation.annrpython import RPythonAnnotator
from pypy.rpython.rtyper import RPythonTyper
Modified: pypy/branch/leak-finder/pypy/rpython/memory/gctransform/transform.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/memory/gctransform/transform.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/memory/gctransform/transform.py Fri Oct 15 14:37:16 2010
@@ -430,7 +430,8 @@
return self.parenttransformer.gct_malloc_varsize(hop)
def gct_free(self, hop):
- flavor = hop.spaceop.args[1].value
+ flags = hop.spaceop.args[1].value
+ flavor = flags['flavor']
assert flavor == 'raw'
return self.parenttransformer.gct_free(hop)
@@ -606,7 +607,8 @@
def gct_free(self, hop):
op = hop.spaceop
- flavor = op.args[1].value
+ flags = op.args[1].value
+ flavor = flags['flavor']
v = op.args[0]
assert flavor != 'cpy', "cannot free CPython objects directly"
if flavor == 'raw':
Modified: pypy/branch/leak-finder/pypy/rpython/memory/gcwrapper.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/memory/gcwrapper.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/memory/gcwrapper.py Fri Oct 15 14:37:16 2010
@@ -42,7 +42,8 @@
#
# Interface for the llinterp
#
- def malloc(self, TYPE, n=None, flavor='gc', zero=False):
+ def malloc(self, TYPE, n=None, flavor='gc', zero=False,
+ track_allocation=True):
if flavor == 'gc':
typeid = self.get_type_id(TYPE)
addr = self.gc.malloc(typeid, n, zero=zero)
@@ -51,7 +52,8 @@
gctypelayout.zero_gc_pointers(result)
return result
else:
- return lltype.malloc(TYPE, n, flavor=flavor, zero=zero)
+ return lltype.malloc(TYPE, n, flavor=flavor, zero=zero,
+ track_allocation=track_allocation)
def malloc_nonmovable(self, TYPE, n=None, zero=False):
typeid = self.get_type_id(TYPE)
@@ -69,9 +71,10 @@
return self.gc.shrink_array(addr, smallersize)
return False
- def free(self, TYPE, flavor='gc'):
+ def free(self, TYPE, flavor='gc', track_allocation=True):
assert flavor != 'gc'
- return lltype.free(TYPE, flavor=flavor)
+ return lltype.free(TYPE, flavor=flavor,
+ track_allocation=track_allocation)
def setfield(self, obj, fieldname, fieldvalue):
STRUCT = lltype.typeOf(obj).TO
Modified: pypy/branch/leak-finder/pypy/rpython/memory/support.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/memory/support.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/memory/support.py Fri Oct 15 14:37:16 2010
@@ -30,7 +30,8 @@
# we zero-initialize the chunks to make the translation
# backends happy, but we don't need to do it at run-time.
zero = not we_are_translated()
- return lltype.malloc(CHUNK, flavor="raw", zero=zero)
+ return lltype.malloc(CHUNK, flavor="raw", zero=zero,
+ track_allocation=False)
result = self.free_list
self.free_list = result.next
@@ -44,7 +45,7 @@
# Don't cache the old chunks but free them immediately.
# Helps debugging, and avoids that old chunks full of
# addresses left behind by a test end up in genc...
- lltype.free(chunk, flavor="raw")
+ lltype.free(chunk, flavor="raw", track_allocation=False)
unused_chunks = FreeList()
cache[chunk_size] = unused_chunks, null_chunk
Modified: pypy/branch/leak-finder/pypy/rpython/rbuiltin.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/rbuiltin.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/rbuiltin.py Fri Oct 15 14:37:16 2010
@@ -345,17 +345,22 @@
BUILTIN_TYPER[object.__init__] = rtype_object__init__
# annotation of low-level types
-def rtype_malloc(hop, i_flavor=None, i_zero=None):
+def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None):
assert hop.args_s[0].is_constant()
vlist = [hop.inputarg(lltype.Void, arg=0)]
opname = 'malloc'
- v_flavor, v_zero = parse_kwds(hop, (i_flavor, lltype.Void), (i_zero, None))
+ v_flavor, v_zero, v_track_allocation = parse_kwds(hop,
+ (i_flavor, lltype.Void),
+ (i_zero, None),
+ (i_track_allocation, None))
flags = {'flavor': 'gc'}
if v_flavor is not None:
flags['flavor'] = v_flavor.value
if i_zero is not None:
flags['zero'] = v_zero.value
+ if i_track_allocation is not None:
+ flags['track_allocation'] = v_track_allocation.value
vlist.append(hop.inputconst(lltype.Void, flags))
if hop.nb_args == 2:
@@ -366,10 +371,19 @@
hop.exception_is_here()
return hop.genop(opname, vlist, resulttype = hop.r_result.lowleveltype)
-def rtype_free(hop, i_flavor):
- assert i_flavor == 1
+def rtype_free(hop, i_flavor, i_track_allocation=None):
+ vlist = [hop.inputarg(hop.args_r[0], arg=0)]
+ v_flavor, v_track_allocation = parse_kwds(hop,
+ (i_flavor, lltype.Void),
+ (i_track_allocation, None))
+ #
+ assert v_flavor is not None and v_flavor.value == 'raw'
+ flags = {'flavor': 'raw'}
+ if i_track_allocation is not None:
+ flags['track_allocation'] = v_track_allocation.value
+ vlist.append(hop.inputconst(lltype.Void, flags))
+ #
hop.exception_cannot_occur()
- vlist = hop.inputargs(hop.args_r[0], lltype.Void)
hop.genop('free', vlist)
def rtype_const_result(hop):
@@ -584,8 +598,9 @@
vinst, = hop.inputargs(hop.args_r[0])
flavor = hop.args_r[0].gcflavor
assert flavor != 'gc'
- cflavor = hop.inputconst(lltype.Void, flavor)
- return hop.genop('free', [vinst, cflavor])
+ flags = {'flavor': flavor}
+ cflags = hop.inputconst(lltype.Void, flags)
+ return hop.genop('free', [vinst, cflags])
BUILTIN_TYPER[objectmodel.free_non_gc_object] = rtype_free_non_gc_object
Modified: pypy/branch/leak-finder/pypy/rpython/test/test_llinterp.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/test/test_llinterp.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/test/test_llinterp.py Fri Oct 15 14:37:16 2010
@@ -12,13 +12,11 @@
from pypy.annotation.model import lltype_to_annotation
from pypy.rlib.rarithmetic import r_uint, ovfcheck
from pypy.rpython.ootypesystem import ootype
+from pypy.tool import leakfinder
from pypy import conftest
# switch on logging of interp to show more info on failing tests
-class MallocMismatch(Exception):
- pass
-
def setup_module(mod):
mod.logstate = py.log._getstate()
py.log.setconsumer("llinterp", py.log.STDOUT)
@@ -72,7 +70,7 @@
def get_interpreter(func, values, view='auto', viewbefore='auto', policy=None,
someobjects=False, type_system="lltype", backendopt=False,
- config=None, malloc_check=True, **extraconfigopts):
+ config=None, **extraconfigopts):
extra_key = [(key, value) for key, value in extraconfigopts.iteritems()]
extra_key.sort()
extra_key = tuple(extra_key)
@@ -97,7 +95,7 @@
viewbefore, policy, type_system=type_system,
backendopt=backendopt, config=config,
**extraconfigopts)
- interp = LLInterpreter(typer, malloc_check=malloc_check)
+ interp = LLInterpreter(typer)
_tcache[key] = (t, interp, graph)
# keep the cache small
_lastinterpreted.append(key)
@@ -115,10 +113,17 @@
interp, graph = get_interpreter(func, values, view, viewbefore, policy,
someobjects, type_system=type_system,
backendopt=backendopt, config=config,
- malloc_check=malloc_check, **kwargs)
- result = interp.eval_graph(graph, values)
- if malloc_check and interp.mallocs:
- raise MallocMismatch(interp.mallocs)
+ **kwargs)
+ if not malloc_check:
+ result = interp.eval_graph(graph, values)
+ else:
+ prev = leakfinder.start_tracking_allocations()
+ try:
+ result = interp.eval_graph(graph, values)
+ finally:
+ leaks = leakfinder.stop_tracking_allocations(False, prev)
+ if leaks:
+ raise leakfinder.MallocMismatch(leaks)
return result
def interpret_raises(exc, func, values, view='auto', viewbefore='auto',
@@ -418,6 +423,7 @@
assert result
def test_stack_malloc():
+ py.test.skip("stack-flavored mallocs no longer supported")
class A(object):
pass
def f():
@@ -430,6 +436,7 @@
assert result == 1
def test_invalid_stack_access():
+ py.test.skip("stack-flavored mallocs no longer supported")
class A(object):
pass
globala = A()
@@ -605,7 +612,7 @@
if x:
free(t, flavor='raw')
interpret(f, [1])
- py.test.raises(MallocMismatch, "interpret(f, [0])")
+ py.test.raises(leakfinder.MallocMismatch, "interpret(f, [0])")
def f():
t1 = malloc(T, flavor='raw')
Modified: pypy/branch/leak-finder/pypy/rpython/test/test_nongc.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/test/test_nongc.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/test/test_nongc.py Fri Oct 15 14:37:16 2010
@@ -79,7 +79,7 @@
py.test.raises(TypeError,rtyper.specialize) # results in an invalid cast
def test_isinstance():
- class A:
+ class A(object):
_alloc_flavor_ = "raw"
class B(A):
pass
@@ -95,7 +95,24 @@
o = B()
else:
o = C()
- return 100*isinstance(o, A)+10*isinstance(o, B)+1*isinstance(o ,C)
+ res = 100*isinstance(o, A) + 10*isinstance(o, B) + 1*isinstance(o, C)
+ if i == 0:
+ pass
+ elif i == 1:
+ assert isinstance(o, A)
+ free_non_gc_object(o)
+ elif i == 2:
+ assert isinstance(o, B)
+ free_non_gc_object(o)
+ else:
+ assert isinstance(o, C)
+ free_non_gc_object(o)
+ return res
+
+ assert f(1) == 100
+ assert f(2) == 110
+ assert f(3) == 111
+ assert f(0) == 0
a = RPythonAnnotator()
#does not raise:
@@ -131,10 +148,14 @@
d = b
elif i == 2:
e = c
- return (0x0001*(a is b) | 0x0002*(a is c) | 0x0004*(a is d) |
+ res = (0x0001*(a is b) | 0x0002*(a is c) | 0x0004*(a is d) |
0x0008*(a is e) | 0x0010*(b is c) | 0x0020*(b is d) |
0x0040*(b is e) | 0x0080*(c is d) | 0x0100*(c is e) |
0x0200*(d is e))
+ free_non_gc_object(a)
+ free_non_gc_object(b)
+ free_non_gc_object(c)
+ return res
a = RPythonAnnotator()
#does not raise:
s = a.build_types(f, [int])
@@ -169,10 +190,13 @@
d = b
elif i == 2:
e = c
- return (0x0001*(a is b) | 0x0002*(a is c) | 0x0004*(a is d) |
+ res = (0x0001*(a is b) | 0x0002*(a is c) | 0x0004*(a is d) |
0x0008*(a is e) | 0x0010*(b is c) | 0x0020*(b is d) |
0x0040*(b is e) | 0x0080*(c is d) | 0x0100*(c is e) |
0x0200*(d is e))
+ free_non_gc_object(a)
+ free_non_gc_object(b)
+ return res
a = RPythonAnnotator()
#does not raise:
s = a.build_types(f, [int])
Modified: pypy/branch/leak-finder/pypy/rpython/test/test_rptr.py
==============================================================================
--- pypy/branch/leak-finder/pypy/rpython/test/test_rptr.py (original)
+++ pypy/branch/leak-finder/pypy/rpython/test/test_rptr.py Fri Oct 15 14:37:16 2010
@@ -212,10 +212,31 @@
S = Struct('S', ('x', Signed))
def fn(n):
- p = malloc(S, flavor='whatever')
+ p = malloc(S, flavor='raw')
p.x = n
result = p.x
- free(p, flavor='whatever')
+ free(p, flavor='raw')
+ return n
+
+ res = interpret(fn, [23])
+ assert res == 23
+
+ S = Struct('S', ('x', Signed))
+ def fn(n):
+ p = malloc(S, flavor='raw', track_allocation=False)
+ p.x = n
+ result = p.x
+ return n
+
+ res = interpret(fn, [23])
+ assert res == 23
+
+ S = Struct('S', ('x', Signed))
+ def fn(n):
+ p = malloc(S, flavor='raw', track_allocation=False)
+ p.x = n
+ result = p.x
+ free(p, flavor='raw', track_allocation=False)
return n
res = interpret(fn, [23])
Added: pypy/branch/leak-finder/pypy/tool/leakfinder.py
==============================================================================
--- (empty file)
+++ pypy/branch/leak-finder/pypy/tool/leakfinder.py Fri Oct 15 14:37:16 2010
@@ -0,0 +1,73 @@
+import sys, gc
+import cStringIO
+import traceback
+
+# Track allocations to detect memory leaks.
+# So far, this is used for lltype.malloc(flavor='raw').
+TRACK_ALLOCATIONS = False
+ALLOCATED = {}
+
+class MallocMismatch(Exception):
+ def __str__(self):
+ dict = self.args[0]
+ dict2 = {}
+ for obj, traceback in dict.items():
+ traceback = traceback.splitlines()
+ if len(traceback) > 8:
+ traceback = [' ...'] + traceback[-6:]
+ traceback = '\n'.join(traceback)
+ dict2.setdefault(traceback, [])
+ dict2[traceback].append(obj)
+ lines = ['{']
+ for traceback, objs in dict2.items():
+ lines.append('')
+ for obj in objs:
+ lines.append('%s:' % (obj,))
+ lines.append(traceback)
+ lines.append('}')
+ return '\n'.join(lines)
+
+def start_tracking_allocations():
+ global TRACK_ALLOCATIONS
+ if TRACK_ALLOCATIONS:
+ result = ALLOCATED.copy() # nested start
+ else:
+ result = None
+ TRACK_ALLOCATIONS = True
+ ALLOCATED.clear()
+ return result
+
+def stop_tracking_allocations(check, prev=None):
+ global TRACK_ALLOCATIONS
+ assert TRACK_ALLOCATIONS
+ for i in range(5):
+ if not ALLOCATED:
+ break
+ gc.collect()
+ result = ALLOCATED.copy()
+ ALLOCATED.clear()
+ if prev is None:
+ TRACK_ALLOCATIONS = False
+ else:
+ ALLOCATED.update(prev)
+ if check and result:
+ raise MallocMismatch(result)
+ return result
+
+def remember_malloc(obj, framedepth=1):
+ if TRACK_ALLOCATIONS:
+ frame = sys._getframe(framedepth)
+ sio = cStringIO.StringIO()
+ traceback.print_stack(frame, limit=10, file=sio)
+ tb = sio.getvalue()
+ ALLOCATED[obj] = tb
+
+def remember_free(obj):
+ if TRACK_ALLOCATIONS:
+ if obj not in ALLOCATED:
+ # rehashing is needed because some objects' hash may change
+ # e.g. when lltype objects are turned into <C object>
+ items = ALLOCATED.items()
+ ALLOCATED.clear()
+ ALLOCATED.update(items)
+ del ALLOCATED[obj]
Added: pypy/branch/leak-finder/pypy/tool/test/test_leakfinder.py
==============================================================================
--- (empty file)
+++ pypy/branch/leak-finder/pypy/tool/test/test_leakfinder.py Fri Oct 15 14:37:16 2010
@@ -0,0 +1,70 @@
+import py
+from pypy.tool import leakfinder
+
+def test_start_stop():
+ leakfinder.start_tracking_allocations()
+ assert leakfinder.TRACK_ALLOCATIONS
+ leakfinder.stop_tracking_allocations(True)
+ assert not leakfinder.TRACK_ALLOCATIONS
+
+def test_start_stop_nested():
+ leakfinder.start_tracking_allocations()
+ p2 = leakfinder.start_tracking_allocations()
+ assert leakfinder.TRACK_ALLOCATIONS
+ leakfinder.stop_tracking_allocations(True, prev=p2)
+ assert leakfinder.TRACK_ALLOCATIONS
+ leakfinder.stop_tracking_allocations(True)
+ assert not leakfinder.TRACK_ALLOCATIONS
+
+def test_remember_free():
+ leakfinder.start_tracking_allocations()
+ x = 1234
+ leakfinder.remember_malloc(x)
+ leakfinder.remember_free(x)
+ leakfinder.stop_tracking_allocations(True)
+
+def test_remember_forget():
+ leakfinder.start_tracking_allocations()
+ x = 1234
+ leakfinder.remember_malloc(x)
+ py.test.raises(leakfinder.MallocMismatch,
+ leakfinder.stop_tracking_allocations, True)
+
+def test_nested_remember_forget_1():
+ leakfinder.start_tracking_allocations()
+ x = 1234
+ leakfinder.remember_malloc(x)
+ p2 = leakfinder.start_tracking_allocations()
+ leakfinder.stop_tracking_allocations(True, prev=p2)
+ py.test.raises(leakfinder.MallocMismatch,
+ leakfinder.stop_tracking_allocations, True)
+
+def test_nested_remember_forget_2():
+ p2 = leakfinder.start_tracking_allocations()
+ x = 1234
+ leakfinder.remember_malloc(x)
+ py.test.raises(leakfinder.MallocMismatch,
+ leakfinder.stop_tracking_allocations, True, prev=p2)
+ leakfinder.stop_tracking_allocations(True)
+
+def test_traceback():
+ leakfinder.start_tracking_allocations()
+ x = 1234
+ leakfinder.remember_malloc(x)
+ res = leakfinder.stop_tracking_allocations(check=False)
+ assert res.keys() == [x]
+ print res[x]
+ assert isinstance(res[x], str)
+ assert 'test_traceback' in res[x]
+ assert 'leakfinder.remember_malloc(x)' in res[x]
+
+def test_malloc_mismatch():
+ import sys, traceback, cStringIO
+ sio = cStringIO.StringIO()
+ traceback.print_stack(sys._getframe(), limit=10, file=sio)
+ tb = sio.getvalue()
+ e = leakfinder.MallocMismatch({1234: tb, 2345: tb})
+ print str(e)
+ # grouped entries for 1234 and 2345
+ assert '1234:\n2345:\n' in str(e) or '2345:\n1234:\n' in str(e)
+ assert tb[-80:] in str(e)
Modified: pypy/branch/leak-finder/pypy/translator/c/test/test_lltyped.py
==============================================================================
--- pypy/branch/leak-finder/pypy/translator/c/test/test_lltyped.py (original)
+++ pypy/branch/leak-finder/pypy/translator/c/test/test_lltyped.py Fri Oct 15 14:37:16 2010
@@ -401,6 +401,7 @@
for i in range(n):
p = malloc(S, flavor='raw', zero=True)
if p.x != 0 or p.y != 0:
+ free(p, flavor='raw')
return -1
p.x = i
p.y = i
@@ -418,14 +419,16 @@
def f(n):
for length in range(n-1, -1, -1):
p = malloc(S, length, flavor='raw', zero=True)
- if p.x != 0:
- return -1
- p.x = n
- for j in range(length):
- if p.y[j] != 0:
- return -3
- p.y[j] = n^j
- free(p, flavor='raw')
+ try:
+ if p.x != 0:
+ return -1
+ p.x = n
+ for j in range(length):
+ if p.y[j] != 0:
+ return -3
+ p.y[j] = n^j
+ finally:
+ free(p, flavor='raw')
return 42
fn = self.getcompiled(f, [int])
@@ -655,7 +658,7 @@
def test_prebuilt_ll2ctypes_array(self):
from pypy.rpython.lltypesystem import rffi, ll2ctypes
A = rffi.CArray(Char)
- a = malloc(A, 6, flavor='raw')
+ a = malloc(A, 6, flavor='raw', immortal=True)
a[0] = 'a'
a[1] = 'b'
a[2] = 'c'
@@ -676,7 +679,7 @@
def test_ll2ctypes_array_from_c(self):
from pypy.rpython.lltypesystem import rffi, ll2ctypes
A = rffi.CArray(Char)
- a = malloc(A, 6, flavor='raw')
+ a = malloc(A, 6, flavor='raw', immortal=True)
a[0] = 'a'
a[1] = 'b'
a[2] = 'c'
More information about the Pypy-commit
mailing list