[Python-Dev] Valgrind on 2.2.2

Guido van Rossum guido@python.org
Thu, 17 Oct 2002 20:38:40 -0400


> I ran valgrind 1.0.3 on Python 2.2.2.  Well, almost 2.2.2, it was from
> 12 Oct.  After 90+ minutes and over 512 MB of RAM, there are no
> new/major issues to report.
> 
> The complete valgrind log (231k) can be found here: 
>         http://www.metaslash.com/py/valgrind-2_2_2.txt
> 
> I've cleaned up the log (200k) to remove some of the uninteresting stuff:
>         http://www.metaslash.com/py/valgrind-clean-2_2_2.txt

I looked at this one only.

> There are a few small memory leaks.  I think most of the leaks are
> related to threads.  The report contains memory still in use as well
> as leaks.  To find memory leaks, search for ' lost ' without quotes.
> It's quite possible some of the memory still in use is due to
> reference leaks.

This seems to be the most worrysome:

==28827== 268980 bytes in 15 blocks are possibly lost in loss record 100 of 106
==28827==    at 0x400487CD: realloc (vg_clientfuncs.c:270)
==28827==    by 0x80994DC: _PyObject_GC_Resize (Modules/gcmodule.c:917)
==28827==    by 0x80BD46E: PyFrame_New (Objects/frameobject.c:264)
==28827==    by 0x80782C3: PyEval_EvalCodeEx (Python/ceval.c:2390)
==28827==    by 0x807AA43: fast_function (Python/ceval.c:3173)
==28827==    by 0x80779B5: eval_frame (Python/ceval.c:2034)
==28827==    by 0x807892B: PyEval_EvalCodeEx (Python/ceval.c:2595)
==28827==    by 0x807AA43: fast_function (Python/ceval.c:3173)
==28827==    by 0x80779B5: eval_frame (Python/ceval.c:2034)
==28827==    by 0x807892B: PyEval_EvalCodeEx (Python/ceval.c:2595)

There are a few other records mentioning GC_Resize, but this one is
the biggest.  Could it be that the free frame list is botched?  OTOH,
what does "possibly lost" really mean?

There are also a few fingers pointing in the direction of weakref_ref,
e.g.

==28827== 520 bytes in 14 blocks are possibly lost in loss record 48 of 106
==28827==    at 0x400481B4: malloc (vg_clientfuncs.c:100)
==28827==    by 0x8099519: _PyObject_GC_New (Modules/gcmodule.c:868)
==28827==    by 0x8067BA5: PyWeakref_NewRef (Objects/weakrefobject.c:37)
==28827==    by 0x8066119: add_subclass (Objects/typeobject.c:2249)
==28827==    by 0x8061F29: PyType_Ready (Objects/typeobject.c:2219)
==28827==    by 0x80605A8: type_new (Objects/typeobject.c:1280)
==28827==    by 0x805EDA4: type_call (Objects/typeobject.c:183)
==28827==    by 0x80ABB0C: PyObject_Call (Objects/abstract.c:1688)
==28827==    by 0x807A34F: PyEval_CallObjectWithKeywords (Python/ceval.c:3058)
==28827==    by 0x80AB0C6: PyObject_CallFunction (Objects/abstract.c:1679)

Of course many of these could be caused by a single leak that drops a
pointer to a container -- then everything owned by that container is
also leaked.

I noticed this one:

==28713== 572 bytes in 15 blocks are possibly lost in loss record 39 of 78
==28713==    at 0x400481B4: malloc (vg_clientfuncs.c:100)
==28713==    by 0x8099519: _PyObject_GC_New (Modules/gcmodule.c:868)
==28713==    by 0x80B2D09: PyMethod_New (Objects/classobject.c:2008)
==28713==    by 0x80AF837: instance_getattr2 (Objects/classobject.c:702)
==28713==    by 0x80AF73A: instance_getattr1 (Objects/classobject.c:676)
==28713==    by 0x80B30F1: instance_getattr (Objects/classobject.c:715)
==28713==    by 0x80577A2: PyObject_GetAttr (Objects/object.c:1108)
==28713==    by 0x80B1731: half_cmp (Objects/classobject.c:1503)
==28713==    by 0x80B1937: instance_compare (Objects/classobject.c:1572)
==28713==    by 0x8055A6E: try_3way_compare (Objects/object.c:477)

which led me to an easy-to-fix leak in half_cmp(), both in 2.2.2 and 2.3:

diff -c -c -r2.154.8.1 classobject.c
*** classobject.c	13 Jun 2002 21:36:35 -0000	2.154.8.1
--- classobject.c	18 Oct 2002 00:36:06 -0000
***************
*** 1507,1514 ****
  	}
  
  	args = Py_BuildValue("(O)", w);
! 	if (args == NULL)
  		return -2;
  
  	result = PyEval_CallObject(cmp_func, args);
  	Py_DECREF(args);
--- 1507,1516 ----
  	}
  
  	args = Py_BuildValue("(O)", w);
! 	if (args == NULL) {
! 		Py_DECREF(cmp_func);
  		return -2;
+ 	}
  
  	result = PyEval_CallObject(cmp_func, args);
  	Py_DECREF(args);

but somehow I don't think that caused the report, because this exit
can only be taken if there's a memory error.  (Hm...  or if w == NULL
upon entry?  How could that happen?)

A similar one is on half_binop.

--Guido van Rossum (home page: http://www.python.org/~guido/)