I have been debugging a distributed application for about 2 days that
has a memory leak. My app is a Twisted app, so I thought that maybe it
was on the twisted side, I finally isolated it to no being a Twisted
problem but a Python problem. The problem comes from the code that uses
wxPython and pySerial, these each run in a different process. I wrote
test programs, one "pure" wxPython, one "pure" pySerial and let them
run. The number of objects in the wxPython code doubles in about an
hour, the pySerial code doubles about every 10 minutes. I think there is
something wrong with the GC/memory management or C lib. The wxPython lib
is big, so it may have bugs, but pySerial is small and pure python (no
C/C++ directly, event thought it uses os.open/close & termios that
themselves use C (unless I mistaking)) and I reviewed it's code and I
see nothing that could cause this.
I ran these tests on linux 2.6 (ubuntu 8.04) using python 2.5.2. I used
the "ps" command to see the memory usage and also a recipe that I found
that counts the number of objects. I attached the pyserial, wxPython
code & the recipe. To test them just run each example and run "ps waux |
grep python" once in a while, or send the process a SIGUSR1 and it will
print in the terminal the object count. The wxCode has a refresh
problem, if you use the SIGUSR1/object count method you have to right
click on the taskbar icon to have it show up in the terminal.
I hope I wrong and the problem is elsewhere though.
Thank you very much,
Gabriel
import code, traceback, signal, gc, inspect, types, operator
def listen():
"""
Call this to show the object count. To use send the C{SIGUSR1}
segnal to the process
"""
signal.signal(signal.SIGUSR1, printObjectCount)
def _getr(slist, olist, seen):
"""
Recursively expand slist's objects into olist, using seen to track
already processed objects.
@see: U{http://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects}
"""
for e in slist:
if id(e) in seen:
continue
seen[id(e)] = None
olist.append(e)
tl = gc.get_referents(e)
if tl:
_getr(tl, olist, seen)
def getAllObjects():
"""
Return a list of all live Python objects, not including the list itself.
@see: U{http://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects}
"""
gcl = gc.get_objects()
olist = []
seen = {}
# Just in case:
seen[id(gcl)] = None
seen[id(olist)] = None
seen[id(seen)] = None
# _getr does the real work.
_getr(gcl, olist, seen)
return olist
def printObjectCount(sig, frame):
olist = getAllObjects()
print "\n####################################################################"
print "# DEBUG: The object count is : %d" % len(olist)
print "####################################################################\n"
import serial
import debug_utils
debug_utils.listen()
s = serial.Serial("/dev/pts/0")
# The idea is to have it do opens & closes often
while True:
try:
s.open()
s.timeout = 2
while True:
try:
t = s.read(1)
except Exception, e:
print "error inner-loop : %s" % str(e)
else:
if(t == ""):
raise Exception("timeout")
else:
print "data : %s" % t
except Exception, e:
print "error outer-loop : %s" % str(e)
s.close()
import wx
import wx.lib.newevent
import sha, os
# The wxPython event for error notifications
ShowErrorEvent, SHOW_ERROR_EVENT_ID = wx.lib.newevent.NewEvent()
class MyTaskBarIcon(wx.TaskBarIcon):
ID_LOGIN = wx.NewId()
ID_CONFIG = wx.NewId()
ID_MUTE = wx.NewId()
def __init__(self, parent, *args, **kwargs):
wx.TaskBarIcon.__init__(self, *args, **kwargs)
self.parent = parent
icon = wx.Icon("./icon/linux/logo-connected-22x22.png", wx.BITMAP_TYPE_PNG)
self.SetIcon(icon, "Control")
self.Bind(wx.EVT_MENU, self.parent.showLogin, id=MyTaskBarIcon.ID_LOGIN)
self.Bind(wx.EVT_MENU, self.onExit, id=wx.ID_EXIT)
def CreatePopupMenu(self):
menu=wx.Menu()
menu.Append(MyTaskBarIcon.ID_LOGIN, "&Login/Change user")
menu.AppendSeparator()
menu.Append(wx.ID_EXIT, "E&xit")
return menu
def onExit(self, evt):
dlg = wx.MessageDialog(None, "Are you sure you want to exit?", "Exit the application?", wx.YES_NO | wx.ICON_QUESTION | wx.NO_DEFAULT)
if dlg.ShowModal() == wx.ID_YES:
# need to destroy the icon BEFORE the frame
# because the frame is passed into the icon's class
wx.CallAfter(self.Destroy)
wx.CallAfter(self.parent.Destroy)
dlg.Destroy()
class LoginDialog(wx.Frame):
def __init__(self, parent=None, id=wx.ID_ANY, title="Login",
pos=wx.DefaultPosition, size=(300, 175),
#style=wx.FRAME_NO_TASKBAR|wx.NO_FULL_REPAINT_ON_RESIZE,
*args, **kwargs):
wx.Frame.__init__(self, parent, id, title, pos, size, *args, **kwargs)
panel = wx.Panel(self, wx.ID_ANY) # added a panel
icon = wx.Icon("./icon/linux/logo-connected-22x22.png", wx.BITMAP_TYPE_PNG)
self.SetIcon(icon)
wx.StaticText(panel, wx.ID_ANY, "Please type your user name and password.", wx.Point(15, 5))
wx.StaticText(panel, wx.ID_ANY, "User name: ", wx.Point(20, 30))
wx.StaticText(panel, wx.ID_ANY, "Password: ", wx.Point(20, 55))
self.usernameBox = wx.TextCtrl(panel, wx.ID_ANY, "", wx.Point(100, 30), wx.Size(175, wx.ID_ANY))
self.usernameBox.SetFocus()
self.passwordBox = wx.TextCtrl(panel, wx.ID_ANY, "", wx.Point(100, 55), wx.Size(175, wx.ID_ANY), style=wx.TE_PASSWORD)
self.checkBox = wx.CheckBox(panel, wx.ID_ANY, "Remember login information", wx.Point(20, 90))
wx.Button(panel, wx.ID_OK, " OK ", wx.Point(100, 115), wx.DefaultSize).SetDefault()
wx.Button(panel, wx.ID_CANCEL, " Cancel ", wx.Point(200, 115), wx.DefaultSize)
self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)
self.tbIcon = MyTaskBarIcon(self)
self.Bind(wx.EVT_CLOSE, self.onCancel)
def onOk(self, event):
username = self.usernameBox.GetValue()
password = self.passwordBox.GetValue()
if(len(username) > 0 and len(password) > 0): # Test if the user entered the required info
password = sha.new(password).hexdigest()
print "Username : %s, Password : %s" % (username, password)
self.Show(False)
else:
dlg = wx.MessageDialog(self, "You are required to enter a username and password to login", "Missing information", wx.OK | wx.ICON_EXCLAMATION)
dlg.ShowModal()
dlg.Destroy()
def onCancel(self, event):
dlg = wx.MessageDialog(self, "Are you sure you want to cancel logging in?", "Cancel login?", wx.YES_NO | wx.ICON_QUESTION | wx.NO_DEFAULT)
if dlg.ShowModal() == wx.ID_YES:
self.abortLogin(event)
dlg.Destroy()
def abortLogin(self, event):
self.Show(False)
def showLogin(self, event):
self.usernameBox.SetValue("")
self.passwordBox.SetValue("")
self.usernameBox.SetFocus()
self.Show(True)
class MyApp(wx.App):
def OnInit(self):
self.gui = LoginDialog()
self.gui.Center(wx.BOTH)
self.gui.Show(False)
self.SetTopWindow(self.gui)
return True
if(__name__ == "__main__"):
if(__debug__):
print "turning on debuging"
from arimaz.mydeskfriend import debug_utils
debug_utils.listen()
app = MyApp(False)
app.MainLoop()
--
http://mail.python.org/mailman/listinfo/python-list