Confirmed users
729
edits
|  (→Tips:  Add tips section) |  (Rearrange and update) | ||
| Line 1: | Line 1: | ||
| ==  | == Strategy for finding leaks == | ||
| When trying to make a particular testcase not leak, I recommend focusing first on the largest object graphs (since these entrain many smaller objects), then on smaller reference-counted object graphs, and then on any remaining individual objects or small object graphs that don't entrain other objects. | When trying to make a particular testcase not leak, I recommend focusing first on the largest object graphs (since these entrain many smaller objects), then on smaller reference-counted object graphs, and then on any remaining individual objects or small object graphs that don't entrain other objects. | ||
| Because (1) large graphs of leaked objects tend to include some objects pointed to by global variables that confuse GC-based leak detectors, which can make leaks look smaller (as in [https://bugzilla.mozilla.org/show_bug.cgi?id=99180 bug 99180]) or hide them completely and (2) large graphs of leaked objects tend to hide smaller ones, it's much better to go after the large graphs of leaks first. A good general pattern for finding and fixing leaks is to start with a task that you want not to leak (for example, reading email). Start finding and fixing leaks by running part of the task under nsTraceRefcnt logging, gradually building up from as little as possible to the complete task, and fixing most of the leaks in the first steps before adding additional steps. (By most of the leaks, I mean the leaks of large numbers of different types of objects or leaks of objects that are known to entrain many non-logged objects such as JS objects. Seeing a leaked GlobalWindowImpl, nsXULPDGlobalObject, nsXBLDocGlobalObject, or nsXPCWrappedJS is a sign that there could be significant numbers of JS objects leaked.) For example, start with bringing up the mail window and closing the window without doing anything. Then go on to selecting a folder, then selecting a message, and then other activities one does while reading mail (deleting messages, etc.). Once you've done this, and it doesn't leak much, then try the action under trace-malloc or Purify or Valgrind to find the leaks of smaller graphs of objects. (When I refer to the size of a graph of objects, I'm referring to the number of objects, not the size in bytes. Leaking many copies of a string could be a very large leak, but the object graphs are small and easy to identify using GC-based leak detection.) | |||
| == What leak tools do we have? == | |||
| === Leak tools for large object graphs === | === Leak tools for large object graphs === | ||
| Line 11: | Line 15: | ||
| ==== leak-monitor ==== | ==== leak-monitor ==== | ||
| [http://dbaron.org/mozilla/leak-monitor/ Leak Monitor] is an extension that detects (when closing windows) the primary ways in which JavaScript code can cause leaks that are not bugs in the native code core.  | [http://dbaron.org/mozilla/leak-monitor/ Leak Monitor] is an extension that detects (when closing windows) the primary ways in which JavaScript code can cause leaks that are not bugs in the native code core.  An example of such a bug would be registering (in each window) a JavaScript-implemented observer with the global observer service and never removing the observers, leaving JavaScript code running in the context of each window registered as an observer until the user quits the browser. | ||
| When a leak is detected, the extension presents the user (or application/extension developer!) with a dialog with information about the leaked objects. (The alerts can also be triggered by bugs in the core code. See the [http://dbaron.org/mozilla/leak-monitor/ extension's homepage] for more details.) | |||
| ==== Cycle Collector Debugging (DEBUG_CC) ==== | ==== Cycle Collector Debugging (DEBUG_CC) ==== | ||
| Line 29: | Line 35: | ||
| === Leak tools for medium-size object graphs === | === Leak tools for medium-size object graphs === | ||
| ==== [http://mozilla.org/performance/refcnt-balancer.html  | ==== trace-refcnt and the [http://mozilla.org/performance/refcnt-balancer.html refcount balancer] ==== | ||
| The refcount balancer consists of the [http://lxr.mozilla.org/seamonkey/source/xpcom/base/nsTraceRefcntImpl.cpp nsTraceRefcnt] code in [http://lxr.mozilla.org/seamonkey/source/xpcom/base/ mozilla/xpcom/base/] and the perl scripts in [http://lxr.mozilla.org/mozilla/source/tools/rb/ mozilla/tools/rb/]. It works by instrumenting refcounting, so every AddRef and Release method created using NS_IMPL_ISUPPORTSn and variants, NS_IMPL_ADDREF, or NS_IMPL_RELEASE is automatically instrumented. It also logs information for non-refcounted objects instrumented using the MOZ_COUNT_CTOR and MOZ_COUNT_DTOR macros. | The refcount balancer consists of the [http://lxr.mozilla.org/seamonkey/source/xpcom/base/nsTraceRefcntImpl.cpp nsTraceRefcnt] code in [http://lxr.mozilla.org/seamonkey/source/xpcom/base/ mozilla/xpcom/base/] and the perl scripts in [http://lxr.mozilla.org/mozilla/source/tools/rb/ mozilla/tools/rb/]. It works by instrumenting refcounting, so every AddRef and Release method created using NS_IMPL_ISUPPORTSn and variants, NS_IMPL_ADDREF, or NS_IMPL_RELEASE is automatically instrumented. It also logs information for non-refcounted objects instrumented using the MOZ_COUNT_CTOR and MOZ_COUNT_DTOR macros. | ||
| Because it is based on instrumentation, it is not reliable for gathering aggregate statistics. (In spite of this, it is  | Because it is based on instrumentation, it is not reliable for gathering aggregate statistics. (In spite of this, it is used for the "RLk" leak stats on tinderbox, so the trace-malloc-based "Lk" numbers are more meaningful.) However, it is by far the best tool we have for ''debugging'' leaks of reference counted objects, which are the leaks in Mozilla that can entrain the largest object graphs. | ||
| These tools work on Windows, Mac (PPC and Intel), and Linux (x86), although  | These tools work on Windows, Mac (PPC and Intel), and Linux (x86), although nsCOMPtr logging doesn't work on Windows (?) and Mac and Linux stack traces require some post-processing (see [[#Post-processing of stack traces|below]]). It can be used on any standard --enable-debug build, or on a --disable-debug build with --enable-logrefcnt. | ||
| See  | '''Using the refcount balancer'''.  To enable summary statistics, simply set XPCOM_MEM_LEAK_LOG to 1 (stdout), 2 (stderr), or a filename.  In this mode, trace-refcnt will tell you what types of objects leaked, but won't tell you why they leaked.  See the [http://www.mozilla.org/performance/leak-tutorial.html tutorial on finding leaks of XPCOM objects] for information on debugging leaks using the refcount balancer. (Be aware that the later sections on leak graphs that include JS objects have been out of date since the XPCDOM landing.) | ||
| ==== leaksoup ==== | ==== leaksoup ==== | ||
| Line 51: | Line 57: | ||
| ==== Trace-malloc ==== | ==== Trace-malloc ==== | ||
| The trace-malloc code consists of the nsTraceMalloc code in [http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/lib/ mozilla/tools/trace-malloc/lib/] and the perl scripts and trace-malloc readers in [http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/ mozilla/tools/trace-malloc/]. It works by overriding / hooking into malloc and free (and related functions) and logging all calls, with stacks. It is probably the best tool we currently have for gathering aggregate memory usage statistics. It is generally more useful for bloat measurement than leak measurement, but it can be used to find leaks. It works on Windows, Linux (x86), and Mac (PPC and Intel).  It can be built by checking out mozilla/tools/trace-malloc/ and building with --enable-trace-malloc. | The trace-malloc code consists of the nsTraceMalloc code in [http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/lib/ mozilla/tools/trace-malloc/lib/] and the perl scripts and trace-malloc readers in [http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/ mozilla/tools/trace-malloc/]. It works by overriding / hooking into malloc and free (and related functions) and logging all calls, with stacks. It is probably the best tool we currently have for gathering aggregate memory usage statistics. It is generally more useful for bloat measurement than leak measurement, but it can be used to find leaks, including leaks of objects not instrumented for trace-refcnt. It works on Windows, Linux (x86), and Mac (PPC and Intel).  It can be built by checking out mozilla/tools/trace-malloc/ and building with --enable-trace-malloc. | ||
| <!-- | |||
| ==== Boehm GC ==== | ==== Boehm GC ==== | ||
| The Boehm GC is a garbage collector for C/C++ programs that can be used to debug leaks in a program that does not require a garbage collector. It can be used with leaksoup or leak-soup.pl to find the roots of graphs of leaked objects. The leak detection tools find leaks using garbage collection while the app is running (which tends to hide many of the largest leaked object graphs, which can usually be reached through global variables). Some #defines can be flipped in xpcom/base/nsLeakDetector.cpp to cause it to print out all objects leaked at shutdown (into a different file). These shutdown leaks will include both one-time shutdown leaks (which aren't really a serious problem, but make it harder to find real leaks) and real leaks that had previously been rooted (as far as the GC was concerned) through a global variable. Some of the trace-malloc tools imitate its output format and therefore some of the processing tools are compatible. It works on Mac (Classic) and Linux (although slightly better on Mac). On Linux it can be built with  | The Boehm GC is a garbage collector for C/C++ programs that can be used to debug leaks in a program that does not require a garbage collector. It can be used with leaksoup or leak-soup.pl to find the roots of graphs of leaked objects. The leak detection tools find leaks using garbage collection while the app is running (which tends to hide many of the largest leaked object graphs, which can usually be reached through global variables). Some #defines can be flipped in xpcom/base/nsLeakDetector.cpp to cause it to print out all objects leaked at shutdown (into a different file). These shutdown leaks will include both one-time shutdown leaks (which aren't really a serious problem, but make it harder to find real leaks) and real leaks that had previously been rooted (as far as the GC was concerned) through a global variable. Some of the trace-malloc tools imitate its output format and therefore some of the processing tools are compatible. It works on Mac (Classic) and Linux (although slightly better on Mac). On Linux it can be built with DASHDASHenable-boehm. On Mac, (?). | ||
| '''Update (2006-06-13): The code integrating the Boehm GC in Mozilla has not been tested for a number of years and is unlikely to work anymore.''' | '''Update (2006-06-13): The code integrating the Boehm GC in Mozilla has not been tested for a number of years and is unlikely to work anymore.''' | ||
| --> | |||
| ==== Purify ==== | ==== Purify ==== | ||
| Line 73: | Line 81: | ||
| === Leak tools for debugging memory growth that is cleaned up on shutdown === | === Leak tools for debugging memory growth that is cleaned up on shutdown === | ||
| It is also possible to have a leak that is visible to the user (but not to many of our leak detection tools) by holding objects longer than one should. (For example, one could store an owning reference to every document ever loaded in an nsISupportsArray owned by a service that is destroyed at shutdown, causing every document to stay around until shutdown.) It is sometimes worth testing for this type of leak, especially if there are known leak problems that are visible to the user. | |||
| ==== trace-malloc with diffbloatdump ==== | |||
| The best way I know to do this is with [http://www.mozilla.org/performance/leak-brownbag.html#enabling-trace-malloc trace-malloc]. In a Linux build with trace-malloc enabled you can dump the existing allocations to a file by calling the function TraceMallocDumpAllocations from JavaScript (which is equivalent to calling NS_TraceMallocDumpAllocations from C. The following web page will allow dumping of allocations: | |||
|   <script type="text/javascript"> |   <script type="text/javascript"> | ||
| Line 87: | Line 95: | ||
| One can then use the script mozilla/tools/trace-malloc/diffbloatdump.pl to compare trace-malloc dumps before and after doing an action that might leak. If there are significant differences, it might be worth examining the call stacks for the destructors of the objects in question to see what is extending their lifetime. (This can be done by examining the unprocessed output of an XPCOM_MEM_REFCNT_LOG, one of the nsTraceRefcnt logs.) | One can then use the script mozilla/tools/trace-malloc/diffbloatdump.pl to compare trace-malloc dumps before and after doing an action that might leak. If there are significant differences, it might be worth examining the call stacks for the destructors of the objects in question to see what is extending their lifetime. (This can be done by examining the unprocessed output of an XPCOM_MEM_REFCNT_LOG, one of the nsTraceRefcnt logs.) | ||
| == Common leak patterns == | |||
| When trying to find a leak of reference-counted objects, there are a number of patterns that could cause the leak: | |||
| # Ownership cycles. The most common source of hard-to-fix leaks is ownership cycles. If you can avoid creating cycles in the first place, please do, since it's often hard to be sure to break the cycle in every last case. Sometimes these cycles extend through JS objects (discussed further below), and since JS is garbage-collected, every pointer acts like an owning pointer and the potential for fan-out is larger. See [https://bugzilla.mozilla.org/show_bug.cgi?id=106860 bug 106860] and [https://bugzilla.mozilla.org/show_bug.cgi?id=84136 bug 84136] for examples. | # Ownership cycles. The most common source of hard-to-fix leaks is ownership cycles. If you can avoid creating cycles in the first place, please do, since it's often hard to be sure to break the cycle in every last case. Sometimes these cycles extend through JS objects (discussed further below), and since JS is garbage-collected, every pointer acts like an owning pointer and the potential for fan-out is larger. See [https://bugzilla.mozilla.org/show_bug.cgi?id=106860 bug 106860] and [https://bugzilla.mozilla.org/show_bug.cgi?id=84136 bug 84136] for examples.  (Is this advice still accurate now that we have a cycle collector? --Jesse) | ||
| # Dropping a reference on the floor by: | # Dropping a reference on the floor by: | ||
| ## Forgetting to release (because you weren't using nsCOMPtr when you should have been): See [https://bugzilla.mozilla.org/show_bug.cgi?id=99180 bug 99180] or [https://bugzilla.mozilla.org/show_bug.cgi?id=93087 bug 93087] for an example or [https://bugzilla.mozilla.org/show_bug.cgi?id=28555 bug 28555] for a slightly more interesting one. This is also a frequent problem around early returns when not using nsCOMPtr. | ## Forgetting to release (because you weren't using nsCOMPtr when you should have been): See [https://bugzilla.mozilla.org/show_bug.cgi?id=99180 bug 99180] or [https://bugzilla.mozilla.org/show_bug.cgi?id=93087 bug 93087] for an example or [https://bugzilla.mozilla.org/show_bug.cgi?id=28555 bug 28555] for a slightly more interesting one. This is also a frequent problem around early returns when not using nsCOMPtr. | ||
| Line 97: | Line 105: | ||
| ## [Obscure] Double-assignment into the same variable: If you release a member variable and then assign into it by calling another function that does the same thing, you can leak the object assigned into the variable by the inner function. (This can happen equally with or without nsCOMPtr.) See [https://bugzilla.mozilla.org/show_bug.cgi?id=38586 bug 38586] and [https://bugzilla.mozilla.org/show_bug.cgi?id=287847 bug 287847] for examples. | ## [Obscure] Double-assignment into the same variable: If you release a member variable and then assign into it by calling another function that does the same thing, you can leak the object assigned into the variable by the inner function. (This can happen equally with or without nsCOMPtr.) See [https://bugzilla.mozilla.org/show_bug.cgi?id=38586 bug 38586] and [https://bugzilla.mozilla.org/show_bug.cgi?id=287847 bug 287847] for examples. | ||
| # Dropping a non-refcounted object on the floor (especially one that owns references to reference counted objects). See [https://bugzilla.mozilla.org/show_bug.cgi?id=109671 bug 109671] for an example. | # Dropping a non-refcounted object on the floor (especially one that owns references to reference counted objects). See [https://bugzilla.mozilla.org/show_bug.cgi?id=109671 bug 109671] for an example. | ||
| # Destructors that should have been virtual: If you expect to override an object's destructor (which includes giving a derived class of it an nsCOMPtr member variable) and delete that object through a pointer to the base class using delete, its destructor better be virtual. (But we have many virtual destructors in the codebase that don't need to  | # Destructors that should have been virtual: If you expect to override an object's destructor (which includes giving a derived class of it an nsCOMPtr member variable) and delete that object through a pointer to the base class using delete, its destructor better be virtual. (But we have many virtual destructors in the codebase that don't need to be -- don't do that.) | ||
| == Debugging leaks that go through XPConnect == | |||
| Many large object graphs that leak go through [http://www.mozilla.org/scriptable/ XPConnect]. This can mean there will be XPConnect wrapper objects showing up as owning the leaked objects, but it doesn't mean it's XPConnect's fault (although that [https://bugzilla.mozilla.org/show_bug.cgi?id=76102 has been known to happen], it's rare). Debugging leaks that go through XPConnect requires a basic understanding of what XPConnect does. XPConnect allows an XPCOM object to be exposed to JavaScript, and it allows certain JavaScript objects to be exposed to C++ code as normal XPCOM objects. | Many large object graphs that leak go through [http://www.mozilla.org/scriptable/ XPConnect]. This can mean there will be XPConnect wrapper objects showing up as owning the leaked objects, but it doesn't mean it's XPConnect's fault (although that [https://bugzilla.mozilla.org/show_bug.cgi?id=76102 has been known to happen], it's rare). Debugging leaks that go through XPConnect requires a basic understanding of what XPConnect does. XPConnect allows an XPCOM object to be exposed to JavaScript, and it allows certain JavaScript objects to be exposed to C++ code as normal XPCOM objects. | ||
| Line 129: | Line 137: | ||
| === Reading the old-style leak stats === | === Reading the old-style leak stats === | ||
| The RLk (nsTraceRefcnt-based) leak stats look like this: | |||
| The  | |||
| <table style="margin: 1em auto" border="1" width="120"><tr><td style="background: #1d1; text-align: center; font: medium monospace; padding: 2em 1em"><span style="text-decoration:underline; color: #00e; cursor:pointer">L</span> <span style="text-decoration:underline; color: #00e; cursor:pointer">C</span><br>RLk:700B</td></tr></table> | <table style="margin: 1em auto" border="1" width="120"><tr><td style="background: #1d1; text-align: center; font: medium monospace; padding: 2em 1em"><span style="text-decoration:underline; color: #00e; cursor:pointer">L</span> <span style="text-decoration:underline; color: #00e; cursor:pointer">C</span><br>RLk:700B</td></tr></table> | ||
| Line 161: | Line 167: | ||
| <table style="margin: 1em auto" border="1" width="120"><tr><td style="background: #1d1; text-align: center; font: medium monospace; padding: 2em 1em"><span style="text-decoration:underline; color: #00e; cursor:pointer">L</span> <span style="text-decoration:underline; color: #00e; cursor:pointer">C</span><br>Lk:382KB<br>MH:7.75MB<br>A:391K</td></tr></table> | <table style="margin: 1em auto" border="1" width="120"><tr><td style="background: #1d1; text-align: center; font: medium monospace; padding: 2em 1em"><span style="text-decoration:underline; color: #00e; cursor:pointer">L</span> <span style="text-decoration:underline; color: #00e; cursor:pointer">C</span><br>Lk:382KB<br>MH:7.75MB<br>A:391K</td></tr></table> | ||
| These statistics are generated using trace-malloc. They therefore give accurate aggregate statistics for all heap allocations during the test. Like the old-style leak statistics, the action tested is loading of a browser window and a run through the bloat URLs (bloaturls.txt). The Lk (leak) number is the total number of bytes (not counting any overhead in the allocator) allocated on the heap and not freed over the entire run. This number (as does the number in the old-style leak statistics) includes shutdown leaks, leaks that happen only once for a run of the browser, but there are more here since most shutdown leaks are not of objects logged by nsTraceRefcnt. The MH (max heap, or a meaningful bloat number that I gave a name other than  | These statistics are generated using trace-malloc. They therefore give accurate aggregate statistics for all heap allocations during the test. Like the old-style leak statistics, the action tested is loading of a browser window and a run through the bloat URLs (bloaturls.txt). The Lk (leak) number is the total number of bytes (not counting any overhead in the allocator) allocated on the heap and not freed over the entire run. This number (as does the number in the old-style leak statistics) includes shutdown leaks, leaks that happen only once for a run of the browser, but there are more here since most shutdown leaks are not of objects logged by nsTraceRefcnt. The MH (max heap, or a meaningful bloat number that I gave a name other than "bloat") is the number of bytes allocated on the heap at the point during the run when the heap was at its maximum size (again, excluding overhead). The A (allocations) number is the total number of allocations over the run, and is an indicator of a subset of performance rather than an indicator of memory use, although high allocation churn could contribute to fragmentation. | ||
| === Running the new-style leak tests === | === Running the new-style leak tests === | ||