Memory leaks in Python 1.6
Charles G Waldman
cgw at fnal.gov
Fri Apr 14 16:42:19 EDT 2000
I've been running a small script I wrote called "leaktest" (attached
below) which runs tests from the the Python/Lib/test directory in a
loop, while watching the memory use of the process. I've found quite
a few memory leaks this way, and am working on fixing them. I will be
submitting a bunch of patches to the patches list over the next
several days.
Script: leaktest.py
#!/usr/bin/env python
"""
Runs tests repeatedly, looking for memory leaks
If you have gnuplot installed you can also get nice plots of memory
usage vs. time
Usage: leaktest [-max max] [-min min] [-check check] [testname] [testname]...
runs one or more tests repeatedly, looking for memory leaks
The tests can be specified as module name (test_gzip) or file name
(/path/to/test_gzip.py)
memory usage is checked every "check" iterations of the test.
at least "min" readings are taken, and at most "max" readings. If no
memory leak is detected after "min", the test stops.
If "DO_LOG" is set to 1, memory usage will be logged to a file.
If "DO_PLOT" is set to 1, pretty pictures will be drawn.
If "VERBOSE" is set to 1, memory usage will be printed during the test
"""
import sys
import os
import time
import string
DO_LOG=1
DO_PLOT=1
VERBOSE=0
def open_file(name):
if os.path.exists(name):
os.system("cp -b %s %s.bak"%(name,name))
f=open(name,'w')
return f
def mem_usage(pid):
p=os.popen('ps uwp %s'%pid)
lines=p.readlines()
status=p.close()
if status or len(lines)!=2:
return None
return int(string.split(lines[1])[4])
def no_increase(l):
for i in xrange(1,len(l)):
if l[i]>l[i-1]:
return 0
return 1
def run_test(testname, min_readings=10, max_readings=50, check_interval=10):
print "RUN %s min %d max %d check %d"%(testname,min_readings,
max_readings, check_interval)
if DO_LOG:
log = open_file('%s.log'%testname)
logdata=[]
ret=1
pid=os.getpid()
stdout, stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = open_file('%s.out'%testname), open_file('%s.err'%testname)
for loop_readings in xrange(max_readings):
mem = mem_usage(pid)
if not mem:
break
if VERBOSE:
stdout.write('%s\n'%mem)
logdata.append(mem)
if DO_LOG:
log.write('%s\n'%mem)
log.flush()
for n in xrange(check_interval):
try:
if loop_readings==0:
exec "import test.%s"%testname
else:
exec('reload(test.%s)'%testname)
except:
ret=-1
break
if ret<0:
break
if len(logdata)>=min_readings and no_increase(logdata[-min_readings:]):
ret=0
break
sys.stdout.close()
sys.stderr.close()
sys.stdout, sys.stderr = stdout, stderr
if DO_LOG:
log.close()
return ret, logdata
def do_plot(testname):
cmdfile=open('%s.gnuplot' % testname, 'w')
template='set nokey\nset xlabel "Iterations"\nset ylabel "Memory Use (KB)"\n\
set title "%s\nplot "%s" with lines\npause 60\n'
cmdfile.write(template % (testname, testname+'.log'))
cmdfile.close()
os.system("gnuplot %s.gnuplot &"%testname)
def main(args):
check_interval=10
min_readings=10
max_readings=50
while args:
arg = args.pop(0)
if arg=='-min':
min_readings = int(args.pop(0))
continue
if arg=='-max':
max_readings = int(args.pop(0))
continue
if arg=='-check':
check_interval = int(args.pop(0))
continue
## allow the test module names to be specified as full pathnames
## for ease in calling this from shellscripts
arg = os.path.split(arg)[1]
arg = os.path.splitext(arg)[0]
status, data=run_test(arg, min_readings, max_readings, check_interval)
if status<0:
print "test", arg, "did not run"
elif status==0:
print arg, "no memory leak detected", data
else:
print arg, "leaks memory", data
if DO_PLOT:
do_plot(arg)
if __name__ == '__main__':
main(sys.argv[1:])
More information about the Python-list
mailing list