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

Reply via email to