[lxml-dev] Leaking tracebacks

Hello! A college run into a memory leak on one of our servers a couple of days ago. With the help of Dowser - a CherryPy plugin - we were able to nail down the leak to traceback and frame objects. Some code in Martijn Faassen's oaipmh package kept references to an exception traceback. Today I was able to track 'n nail down the issue. I highly suspect lxml to be the culprit. Whenever the code reaches lxml with an exception in sys.exc_info the exception leaks. My dowser patch shows a refcnt of 4 references to the root traceback object that can't be traced to objects in the gc.get_objects() list. A minor change to oaipmh.server.XMLTreeServer gets rid of the leaking tracebacks: def handleException(self, exception): if isinstance(exception, error.ErrorBase): sys.exc_clear() ## <- REF LEAK FIX envelope = self._outputErrors( [(exception.oainame(), str(exception))]) return envelope # unhandled exception, so raise again raise We are using lxml 2.1.2 on Python 2.5.2. I think lxml 2.0.7 didn't have the problem. Christian

Hi, Christian Heimes wrote:
A college run into a memory leak on one of our servers a couple of days ago. With the help of Dowser - a CherryPy plugin - we were able to nail down the leak to traceback and frame objects. Some code in Martijn Faassen's oaipmh package kept references to an exception traceback.
Today I was able to track 'n nail down the issue. I highly suspect lxml to be the culprit. Whenever the code reaches lxml with an exception in sys.exc_info the exception leaks. My dowser patch shows a refcnt of 4 references to the root traceback object that can't be traced to objects in the gc.get_objects() list.
There was a change in Cython regarding exception handling not so long ago. Cython now mimics more the behaviour of Py3, i.e. exceptions disappear once they were caught. If the problem was introduced at this point, it would explain why this appears with later lxml releases only, as older releases are based on older Cython versions. I'm not sure what the problem is here, though, so this needs further investigation. From a couple of quick tests, I can't seem to reproduce this problem. Could you try to come up with some test code that shows this behaviour? Is it only a problem with functionality that interacts with libxml2 in both directions (such as XPath/XSLT, which uses function callbacks back into lxml), or does it also happen in functions like etree.iselement()? Stefan

Stefan Behnel wrote:
There was a change in Cython regarding exception handling not so long ago. Cython now mimics more the behaviour of Py3, i.e. exceptions disappear once they were caught. If the problem was introduced at this point, it would explain why this appears with later lxml releases only, as older releases are based on older Cython versions.
I was able to come up with a minimal test case. It shows that lxml 2.0.7 is fine but 2.0.9 and 2.1.2 are leaking references somewhere.
I'm not sure what the problem is here, though, so this needs further investigation. From a couple of quick tests, I can't seem to reproduce this problem. Could you try to come up with some test code that shows this behaviour? Is it only a problem with functionality that interacts with libxml2 in both directions (such as XPath/XSLT, which uses function callbacks back into lxml), or does it also happen in functions like etree.iselement()?
--- import sys import pkg_resources pkg_resources.require("lxml==%s" % sys.argv[1]) from lxml import etree class SampleException(Exception): pass def test(): print "lxml: %s, %s, %s" % (etree.LXML_VERSION, etree.LIBXML_VERSION, etree.LIBXSLT_VERSION) print "SampleException start: %i" % sys.getrefcount(SampleException) for i in range(1000): try: raise SampleException except Exception, err: el = etree.Element("test") if i == 999: print "exception: %i" % sys.getrefcount(err) print "SampleException stop: %i" % sys.getrefcount(SampleException) if __name__ == "__main__": test() --- Output: $ python2.5 lxml_bug.py 2.0.7 lxml: (2, 0, 7, 0), (2, 6, 31), (1, 1, 24) SampleException start: 4 exception: 4 SampleException stop: 7 $ python2.5 lxml_bug.py 2.0.9 lxml: (2, 0, 9, 0), (2, 6, 31), (1, 1, 24) SampleException start: 4 exception: 5 SampleException stop: 2006 $ python2.5 lxml_bug.py 2.1.2 lxml: (2, 1, 2, 0), (2, 6, 31), (1, 1, 24) SampleException start: 4 exception: 5 SampleException stop: 2006 Every loop leaks 2 references to the exeption class. Christian

Christian Heimes wrote:
import sys import pkg_resources pkg_resources.require("lxml==%s" % sys.argv[1])
from lxml import etree
class SampleException(Exception): pass
def test(): print "lxml: %s, %s, %s" % (etree.LXML_VERSION, etree.LIBXML_VERSION, etree.LIBXSLT_VERSION) print "SampleException start: %i" % sys.getrefcount(SampleException) for i in range(1000): try: raise SampleException except Exception, err:
# this removes the ref leak sys.exc_clear()
el = etree.Element("test") if i == 999: print "exception: %i" % sys.getrefcount(err) print "SampleException stop: %i" % sys.getrefcount(SampleException)
if __name__ == "__main__":
hristian

Hi, Christian Heimes wrote:
I was able to come up with a minimal test case. It shows that lxml 2.0.7 is fine but 2.0.9 and 2.1.2 are leaking references somewhere.
Thanks, that made it really easy to track down. The modified exception handling code really leaks references to class, value and traceback when no exception is raised. I'll look into fixing it. Stefan

Stefan Behnel wrote:
Thanks, that made it really easy to track down. The modified exception handling code really leaks references to class, value and traceback when no exception is raised.
I'll look into fixing it.
Excellent! Can I expect lxml 2.0.10 and 2.1.3 anytime soon? Christian

Hi, Christian Heimes wrote:
Stefan Behnel wrote:
Thanks, that made it really easy to track down. The modified exception handling code really leaks references to class, value and traceback when no exception is raised.
I'll look into fixing it.
Excellent! Can I expect lxml 2.0.10 and 2.1.3 anytime soon?
2.1.3 was due weeks ago, so, yes, there will be bug fix releases pretty soon. Stefan
participants (2)
-
Christian Heimes
-
Stefan Behnel