mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
1167 lines
62 KiB
HTML
1167 lines
62 KiB
HTML
<!DOCTYPE html PUBLIC "-//w3c//dtd html 4.0 transitional//en">
|
|
<html>
|
|
<head>
|
|
<title>Memory Tools</title>
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
|
|
<style type="TEXT/CSS">
|
|
BODY {background-color: #FFFFFF; color: #000000}
|
|
.num {text-align: right}
|
|
.pos {text-align: right; color: #CC0000}
|
|
.neg {text-align: right; color: #009900}
|
|
.example {margin-left: 30px; border-style: solid; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<center>
|
|
<h1> How to debug memory leaks/refcnt leaks</h1>
|
|
</center>
|
|
|
|
<center> Last update: September 5, 2000 </center>
|
|
|
|
<h2> What tools do we have?</h2>
|
|
The mozilla team has developed a number of memory analysis tools to augment
|
|
commercial tools like Purify. These can help us more quickly spot and fix
|
|
memory leaks and memory bloat (our term for taking up too much memory, aka
|
|
footprint). Here's a list of what we have at our disposal:
|
|
<ul>
|
|
|
|
<li> <b>BloatView </b>- This tool dumps out per-class statistics on the
|
|
total number of refcounts and instances, as well as unreleased refcounts
|
|
and un-deleted instances, and the amount of memory consumed by them. (more
|
|
<a href="#BloatView">below</a>)</li>
|
|
|
|
<li> <b>Boehm GC Leak Detector </b>- The Boehm garbage collector has be
|
|
modified to serve as a leak detector. It's output can be post-processed by
|
|
the "Leak Soup" tool to find the roots of leaked objects. This lets developers
|
|
quickly focus on the key objects that need to be freed, rather than the whole
|
|
graph of objects to which they refer. (more <a href="#Boehm">below</a>)</li>
|
|
|
|
<li> <b>Refcount Tracing</b> - Stack traces can also be dumped out for
|
|
object allocations and refcounting. This information can be post-processed
|
|
by the <a href="http://www.mozilla.org/performance/refcnt-balancer.html">
|
|
Refcount Balancer</a> tool to match up AddRefs and Releases to find the code
|
|
that has missing Releases. (more <a href="#RefcountTracing">below</a>)</li>
|
|
<li><b>Trace-Malloc</b> - A tree of callsites, where paths from the root
|
|
to leaves form stack traces that call malloc, calloc, realloc, and free,
|
|
can by built in-process by building with --enable-trace-malloc and running
|
|
with --trace-malloc <i>filename</i>. As malloc, free, etc. are called,
|
|
log events identifying the function call and its callsite are emitted to
|
|
<i>filename</i>. (more <a href="#TraceMalloc">below</a>).<br>
|
|
</li>
|
|
|
|
<li> <b>Leaky</b> - This tool also dumps out stack traces (in a slightly
|
|
different format) and can again be used to match up AddRefs with Releases
|
|
in order to find missing Release calls. (more <a href="#Leaky">below</a>)</li>
|
|
|
|
<li> <b>Menu items to interactively control Purify</b> - Interactively
|
|
triggering a dump of all leaks or new leaks from within the running application
|
|
is still one of the best ways to debug leaks in your subsystem. Menu items
|
|
for this can be enabled for both viewer and apprunner. (more <a href="#Purify">
|
|
below</a>)</li>
|
|
|
|
</ul>
|
|
More description on each of these will be provided below.
|
|
<h2> How to turn on refcnt/memory logging</h2>
|
|
Assuming you have a build with refcnt logging enabled (we'll tell you how
|
|
to do that next), here's what you have to do to use it. All of the following
|
|
environment variables can be set to any of these values:
|
|
<ul>
|
|
|
|
<li> <b>1</b> - log to stdout</li>
|
|
|
|
<li> <b>2</b> - log to stderr</li>
|
|
|
|
<li> <b><i>filename</i></b> - write log to a file</li>
|
|
|
|
</ul>
|
|
The log environment variables are:
|
|
<blockquote>XPCOM_MEM_BLOAT_LOG</blockquote>
|
|
|
|
<blockquote>
|
|
<blockquote>If this environment variable is set then xpcom will use the
|
|
"bloat" trackers. The bloat trackers gather data for the BloatView output
|
|
that occurs when the program exits, when about:bloat is loaded, or a call
|
|
to nsTraceRefcnt::DumpStatistics is made.
|
|
<p>When an addref/release/ctor/dtor call is made, the data is logged
|
|
and attributed to the particular data type. </p>
|
|
<p>By default enabling this environment variable will cause the BloatView
|
|
software to dump out the entire database of collected data. If all you want
|
|
to see is that data for objects that leaked, set the environment variable
|
|
XPCOM_MEM_LEAK_LOG.</p>
|
|
</blockquote>
|
|
XPCOM_MEM_LEAK_LOG
|
|
<blockquote>This is basically a subset of XPCOM_MEM_BLOAT_LOG, and
|
|
only shows classes that had object that were leaked, instead of statistics
|
|
for all classes.</blockquote>
|
|
XPCOM_MEM_REFCNT_LOG
|
|
<blockquote>Setting this environment variable enables refcount tracing.
|
|
<br>
|
|
Only enable this for severe pain (unless you are using refcount tracing or
|
|
leaky, see below). What this does is to enable logging (to stdout) of each
|
|
and every call to addref/release without discrimination to the types involved.
|
|
The output includes mapping the call-stacks at the time of the call to symbolic
|
|
forms (on platforms that support this) and thus will be *very* *VERY* *VERY*
|
|
slow. Did I say slow? It is not as slow when using XPCOM_MEM_LOG_CLASSES
|
|
and XPCOM_MEM_LOG_OBJECTS</blockquote>
|
|
XPCOM_MEM_COMPTR_LOG
|
|
<blockquote>This environment variable enables logging of additions
|
|
and releases of objects into nsCOMPtrs. This is currently only enabled on
|
|
Linux.</blockquote>
|
|
XPCOM_MEM_ALLOC_LOG
|
|
<blockquote>For losing architectures (those that don't have stack-crawl
|
|
software written for them), xpcom supports logging at the *call site* to AddRef/Release
|
|
using the usual cpp __FILE__ and __LINE__ number macro expansion hackery.
|
|
This results in slower code, but at least you get *some* data about where
|
|
the leaks might be occurring from.</blockquote>
|
|
XPCOM_MEM_LEAKY_LOG
|
|
<blockquote>For platforms that support leaky, xpcom will endeavor
|
|
to find at run time the symbols "__log_addref" and "__log_release" and if
|
|
found, instead of doing the slow painful stack crawls at program execution
|
|
time instead it will pass the buck to the leaky software. This will allow
|
|
your program to actually run in user friendly real time, but does require
|
|
that your platform support leaky. Currently only linux supports leaky.</blockquote>
|
|
</blockquote>
|
|
In addition, the following variable may be set to a list of class names:
|
|
<blockquote>XPCOM_MEM_LOG_CLASSES</blockquote>
|
|
|
|
<blockquote>
|
|
<blockquote>Instead of slowing to a useless, instead
|
|
you can slow to a meer crawl by using this option. When enabled, the xpcom
|
|
logging software will look for the XPCOM_MEM_LOG_CLASSES environment variable
|
|
(for platforms that support getenv). The variable contains a comma seperated
|
|
list of names which will be used to compare against the type's of the objects
|
|
being logged. For example:
|
|
<blockquote>env XPCOM_MEM_LOG_CLASSES=nsWebShell XPCOM_MEM_REFCNT_LOG=1
|
|
./apprunner</blockquote>
|
|
will show you just the AddRef/Release calls to instances of nsWebShell while
|
|
running apprunner.Note that setting XPCOM_MEM_LOG_CLASSES will also list
|
|
the <i>serial number</i> of each object that leaked in the "bloat log" (that
|
|
is, the file specified by the XPCOM_MEM_BLOAT_LOG variable). An object's
|
|
serial number is simply a unique number, starting at one, that is assigned
|
|
to the object when it is allocated. </blockquote>
|
|
</blockquote>
|
|
You may use an object's serial number with the following variable to further
|
|
restrict the reference count tracing:
|
|
<blockquote>XPCOM_MEM_LOG_OBJECTS</blockquote>
|
|
|
|
<blockquote>
|
|
<blockquote>Set this variable to a comma-separated
|
|
list of object <i>serial number</i> or ranges of <i>serial number</i>, e.g.,
|
|
<code>1,37-42,73,165</code>. When this is set,
|
|
along with XPCOM_MEM_LOG_CLASSES and XPCOM_MEM_REFCNT_LOG, a stack track
|
|
will be generated for <em>only</em> the specific objects that you list. For
|
|
example,
|
|
<blockquote>env XPCOM_MEM_LOG_CLASSES=nsWebShell
|
|
XPCOM_MEM_LOG_OBJECTS=2 XPCOM_MEM_REFCNT_LOG=1 ./apprunner</blockquote>
|
|
will dump stack traces to the console for the 2nd <code>nsWebShell</code>
|
|
object that gets allocated, and nothing else. </blockquote>
|
|
</blockquote>
|
|
|
|
<hr width="100%">
|
|
<h2> <a name="BloatView"></a>1. BloatView</h2>
|
|
BloatView dumps out per-class statistics on allocations and refcounts, and
|
|
provides gross numbers on the amount of memory being leaked broken down by
|
|
class. Here's a sample of the BloatView output: <pre>== BloatView: ALL (cumulative) LEAK AND BLOAT STATISTICS<br><br> |<------Class----->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|<br> Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev<br> 0 TOTAL 193 2480436 316271 12852 ( 5377.07 +/- 5376.38) 410590 16079 ( 2850.93 +/- 2849.79)<br> 1 StyleSetImpl 32 0 8 0 ( 3.88 +/- 3.15) 6304 0 ( 7.18 +/- 6.63)<br> 2 SinkContext 32 0 19 0 ( 1.87 +/- 1.04) 0 0 ( 0.00 +/- 0.00)<br> 3 nsXPCClasses 12 0 2 0 ( 1.00 +/- 0.71) 41 0 ( 5.57 +/- 4.98)<br> 4 NameSpaceURIKey 8 72 158 9 ( 8.16 +/- 7.62) 0 0 ( 0.00 +/- 0.00)<br> 5 nsSupportsArray 36 11304 2581 314 ( 477.13 +/- 476.53) 9223 314 ( 579.23 +/- 578.64)<br> 6 nsView 96 0 57 0 ( 27.64 +/- 26.98) 0 0 ( 0.00 +/- 0.00)<br> 7 nsEnderDocumentObser 12 0 1 0 ( 0.50 +/- 0.87) 1 0 ( 0.50 +/- 0.87)</pre>
|
|
Here's how you interpret the columns:
|
|
<ul>
|
|
|
|
<li> <b>Class</b> - The name of the class
|
|
in question. (Warning: The class name is truncated to 20 characters.)</li>
|
|
|
|
<li> <b>Bytes Per-Inst </b>- The number
|
|
of bytes returned if you were to write sizeof(<i>Class</i>). Note that this
|
|
number does not reflect any memory held onto by the class, such as internal
|
|
buffers, etc. (E.g. for nsStr -- you're seeing the size of the header struct,
|
|
not the size of the string!)</li>
|
|
|
|
<li> <b>Bytes Leaked</b> - The number of
|
|
bytes per instance times the number of objects leaked (Bytes Per-Inst * Objects
|
|
Rem). Use this number to look for the worst offenders. <font color="#ff0000">
|
|
(Should be zero!)</font></li>
|
|
|
|
<li> <b>Objects Total</b> - The total count
|
|
of objects allocated of a given class.</li>
|
|
|
|
<li> <b>Objects Rem</b> - The number of
|
|
objects allocated of a given class that weren't deleted. <font color="#ff0000">
|
|
(Should be zero!)</font></li>
|
|
|
|
<li> <b>Objects Mean</b> - The mean (average)
|
|
number of objects of a given class across the lifetime of the run. Data points
|
|
are taken whenever new and delete are called (not at regular intervals).</li>
|
|
|
|
<li> <b>Objects StdDev</b> - The standard
|
|
deviation in the mean number of objects allocated.</li>
|
|
|
|
<li> <b>References Total </b>- The total
|
|
number of AddRefs performed on a given class.</li>
|
|
|
|
<li> <b>References Rem</b> - The number
|
|
of references on a given class that weren't Released. <font color="#ff0000">
|
|
(Should be zero!)</font></li>
|
|
|
|
<li> <b>References Mean</b> - The mean
|
|
(average) number of references to a given class across the lifetime of the
|
|
run. Data points are taken whenever AddRef and Release are called (not at
|
|
regular intervals).</li>
|
|
|
|
<li> <b>References StdDev</b> - The standard
|
|
deviation in the mean number of references</li>
|
|
|
|
</ul>
|
|
Interesting things to look for:
|
|
<ul>
|
|
|
|
<li> <b><font color="#ff0000">Are your
|
|
classes in the list?</font></b> - Look! If they aren't, then you're not using
|
|
the NS_IMPL_ADDREF and NS_IMPL_RELEASE (or NS_IMPL_ISUPPORTS which calls
|
|
them) for xpcom objects, or MOZ_COUNT_CTOR and MOZ_COUNT_DTOR for non-xpcom
|
|
objects. Not having your classes in the list is <i>not</i> ok. That means
|
|
no one is looking at them, and we won't be able to tell if someone introduces
|
|
a leak. (see <a href="#Instrumenting">below</a> for how to fix this)</li>
|
|
|
|
<li> <b><font color="#ff0000">The Bytes
|
|
Leaked for your classes should be zero!</font></b> - Need I say more? If
|
|
it isn't, you should use the other tools to fix it.</li>
|
|
|
|
<li> <b>The number of objects remaining
|
|
might be equal to the total number of objects.</b> This could indicate a
|
|
hand-written Release method (that doesn't use the NS_LOG_RELEASE macro from
|
|
nsTraceRefcnt.h), or perhaps you're just not freeing any of the instances
|
|
you've allocated. These sorts of leaks are easy to fix.</li>
|
|
|
|
<li> <b>The total number of objects might
|
|
be 1.</b> This might indicate a global variable or service. Usually this
|
|
will have a large number of refcounts.</li>
|
|
|
|
<li> <b>The number of refcounts might equal
|
|
the total number of objects.</b> This class might be a candidate for a non-xpcom
|
|
object (but be very cautious about changing this if it implements any interfaces
|
|
besides nsISupports).</li>
|
|
|
|
<li> <b>The mean number of objects is much
|
|
lower than the total.</b> This indicates a few objects of this class are
|
|
allocated and then freed, as opposed to the opposite case where there's a
|
|
big build-up of instances that are all freed together (perhaps at the end
|
|
of the program).</li>
|
|
|
|
</ul>
|
|
You can also dump out bloat statistics interactively by typing <a href="about:bloat">
|
|
about:bloat</a> in the location bar, or by using the menu items under the
|
|
QA menu in debug builds. Note that you need to have the XPCOM_MEM_BLOAT_LOG
|
|
or XPCOM_MEM_LEAK_LOG envirionment variable defined first. You can also type
|
|
<a href="about:bloat?new">about:bloat?new</a>
|
|
to get a log since the last time you called it, or <a href="about:bloat?clear">
|
|
about:bloat?clear</a> to clear the current set of statistics completely (use
|
|
this option with caution as it can result in what look like negative refcounts,
|
|
etc). Whenever these options are used, the log data is dumped to a file relative
|
|
to the program's directory:
|
|
<blockquote>bloatlogs/all-1999-10-16-010302.txt
|
|
(a complete log resulting from the about:bloat command) <br>
|
|
bloatlogs/new-1999-10-16-010423.txt (an incremental
|
|
log resulting from the about:bloat?new command)</blockquote>
|
|
|
|
<h3>Viewing, Sorting, and Comparing Bloat
|
|
Logs</h3>
|
|
|
|
<p>You can view one or more bloat logs
|
|
in your browser by running the following program:</p>
|
|
|
|
<blockquote><code>perl</code> <code>
|
|
mozilla/tools/memory/bloattable.pl</code> <i>log1</i> <i>log2</i>
|
|
... <i>logn</i> <code>></code> <i>htmlfile</i></blockquote>
|
|
|
|
<p>This will produce an HTML file that
|
|
contains a table such as:</p>
|
|
|
|
<div class="example">
|
|
<script type="text/javascript">
|
|
var classTables = [
|
|
["nsHashKey", 8, 610568,32000,76321,4000,0,0, 1842400,536,230300,67,0,0, 2457872,568,307234,71,0,0, 1134592,1216,141824,152,0,0, 6045432,34320,755679,4290,0,0],
|
|
["nsLineLayout", 1100, 2200,0,2,0,0,0, 225500,0,205,0,0,0, 402600,0,366,0,0,0, 562100,0,511,0,0,0, 1192400,0,1084,0,0,0],
|
|
["nsLocalFile", 424, 558832,72080,1318,170,10020,171, 19928,1272,47,3,314,5, 1696,424,4,1,46,2, 1272,-424,3,-1,31,-2, 581728,73352,1372,173,10411,176],
|
|
["nsStr", 20, 6261600,222760,313080,11138,0,0, 3781900,48760,189095,2438,0,0, 1120920,13280,56046,664,0,0, 1791340,76160,89567,3808,0,0, 12955760,360960,647788,18048,0,0],
|
|
["nsStyleContextData", 736, 259808,141312,353,192,353,192, 325312,220800,442,300,442,300, 489440,-11040,665,-15,665,-15, 338560,94944,460,129,460,129, 1413120,446016,1920,606,1920,606],
|
|
["nsTextTransformer", 548, 8220,0,15,0,0,0, 469088,0,856,0,0,0, 1414936,0,2582,0,0,0, 1532756,0,2797,0,0,0, 3425000,0,6250,0,0,0]];
|
|
|
|
var srcArray = [
|
|
"var nFiles = 4;",
|
|
"var fileTags = ['blank', 'mozilla', 'yahoo', 'netscape'];",
|
|
"var fileNames = ['blank.txt', 'mozilla.txt', 'yahoo.txt', 'netscape.txt'];",
|
|
"var fileDates = ['Tue Aug 29 14:17:40 2000', 'Tue Aug 29 14:18:42 2000', 'Tue Aug 29 14:19:32 2000', 'Tue Aug 29 14:20:14 2000'];",
|
|
"var totals = [\"TOTAL\", undefined, undefined,1754408,478927,52979,748796,46867, undefined,432556,475598,7579,1871220,7561, undefined,179828,386541,4623,2348377,5122, undefined,404184,263126,9660,1652000,7178, undefined,2770976,1604192,74841,6620393,66728];",
|
|
"var showMode;",
|
|
"var modeName;",
|
|
"var modeNameUpper;",
|
|
"var sortColumn;",
|
|
"function sortCompare(x, y) {",
|
|
"if (sortColumn) {",
|
|
"var xc = x[sortColumn];",
|
|
"var yc = y[sortColumn];",
|
|
"if (xc < yc || xc === undefined && yc !== undefined) return 1;",
|
|
"if (yc < xc || yc === undefined && xc !== undefined) return -1;",
|
|
"}",
|
|
"var x0 = x[0];",
|
|
"var y0 = y[0];",
|
|
"if (x0 > y0 || x0 === undefined && y0 !== undefined) return 1;",
|
|
"if (y0 > x0 || y0 === undefined && x0 !== undefined) return -1;",
|
|
"return 0;",
|
|
"}",
|
|
"function quoteHTML(s) {",
|
|
"s = s.replace(/&/g, '&');",
|
|
"s = s.replace(/\\x3C/g, '<');",
|
|
"s = s.replace(/>/g, '>');",
|
|
"s = s.replace(/ /g, ' ');",
|
|
"return s;",
|
|
"}",
|
|
"function writeFileTable(d) {",
|
|
"d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');",
|
|
"d.writeln('<TR>\\n<TH>Name<\/TH>\\n<TH>File<\/TH>\\n<TH>Date<\/TH>\\n<\/TR>');",
|
|
"for (var i = 0; i < nFiles; i++)",
|
|
"d.writeln('<TR>\\n<TD>'+quoteHTML(fileTags[i])+'<\/TD>\\n<TD><TT>'+quoteHTML(fileNames[i])+'<\/TT><\/TD>\\n<TD>'+quoteHTML(fileDates[i])+'<\/TD>\\n<\/TR>');",
|
|
"d.writeln('<\/TABLE>');",
|
|
"}",
|
|
"function writeReloadLink(d, column, s, rowspan) {",
|
|
"d.write(rowspan == 1 ? '<TH>' : '<TH rowspan='+rowspan+'>');",
|
|
"if (column != sortColumn)",
|
|
"d.write('<A href=\"javascript:reloadSelf('+column+','+showMode+')\">');",
|
|
"d.write(s);",
|
|
"if (column != sortColumn)",
|
|
"d.write('<\/A>');",
|
|
"d.writeln('<\/TH>');",
|
|
"}",
|
|
"function writeClassTableRow(d, row, base, modeName) {",
|
|
"if (modeName) {",
|
|
"d.writeln('<TR>\\n<TH>'+modeName+'<\/TH>');",
|
|
"} else {",
|
|
"d.writeln('<TR>\\n<TD><A href=\"javascript:showRowDetail(\\''+row[0]+'\\')\">'+quoteHTML(row[0])+'<\/A><\/TD>');",
|
|
"var v = row[1];",
|
|
"d.writeln('<TD class=num>'+(v === undefined ? '' : v)+'<\/TD>');",
|
|
"}",
|
|
"for (var i = 0; i != 2; i++) {",
|
|
"var c = base + i;",
|
|
"for (var j = 0; j <= nFiles; j++) {",
|
|
"v = row[c];",
|
|
"var style = 'num';",
|
|
"if (j != nFiles)",
|
|
"if (v > 0) {",
|
|
"style = 'pos';",
|
|
"v = '+'+v;",
|
|
"} else",
|
|
"style = 'neg';",
|
|
"d.writeln('<TD class='+style+'>'+(v === undefined ? '' : v)+'<\/TD>');",
|
|
"c += 6;",
|
|
"}",
|
|
"}",
|
|
"d.writeln('<\/TR>');",
|
|
"}",
|
|
"function writeClassTable(d) {",
|
|
"var base = 2 + showMode*2;",
|
|
"var table = classTables.concat();",
|
|
"table.sort(sortCompare);",
|
|
"d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');",
|
|
"d.writeln('<TR>');",
|
|
"writeReloadLink(d, 0, 'Class Name', 2);",
|
|
"writeReloadLink(d, 1, 'Instance<BR>Size', 2);",
|
|
"d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated<\/TH>');",
|
|
"d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated but not freed<\/TH>\\n<\/TR>');",
|
|
"d.writeln('<TR>');",
|
|
"for (var i = 0; i != 2; i++) {",
|
|
"var c = base + i;",
|
|
"for (var j = 0; j <= nFiles; j++) {",
|
|
"writeReloadLink(d, c, j == nFiles ? 'Total' : quoteHTML(fileTags[j]), 1);",
|
|
"c += 6;",
|
|
"}",
|
|
"}",
|
|
"d.writeln('<\/TR>');",
|
|
"writeClassTableRow(d, totals, base, 0);",
|
|
"for (var r = 0; r < table.length; r++)",
|
|
"writeClassTableRow(d, table[r], base, 0);",
|
|
"d.writeln('<\/TABLE>');",
|
|
"}",
|
|
"var modeNames = [\"byte\", \"object\", \"reference\"];",
|
|
"var modeNamesUpper = [\"Byte\", \"Object\", \"Reference\"];",
|
|
"var styleSheet = '<STYLE type=\"TEXT/CSS\">\\n'+",
|
|
"'BODY {background-color: #FFFFFF; color: #000000}\\n'+",
|
|
"'.num {text-align: right}\\n'+",
|
|
"'.pos {text-align: right; color: #CC0000}\\n'+",
|
|
"'.neg {text-align: right; color: #009900}\\n'+",
|
|
"'<\/STYLE>';",
|
|
"function showHead(d) {",
|
|
"modeName = modeNames[showMode];",
|
|
"modeNameUpper = modeNamesUpper[showMode];",
|
|
"d.writeln('<TITLE>'+modeNameUpper+' Bloats<\/TITLE>');",
|
|
"d.writeln(styleSheet);",
|
|
"}",
|
|
"function showBody(d) {",
|
|
"d.writeln('<H1>'+modeNameUpper+' Bloats<\/H1>');",
|
|
"writeFileTable(d);",
|
|
"d.write('<FORM>');",
|
|
"for (var i = 0; i != 3; i++)",
|
|
"if (i != showMode) {",
|
|
"var newSortColumn = sortColumn;",
|
|
"if (sortColumn >= 2)",
|
|
"newSortColumn = sortColumn + (i-showMode)*2;",
|
|
"d.write('<INPUT type=\"button\" value=\"Show '+modeNamesUpper[i]+'s\" onClick=\"reloadSelf('+newSortColumn+','+i+')\">');",
|
|
"}",
|
|
"d.writeln('<\/FORM>');",
|
|
"d.writeln('<P>The numbers do not include <CODE>malloc<\/CODE>\\'d data such as string contents.<\/P>');",
|
|
"d.writeln('<P>Click on a column heading to sort by that column. Click on a class name to see details for that class.<\/P>');",
|
|
"writeClassTable(d);",
|
|
"}",
|
|
"function showRowDetail(rowName) {",
|
|
"var row;",
|
|
"var i;",
|
|
"if (rowName == \"TOTAL\")",
|
|
"row = totals;",
|
|
"else {",
|
|
"for (i = 0; i < classTables.length; i++)",
|
|
"if (rowName == classTables[i][0]) {",
|
|
"row = classTables[i];",
|
|
"break;",
|
|
"}",
|
|
"}",
|
|
"if (row) {",
|
|
"var w = window.open(\"\", \"ClassTableRowDetails\");",
|
|
"var d = w.document;",
|
|
"d.open();",
|
|
"d.writeln('<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">');",
|
|
"d.writeln('<HTML>\\n<HEAD>\\n<TITLE>'+quoteHTML(rowName)+' bloat details<\/TITLE>');",
|
|
"d.writeln(styleSheet);",
|
|
"d.writeln('<\/HEAD>\\n\\n<BODY>');",
|
|
"d.writeln('<H2>'+quoteHTML(rowName)+'<\/H2>');",
|
|
"if (row[1] !== undefined)",
|
|
"d.writeln('<P>Each instance has '+row[1]+' bytes.<\/P>');",
|
|
"d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');",
|
|
"d.writeln('<TR>\\n<TH><\/TH>\\n<TH colspan='+(nFiles+1)+'>Allocated<\/TH>');",
|
|
"d.writeln('<TH colspan='+(nFiles+1)+'>Allocated but not freed<\/TH>\\n<\/TR>');",
|
|
"d.writeln('<TR>\\n<TH><\/TH>');",
|
|
"for (i = 0; i != 2; i++)",
|
|
"for (var j = 0; j <= nFiles; j++)",
|
|
"d.writeln('<TH>'+(j == nFiles ? 'Total' : quoteHTML(fileTags[j]))+'<\/TH>');",
|
|
"d.writeln('<\/TR>');",
|
|
"for (i = 0; i != 3; i++)",
|
|
"writeClassTableRow(d, row, 2+i*2, modeNamesUpper[i]+'s');",
|
|
"d.writeln('<\/TABLE>\\n<\/BODY>\\n<\/HTML>');",
|
|
"d.close();",
|
|
"}",
|
|
"return undefined;",
|
|
"}",
|
|
"function stringSource(s) {",
|
|
"s = s.replace(/\\\\/g, '\\\\\\\\');",
|
|
"s = s.replace(/\"/g, '\\\\\"');",
|
|
"s = s.replace(/<\\//g, '<\\\\/');",
|
|
"return '\"'+s+'\"';",
|
|
"}",
|
|
"function reloadSelf(n,m) {",
|
|
"var sa = srcArray;",
|
|
"var ss = stringSource;",
|
|
"var ct = classTables;",
|
|
"var i;",
|
|
"document.open();",
|
|
"document.writeln('<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">');",
|
|
"document.writeln('<HTML>\\n<HEAD>\\n<SCRIPT type=\"text/javascript\">');",
|
|
"if (!ct.length)",
|
|
"document.writeln('var classTables = [];');",
|
|
"else {",
|
|
"document.writeln('var classTables = [');",
|
|
"for (i = 0; i < ct.length; i++) {",
|
|
"var row = ct[i];",
|
|
"document.write('[' + ss(row[0]));",
|
|
"for (var j = 1; j < row.length; j++)",
|
|
"document.write(',' + row[j]);",
|
|
"document.writeln(']' + (i == ct.length-1 ? '];' : ','));",
|
|
"}",
|
|
"}",
|
|
"document.writeln('var srcArray = [');",
|
|
"for (i = 0; i < sa.length; i++) {",
|
|
"document.write(ss(sa[i]));",
|
|
"if (i != sa.length-1)",
|
|
"document.writeln(',');",
|
|
"}",
|
|
"document.writeln('];');",
|
|
"document.writeln('eval(srcArray.join(\"\\\\n\"));');",
|
|
"document.writeln('showMode = '+m+';');",
|
|
"document.writeln('sortColumn = '+n+';');",
|
|
"document.writeln('showHead(document);');",
|
|
"document.writeln('<\/SCRIPT>\\n<\/HEAD>\\n\\n<BODY>\\n<SCRIPT type=\"text/javascript\">showBody(document);<\/SCRIPT>\\n<\/BODY>\\n<\/HTML>');",
|
|
"document.close();",
|
|
"return true;",
|
|
"}"];
|
|
|
|
var nFiles = 4;
|
|
var fileTags = ['blank', 'mozilla', 'yahoo', 'netscape'];
|
|
var fileNames = ['blank.txt', 'mozilla.txt', 'yahoo.txt', 'netscape.txt'];
|
|
var fileDates = ['Tue Aug 29 14:17:40 2000', 'Tue Aug 29 14:18:42 2000', 'Tue Aug 29 14:19:32 2000', 'Tue Aug 29 14:20:14 2000'];
|
|
var totals = ["TOTAL", undefined, undefined,1754408,478927,52979,748796,46867, undefined,432556,475598,7579,1871220,7561, undefined,179828,386541,4623,2348377,5122, undefined,404184,263126,9660,1652000,7178, undefined,2770976,1604192,74841,6620393,66728];
|
|
var showMode;
|
|
var modeName;
|
|
var modeNameUpper;
|
|
var sortColumn;
|
|
function sortCompare(x, y) {
|
|
if (sortColumn) {
|
|
var xc = x[sortColumn];
|
|
var yc = y[sortColumn];
|
|
if (xc < yc || xc === undefined && yc !== undefined) return 1;
|
|
if (yc < xc || yc === undefined && xc !== undefined) return -1;
|
|
}
|
|
var x0 = x[0];
|
|
var y0 = y[0];
|
|
if (x0 > y0 || x0 === undefined && y0 !== undefined) return 1;
|
|
if (y0 > x0 || y0 === undefined && x0 !== undefined) return -1;
|
|
return 0;
|
|
}
|
|
function quoteHTML(s) {
|
|
s = s.replace(/&/g, '&');
|
|
s = s.replace(/\x3C/g, '<');
|
|
s = s.replace(/>/g, '>');
|
|
s = s.replace(/ /g, ' ');
|
|
return s;
|
|
}
|
|
function writeClassTableRow(d, row, base, modeName) {
|
|
if (modeName) {
|
|
d.writeln('<TR>\n<TH>'+modeName+'<\/TH>');
|
|
} else {
|
|
d.writeln('<TR>\n<TD><A href="javascript:showRowDetail(\''+row[0]+'\')">'+quoteHTML(row[0])+'<\/A><\/TD>');
|
|
var v = row[1];
|
|
d.writeln('<TD class=num>'+(v === undefined ? '' : v)+'<\/TD>');
|
|
}
|
|
for (var i = 0; i != 2; i++) {
|
|
var c = base + i;
|
|
for (var j = 0; j <= nFiles; j++) {
|
|
v = row[c];
|
|
var style = 'num';
|
|
if (j != nFiles)
|
|
if (v > 0) {
|
|
style = 'pos';
|
|
v = '+'+v;
|
|
} else
|
|
style = 'neg';
|
|
d.writeln('<TD class='+style+'>'+(v === undefined ? '' : v)+'<\/TD>');
|
|
c += 6;
|
|
}
|
|
}
|
|
d.writeln('<\/TR>');
|
|
}
|
|
var modeNames = ["byte", "object", "reference"];
|
|
var modeNamesUpper = ["Byte", "Object", "Reference"];
|
|
var styleSheet = '<STYLE type="TEXT/CSS">\n'+
|
|
'BODY {background-color: #FFFFFF; color: #000000}\n'+
|
|
'.num {text-align: right}\n'+
|
|
'.pos {text-align: right; color: #CC0000}\n'+
|
|
'.neg {text-align: right; color: #009900}\n'+
|
|
'<\/STYLE>';
|
|
function showRowDetail(rowName) {
|
|
var row;
|
|
var i;
|
|
if (rowName == "TOTAL")
|
|
row = totals;
|
|
else {
|
|
for (i = 0; i < classTables.length; i++)
|
|
if (rowName == classTables[i][0]) {
|
|
row = classTables[i];
|
|
break;
|
|
}
|
|
}
|
|
if (row) {
|
|
var w = window.open("", "ClassTableRowDetails");
|
|
var d = w.document;
|
|
d.open();
|
|
d.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">');
|
|
d.writeln('<HTML>\n<HEAD>\n<TITLE>'+quoteHTML(rowName)+' bloat details<\/TITLE>');
|
|
d.writeln(styleSheet);
|
|
d.writeln('<\/HEAD>\n\n<BODY>');
|
|
d.writeln('<H2>'+quoteHTML(rowName)+'<\/H2>');
|
|
if (row[1] !== undefined)
|
|
d.writeln('<P>Each instance has '+row[1]+' bytes.<\/P>');
|
|
d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');
|
|
d.writeln('<TR>\n<TH><\/TH>\n<TH colspan='+(nFiles+1)+'>Allocated<\/TH>');
|
|
d.writeln('<TH colspan='+(nFiles+1)+'>Allocated but not freed<\/TH>\n<\/TR>');
|
|
d.writeln('<TR>\n<TH><\/TH>');
|
|
for (i = 0; i != 2; i++)
|
|
for (var j = 0; j <= nFiles; j++)
|
|
d.writeln('<TH>'+(j == nFiles ? 'Total' : quoteHTML(fileTags[j]))+'<\/TH>');
|
|
d.writeln('<\/TR>');
|
|
for (i = 0; i != 3; i++)
|
|
writeClassTableRow(d, row, 2+i*2, modeNamesUpper[i]+'s');
|
|
d.writeln('<\/TABLE>\n<\/BODY>\n<\/HTML>');
|
|
d.close();
|
|
}
|
|
return undefined;
|
|
}
|
|
function stringSource(s) {
|
|
s = s.replace(/\\/g, '\\\\');
|
|
s = s.replace(/"/g, '\\"');
|
|
s = s.replace(/<\//g, '<\\/');
|
|
return '"'+s+'"';
|
|
}
|
|
function reloadSelf(n,m) {
|
|
var sa = srcArray;
|
|
var ss = stringSource;
|
|
var ct = classTables;
|
|
var i;
|
|
var w = window.open("", "BloatTableDemo");
|
|
var document = w.document;
|
|
document.open();
|
|
document.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">');
|
|
document.writeln('<HTML>\n<HEAD>\n<SCRIPT type="text/javascript">');
|
|
if (!ct.length)
|
|
document.writeln('var classTables = [];');
|
|
else {
|
|
document.writeln('var classTables = [');
|
|
for (i = 0; i < ct.length; i++) {
|
|
var row = ct[i];
|
|
document.write('[' + ss(row[0]));
|
|
for (var j = 1; j < row.length; j++)
|
|
document.write(',' + row[j]);
|
|
document.writeln(']' + (i == ct.length-1 ? '];' : ','));
|
|
}
|
|
}
|
|
document.writeln('var srcArray = [');
|
|
for (i = 0; i < sa.length; i++) {
|
|
document.write(ss(sa[i]));
|
|
if (i != sa.length-1)
|
|
document.writeln(',');
|
|
}
|
|
document.writeln('];');
|
|
document.writeln('eval(srcArray.join("\\n"));');
|
|
document.writeln('showMode = '+m+';');
|
|
document.writeln('sortColumn = '+n+';');
|
|
document.writeln('showHead(document);');
|
|
document.writeln('<\/SCRIPT>\n<\/HEAD>\n\n<BODY>\n<SCRIPT type="text/javascript">showBody(document);<\/SCRIPT>\n<\/BODY>\n<\/HTML>');
|
|
document.close();
|
|
return undefined;
|
|
}
|
|
|
|
showMode = 0;
|
|
sortColumn = 26;
|
|
</script>
|
|
<h2>Byte Bloats</h2>
|
|
|
|
<table border="1" cellspacing="1" cellpadding="0">
|
|
<tbody>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>File</th>
|
|
<th>Date</th>
|
|
</tr>
|
|
<tr>
|
|
<td>blank</td>
|
|
<td><tt>blank.txt</tt></td>
|
|
<td>Tue Aug 29 14:17:40 2000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>mozilla</td>
|
|
<td><tt>mozilla.txt</tt></td>
|
|
<td>Tue Aug 29 14:18:42 2000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>yahoo</td>
|
|
<td><tt>yahoo.txt</tt></td>
|
|
<td>Tue Aug 29 14:19:32 2000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>netscape</td>
|
|
<td><tt>netscape.txt</tt></td>
|
|
<td>Tue Aug 29 14:20:14 2000</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<form><input type="button" value="Show Objects" onclick="reloadSelf(28,1)"><input type="button" value="Show References" onclick="reloadSelf(30,2)"></form>
|
|
|
|
<p>The numbers do not include <code>malloc</code>
|
|
'd data such as string contents.</p>
|
|
|
|
<p>Click on a column heading to sort
|
|
by that column. Click on a class name to see details for that class.</p>
|
|
|
|
<table border="1" cellspacing="1" cellpadding="0">
|
|
<tbody>
|
|
<tr>
|
|
<th rowspan="2"><a href="javascript:reloadSelf(0,0)">Class Name</a></th>
|
|
<th rowspan="2"><a href="javascript:reloadSelf(1,0)">Instance<br>
|
|
Size</a></th>
|
|
<th colspan="5">Bytes allocated</th>
|
|
<th colspan="5">Bytes allocated but not freed</th>
|
|
</tr>
|
|
<tr>
|
|
<th><a href="javascript:reloadSelf(2,0)">blank</a></th>
|
|
<th><a href="javascript:reloadSelf(8,0)">mozilla</a></th>
|
|
<th><a href="javascript:reloadSelf(14,0)">yahoo</a></th>
|
|
<th><a href="javascript:reloadSelf(20,0)">netscape</a></th>
|
|
<th>Total</th>
|
|
<th><a href="javascript:reloadSelf(3,0)">blank</a></th>
|
|
<th><a href="javascript:reloadSelf(9,0)">mozilla</a></th>
|
|
<th><a href="javascript:reloadSelf(15,0)">yahoo</a></th>
|
|
<th><a href="javascript:reloadSelf(21,0)">netscape</a></th>
|
|
<th><a href="javascript:reloadSelf(27,0)">Total</a></th>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('TOTAL')">TOTAL</a></td>
|
|
<td class="num"><br>
|
|
</td>
|
|
<td class="neg"><br>
|
|
</td>
|
|
<td class="neg"><br>
|
|
</td>
|
|
<td class="neg"><br>
|
|
</td>
|
|
<td class="neg"><br>
|
|
</td>
|
|
<td class="num"><br>
|
|
</td>
|
|
<td class="pos">+1754408</td>
|
|
<td class="pos">+432556</td>
|
|
<td class="pos">+179828</td>
|
|
<td class="pos">+404184</td>
|
|
<td class="num">2770976</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('nsStr')">nsStr</a></td>
|
|
<td class="num">20</td>
|
|
<td class="pos">+6261600</td>
|
|
<td class="pos">+3781900</td>
|
|
<td class="pos">+1120920</td>
|
|
<td class="pos">+1791340</td>
|
|
<td class="num">12955760</td>
|
|
<td class="pos">+222760</td>
|
|
<td class="pos">+48760</td>
|
|
<td class="pos">+13280</td>
|
|
<td class="pos">+76160</td>
|
|
<td class="num">360960</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('nsHashKey')">nsHashKey</a></td>
|
|
<td class="num">8</td>
|
|
<td class="pos">+610568</td>
|
|
<td class="pos">+1842400</td>
|
|
<td class="pos">+2457872</td>
|
|
<td class="pos">+1134592</td>
|
|
<td class="num">6045432</td>
|
|
<td class="pos">+32000</td>
|
|
<td class="pos">+536</td>
|
|
<td class="pos">+568</td>
|
|
<td class="pos">+1216</td>
|
|
<td class="num">34320</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('nsTextTransformer')">nsTextTransformer</a></td>
|
|
<td class="num">548</td>
|
|
<td class="pos">+8220</td>
|
|
<td class="pos">+469088</td>
|
|
<td class="pos">+1414936</td>
|
|
<td class="pos">+1532756</td>
|
|
<td class="num">3425000</td>
|
|
<td class="neg">0</td>
|
|
<td class="neg">0</td>
|
|
<td class="neg">0</td>
|
|
<td class="neg">0</td>
|
|
<td class="num">0</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('nsStyleContextData')">nsStyleContextData</a></td>
|
|
<td class="num">736</td>
|
|
<td class="pos">+259808</td>
|
|
<td class="pos">+325312</td>
|
|
<td class="pos">+489440</td>
|
|
<td class="pos">+338560</td>
|
|
<td class="num">1413120</td>
|
|
<td class="pos">+141312</td>
|
|
<td class="pos">+220800</td>
|
|
<td class="neg">-11040</td>
|
|
<td class="pos">+94944</td>
|
|
<td class="num">446016</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('nsLineLayout')">nsLineLayout</a></td>
|
|
<td class="num">1100</td>
|
|
<td class="pos">+2200</td>
|
|
<td class="pos">+225500</td>
|
|
<td class="pos">+402600</td>
|
|
<td class="pos">+562100</td>
|
|
<td class="num">1192400</td>
|
|
<td class="neg">0</td>
|
|
<td class="neg">0</td>
|
|
<td class="neg">0</td>
|
|
<td class="neg">0</td>
|
|
<td class="num">0</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a href="javascript:showRowDetail('nsLocalFile')">nsLocalFile</a></td>
|
|
<td class="num">424</td>
|
|
<td class="pos">+558832</td>
|
|
<td class="pos">+19928</td>
|
|
<td class="pos">+1696</td>
|
|
<td class="pos">+1272</td>
|
|
<td class="num">581728</td>
|
|
<td class="pos">+72080</td>
|
|
<td class="pos">+1272</td>
|
|
<td class="pos">+424</td>
|
|
<td class="neg">-424</td>
|
|
<td class="num">73352</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<p>The first set of columns, <b>Bytes
|
|
allocated</b>, shows the amount of memory allocated for the first log file
|
|
(<tt>blank.txt</tt>), the difference between the first log file and the
|
|
second (<tt>mozilla.txt</tt>), the difference between the second log file
|
|
and the third (<tt>yahoo.txt</tt>), the difference between the third log
|
|
file and the fourth (<tt>netscape.txt</tt>), and the total amount of memory
|
|
allocated in the fourth log file. These columns provide an idea of how hard
|
|
the memory allocator has to work, but they do not indicate the size of the
|
|
working set.</p>
|
|
|
|
<p>The second set of columns, <b>Bytes
|
|
allocated but not freed</b>, shows the net memory gain or loss by subtracting
|
|
the amount of memory freed from the amount allocated.</p>
|
|
|
|
<p>The <b>Show Objects</b> and <b>Show
|
|
References</b> buttons show the same statistics but counting objects or AddRef'd
|
|
references rather than bytes.</p>
|
|
|
|
<h3>Comparing Bloat Logs</h3>
|
|
You can also compare any two bloat logs (either those produced when the program
|
|
shuts down, or written to the bloatlogs directory) by running the following
|
|
program:
|
|
<blockquote>perl mozilla/tools/tinderbox/bloatdiff.pl
|
|
<previous-log> <current-log></blockquote>
|
|
This will give you output of the form:
|
|
<blockquote> <pre>Bloat/Leak Delta Report<br>Current file: dist/win32_D.OBJ/bin/bloatlogs/all-1999-10-22-133450.txt<br>Previous file: dist/win32_D.OBJ/bin/bloatlogs/all-1999-10-16-010302.txt<br>--------------------------------------------------------------------------<br>CLASS LEAKS delta BLOAT delta<br>--------------------------------------------------------------------------<br>TOTAL 6113530 2.79% 67064808 9.18%<br>StyleContextImpl 265440 81.19% 283584 -26.99%<br>CToken 236500 17.32% 306676 20.64%<br>nsStr 217760 14.94% 5817060 7.63%<br>nsXULAttribute 113048 -70.92% 113568 -71.16%<br>LiteralImpl 53280 26.62% 75840 19.40%<br>nsXULElement 51648 0.00% 51648 0.00%<br>nsProfile 51224 0.00% 51224 0.00%<br>nsFrame 47568 -26.15% 48096 -50.49%<br>CSSDeclarationImpl 42984 0.67% 43488 0.67%</pre>
|
|
</blockquote>
|
|
This "delta report" shows the leak offenders, sorted from most leaks to fewest.
|
|
The delta numbers show the percentage change between runs for the amount
|
|
of leaks and amount of bloat (<font color="#ff0000">negative numbers are
|
|
better!</font>). The bloat number is a metric determined by multiplying the
|
|
total number of objects allocated of a given class by the class size. Note
|
|
that although this isn't necessarily the amount of memory consumed at any
|
|
given time, it does give an indication of how much memory we're consuming.
|
|
The more memory in general, the worse the performance and footprint. The
|
|
percentage 99999.99% will show up indicating an "infinite" amount of leakage.
|
|
This happens when something that didn't leak before is now leaking.
|
|
<h3> Bloat Statistics on Tinderbox</h3>
|
|
Each build rectangle on Tinderbox will soon be capable of displaying the
|
|
total leaks delta and bloat delta percentages from one build to the next.
|
|
Horray! <br>
|
|
<br>
|
|
|
|
<center>
|
|
<table border="1" cols="2" width="100" height="100">
|
|
<tbody>
|
|
<tr>
|
|
<td> <u><font color="#3366ff">warren</font></u></td>
|
|
<td bgcolor="#00cc00"> <u><font color="#3333ff">L</font></u>
|
|
<u><font color="#3333ff">C</font></u> <br>
|
|
L:-3 <br>
|
|
B:+21 </td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
</center>
|
|
|
|
<p>Hmmm. Warren checked in and the
|
|
number of leaks went down by 3%. (Yes!) But the amount of bloat went up by
|
|
21%. (Ouch!) This probably should be investigated further. Sometimes bloat
|
|
can go up because new features were added that just take up more memory (or
|
|
if the set of test URLs were changed, and the activity is different from
|
|
last time), but in general we'd like to see both of these numbers continue
|
|
to go down. You can look at the end of the log (by clicking on the L) to
|
|
see the bloat statistics and delta report for a breakdown of what actually
|
|
happened. <br>
|
|
</p>
|
|
<hr width="100%">
|
|
<h2> <a name="Boehm"></a>2. Boehm
|
|
GC Leak Detector</h2>
|
|
more... <br>
|
|
|
|
<hr width="100%">
|
|
|
|
<h2><a name="RefcountTracing">3. Refcount Tracing</a></h2>
|
|
|
|
<p>Refcount tracing is used to capture stack traces of AddRef and
|
|
Release calls to use with the Refcount Balancer. It is best to set the
|
|
XPCOM_MEM_REFCNT_LOG environment variable to point to a file when using
|
|
it.</p>
|
|
|
|
<p>See <a
|
|
href="http://www.mozilla.org/performance/refcnt-balancer.html">Refcount
|
|
Balancer</a> for more information. Also see the <a
|
|
href="http://mozilla.org/performance/leak-tutorial.html">tutorial on
|
|
finding leaks of XPCOM objects</a>.</p>
|
|
|
|
<hr width="100%">
|
|
<h2><a name="TraceMalloc"></a>4.
|
|
Trace Malloc</h2>
|
|
TraceMalloc captures stack traces of all <tt>malloc</tt>, <tt>calloc</tt>
|
|
, <tt>realloc</tt>, and <tt>free</tt> calls (this currently covers all <tt>
|
|
operator new</tt> and <tt>delete</tt> calls in Mozilla, too). To enable
|
|
TraceMalloc in your build, configure with --enable-trace-malloc. Run
|
|
the resulting mozilla with --trace-malloc <i>filename</i> as an argument,
|
|
and a binary log of events and callsite relations will be written to <i>filename</i>
|
|
. If <i>filename</i> is -, nothing is written, but the TraceMalloc
|
|
machinery keeps track of all live allocations, and builds its tree of callsites
|
|
to keep book on stack backtraces.<br>
|
|
<br>
|
|
Tools such as bloatblame (tools/trace-malloc/bloatblame.c) can be used to process
|
|
<i>filename</i>. Try running
|
|
with the unified output format option, -u. The output is a large HTML
|
|
file that hyperlinks ancestor and descendent libraries, classes, and functions
|
|
that call into one another, attributing malloc blame up and down each graph.
|
|
Bloatblame accumulates allocation counts, and ignores free calls.<br>
|
|
<br>
|
|
If you run with --trace-malloc -, your code can call NS_TraceMallocDumpAllocations(<i>
|
|
pathname</i>) at opportune times, and a human-readable listing of the current
|
|
heap, including stack traces for every allocation, will be written to <i>
|
|
pathname</i>. This file can be post-processed by tools in <a href="http://lxr.mozilla.org/mozilla/source/tools/trace-malloc">
|
|
mozilla/tools/trace-malloc</a> as follows:<br>
|
|
<ul>
|
|
<li><a href="http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/histogram.pl">
|
|
histogram.pl</a>, which produces a type histogram that can be diffed with
|
|
<a href="http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/histogram-diff.sh">
|
|
histogram-diff.sh</a> to produce output that looks like this:</li>
|
|
<ul>
|
|
<li><pre wrap=""> ---- Base ---- ---- Incr ---- ----- Difference ----
|
|
Type Count Bytes Count Bytes Count Bytes %Total
|
|
TOTAL 48942 4754774 76136 6566453 27194 1811679 100.00
|
|
nsTokenAllocator 17 110007 60 388260 43 278253 15.36
|
|
nsImageGTK 476 2197708 341 2366564 -135 168856 9.32
|
|
nsMemCacheRecord 843 45767 2328 124767 1485 79000 4.36
|
|
nsTextNode 209 11704 1614 90384 1405 78680 4.34
|
|
HTMLAttributesImpl 482 14288 2824 88400 2342 74112 4.09
|
|
nsScanner 58 76824 94 146300 36 69476 3.83
|
|
nsScriptError 253 25070 842 91548 589 66478 3.67
|
|
nsHTMLDocument.mReferrer 177 21550 691 85460 514 63910 3.53
|
|
nsHTMLValue 139 7846 1215 68734 1076 60888 3.36
|
|
HTMLContentSink 6 4816 12 57782 6 52966 2.92</pre>
|
|
</li>
|
|
</ul>
|
|
<li><a href="http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/uncategorized.pl">
|
|
uncategorized.pl</a>, which lists all the void* allocations (the ones that
|
|
couldn't be categorized by type), sorted by size.</li>
|
|
</ul>
|
|
Also, your JavaScript can call the following DOM window methods:<br>
|
|
<ul>
|
|
<li><tt>TraceMallocDisable()</tt>
|
|
- turn off tracing, first flushing any buffered log events for all log files.</li>
|
|
<li><tt>TraceMallocEnable()</tt>
|
|
- turn on tracing.</li>
|
|
<li><tt>TraceMallocOpenLogFile(<i>
|
|
filename</i>)</tt> - open a new log file and return its log file descriptor
|
|
(or -1 on error).</li>
|
|
<li><tt>TraceMallocChangeLogFD(<i>
|
|
logfd</i>)</tt> - change the current log file to the one identified by <i>
|
|
logfd</i>, returning the previous fd (so you can maintain a number of open
|
|
files; keep their fds in a JS Array!).wa</li>
|
|
<li><tt>TraceMallocCloseLogFD(<i>
|
|
logfd</i>)</tt> - close the log file identified by <i>logfd</i>, flushing
|
|
its buffer of any events first. If <i>logfd</i> identifies the current
|
|
log file, change the current log file to the default log file given by the
|
|
--trace-malloc command line argument.</li>
|
|
<li><tt>TraceMallocLogTimestamp(<i>
|
|
caption</i>)</tt> - log a timestamp event to the current log file, annotating
|
|
the log even with the <i>caption</i> string.</li>
|
|
<li><tt>TraceMallocDumpAllocations(<i>
|
|
pathname</i>)</tt> - dump a human-readable listing of all traced, live allocations.</li>
|
|
</ul>
|
|
See <a href="http://lxr.mozilla.org/mozilla/source/xpcom/base/nsTraceMalloc.h">
|
|
nsTraceMalloc.h</a> for detailed comments on the log file format.<br>
|
|
<hr width="100%">
|
|
<h2><a name="Leaky"></a>5. Leaky</h2>
|
|
|
|
<h3> Using this stuff with leaky</h3>
|
|
First, setup these environment variables:
|
|
<blockquote>setenv LD_PRELOAD ../lib/libleaky.so
|
|
(assumes you execute apprunner/viewer in the dist/bin directory) <br>
|
|
setenv LIBMALLOC_LOG 8 (tells leaky to log addref/release calls) <br>
|
|
setenv XPCOM_MEM_LEAKY_LOG 1 (use leaky) <br>
|
|
setenv XPCOM_MEM_LOG_CLASSES "a,b,c" (the list of types you care about)</blockquote>
|
|
Then run the viewer or the apprunner and run your test. Then exit it. The
|
|
result will be some large file in your current directory called "malloc-log"
|
|
and a small file called "malloc-map". If these aren't there then somethings
|
|
wrong.
|
|
<p>If it works properly, then you
|
|
now have the tracing data for the problem you are chasing in malloc-log.
|
|
Use leaky to convert it to human readable form and debug away: </p>
|
|
<blockquote>leaky -dRq <viewer|apprunner>
|
|
malloc-log > /tmp/log</blockquote>
|
|
Leaky used to require c++filt, but now it does it itself. With the -R option,
|
|
leaky will only log the refcnts that actually leaked (those that didn't go
|
|
to zero).
|
|
<h3> Leaky environment variables</h3>
|
|
|
|
<blockquote>LD_PRELOAD
|
|
<blockquote>Set this to the
|
|
pathname to libleaky.so if you are using leaky to track memory operations.</blockquote>
|
|
LIBMALLOC_LOG
|
|
<blockquote>Set this to "8"
|
|
to enable leaky to track addref/release calls that are logged by xpcom. Note
|
|
that you must set bit 8 in xpcomrefcnt to connect xpcom's tracing to leakys
|
|
tracing.</blockquote>
|
|
</blockquote>
|
|
|
|
<h3> Sample output</h3>
|
|
Here is what you see when you enable some logging with XPCOM_MEM_LOG_CLASSES
|
|
set to something: <pre>nsWebShell 0x81189f8 Release 5<br>nsWebShell::Release(void)+0x59<br>nsCOMPtr<nsIContentViewerContainer>::~nsCOMPtr(void)+0x34<br>nsChannelListener::OnStartRequest(nsIChannel *, nsISupports *)+0x550<br>nsFileChannel::OnStartRequest(nsIChannel *, nsISupports *)+0x7b<br>nsOnStartRequestEvent::HandleEvent(void)+0x46<br>nsStreamListenerEvent::HandlePLEvent(PLEvent *)+0x62<br>PL_HandleEvent+0x57<br>PL_ProcessPendingEvents+0x90<br>nsEventQueueImpl::ProcessPendingEvents(void)+0x1d<br>nsAppShell::SetDispatchListener(nsDispatchListener *)+0x3e<br>gdk_get_show_events+0xbb<br>g_io_add_watch+0xaa<br>g_get_current_time+0x136<br>g_get_current_time+0x6f1<br>g_main_run+0x81<br>gtk_main+0xb9<br>nsAppShell::Run(void)+0x245<br>nsAppShell::Run(void)+0xc7a92ede<br>nsAppShell::Run(void)+0xc7a9317c<br>__libc_start_main+0xeb</pre>
|
|
|
|
<p>Here is what you see
|
|
when you use the leaky tool to dump out addref/release leaks: </p>
|
|
<p>addref
|
|
082cccc8 0 00000001 --> CViewSourceHTML::AddRef(void)
|
|
CViewSourceHTML::QueryInterface(nsID &, void **) NS_NewViewSourceHTML(nsIDTD
|
|
**) .LM708 GetSharedObjects(void) nsParser::RegisterDTD(nsIDTD *) RDFXMLDataSourceImpl::Refresh(int)
|
|
nsChromeRegistry::InitRegistry(void) nsChromeProtocolHandler::NewChannel(char
|
|
*, nsIURI *, nsILoadGroup *, nsIEventSinkGetter *, nsIChannel **) nsIOService::NewChannelFromURI(char
|
|
*, nsIURI *, nsILoadGroup *, nsIEventSinkGetter *, nsIChannel **) NS_OpenURI(nsIChannel
|
|
**, nsIURI *, nsILoadGroup *, nsIEventSinkGetter *) NS_OpenURI(nsIInputStream
|
|
**, nsIURI *) CSSLoaderImpl::LoadSheet(URLKey &, SheetLoadData *) CSSLoaderImpl::LoadChildSheet(nsICSSStyleSheet
|
|
*, nsIURI *, nsString &, int, int) CSSParserImpl::ProcessImport(int &,
|
|
nsString &, nsString &) CSSParserImpl::ParseImportRule(int &)
|
|
CSSParserImpl::ParseAtRule(int &) CSSParserImpl::Parse(nsIUnicharInputStream
|
|
*, nsIURI *, nsICSSStyleSheet *&) CSSLoaderImpl::ParseSheet(nsIUnicharInputStream
|
|
*, SheetLoadData *, int &, nsICSSStyleSheet *&) CSSLoaderImpl::LoadAgentSheet(nsIURI
|
|
*, nsICSSStyleSheet *&, int &, void (*)(nsICSSStyleSheet *, void *),
|
|
void *) nsLayoutModule::Initialize(void) nsLayoutModule::GetClassObject(nsIComponentManager
|
|
*, nsID &, nsID &, void **) nsNativeComponentLoader::GetFactoryFromModule(nsDll
|
|
*, nsID &, nsIFactory **) nsNativeComponentLoader::GetFactory(nsID &,
|
|
char *, char *, nsIFactory **) .LM1381 nsComponentManagerImpl::FindFactory(nsID
|
|
&, nsIFactory **) nsComponentManagerImpl::CreateInstance(nsID &, nsISupports
|
|
*, nsID &, void **) nsComponentManager::CreateInstance(nsID &, nsISupports
|
|
*, nsID &, void **) RDFXMLDataSourceImpl::Refresh(int) nsChromeRegistry::InitRegistry(void)
|
|
nsChromeProtocolHandler::NewChannel(char *, nsIURI *, nsILoadGroup *, nsIEventSinkGetter
|
|
*, nsIChannel **) nsIOService::NewChannelFromURI(char *, nsIURI *, nsILoadGroup
|
|
*, nsIEventSinkGetter *, nsIChannel **) NS_OpenURI(nsIChannel **, nsIURI
|
|
*, nsILoadGroup *, nsIEventSinkGetter *) nsDocumentBindInfo::Bind(nsIURI *,
|
|
nsILoadGroup *, nsIInputStream *, unsigned short *) nsDocLoaderImpl::LoadDocument(nsIURI
|
|
*, char *, nsIContentViewerContainer *, nsIInputStream *, nsISupports *, unsigned
|
|
int, unsigned int, unsigned short *) nsWebShell::DoLoadURL(nsIURI *, char
|
|
*, nsIInputStream *, unsigned int, unsigned int, unsigned short *) nsWebShell::LoadURI(nsIURI
|
|
*, char *, nsIInputStream *, int, unsigned int, unsigned int, nsISupports
|
|
*, unsigned short *) nsWebShell::LoadURL(unsigned short *, char *, nsIInputStream
|
|
*, int, unsigned int, unsigned int, nsISupports *, unsigned short *) nsWebShell::LoadURL(unsigned
|
|
short *, nsIInputStream *, int, unsigned int, unsigned int, nsISupports *,
|
|
unsigned short *) nsWebShellWindow::Initialize(nsIWebShellWindow *, nsIAppShell
|
|
*, nsIURI *, int, int, nsIXULWindowCallbacks *, int, int, nsWidgetInitData
|
|
&) nsAppShellService::JustCreateTopWindow(nsIWebShellWindow *, nsIURI
|
|
*, int, int, unsigned int, nsIXULWindowCallbacks *, int, int, nsIWebShellWindow
|
|
**) nsAppShellService::CreateTopLevelWindow(nsIWebShellWindow *, nsIURI *,
|
|
int, int, unsigned int, nsIXULWindowCallbacks *, int, int, nsIWebShellWindow
|
|
**) OpenChromURL(char *, int, int) HandleBrowserStartup(nsICmdLineService
|
|
*, nsIPref *, int) DoCommandLines(nsICmdLineService *, int) main1(int, char
|
|
**) main __libc_start_main </p>
|
|
<p> </p>
|
|
<hr width="100%">
|
|
<h2> <a name="Purify"></a>
|
|
5. Purify</h2>
|
|
more... <br>
|
|
|
|
<hr width="100%">
|
|
<h2> How to build xpcom
|
|
with refcnt/memory logging</h2>
|
|
Built into xpcom is the ability to support the debugging of memory leaks.
|
|
By default, an optimized build of xpcom has this disabled. Also by default,
|
|
the debug builds have the logging facilities enabled. You can control either
|
|
of these options by changing environment variables before you build mozilla:
|
|
<blockquote>FORCE_BUILD_REFCNT_LOGGING</blockquote>
|
|
|
|
<blockquote>
|
|
<blockquote>If this
|
|
is defined then regardless of the type of build, refcnt logging (and related
|
|
memory debugging) will be enabled in the build.</blockquote>
|
|
NO_BUILD_REFCNT_LOGGING
|
|
<blockquote>If this
|
|
is defined then regardless of the type of build or of the setting of the
|
|
FORCE_BUILD_REFCNT_LOGGING, no refcnt logging will be enabled and no memory
|
|
debugging will be enabled. This variable overrides FORCE_BUILD_REFCNT_LOGGING.</blockquote>
|
|
</blockquote>
|
|
The remaining discussion assumes that one way or another that xpcom has been
|
|
built with refcnt/memory logging enabled.
|
|
<h2> <a name="Instrumenting"></a>
|
|
How to instrument your objects for refcnt/memory logging</h2>
|
|
First, if your object is an xpcom object and you use the NS_IMPL_ADDREF and
|
|
NS_IMPL_RELEASE (or a variation thereof) macro to implement your AddRef and
|
|
Release methods, then there is nothing you need do. By default, those macros
|
|
support refcnt logging directly.
|
|
<p>If your object
|
|
is not an xpcom object then some manual editing is in order. The following
|
|
sample code shows what must be done: </p>
|
|
<blockquote><b><tt>
|
|
MOZ_DECL_CTOR_COUNTER(MyType)</tt></b>
|
|
<p><b><tt>MyType::MyType()</tt></b>
|
|
<br>
|
|
<b><tt>{</tt></b>
|
|
<br>
|
|
<b><tt>
|
|
MOZ_COUNT_CTOR(MyType);</tt></b> <br>
|
|
<b><tt>}</tt></b>
|
|
</p>
|
|
<p><b><tt>MyType::~MyType()</tt></b>
|
|
<br>
|
|
<b><tt>{</tt></b>
|
|
<br>
|
|
<b><tt>
|
|
MOZ_COUNT_DTOR(MyType);</tt></b> <br>
|
|
<b><tt>}</tt></b></p>
|
|
</blockquote>
|
|
Now currently the MOZ_DECL_CTOR_COUNTER expands to nothing so your code will
|
|
compile if you forget to add it; however, we reserve the right to change
|
|
that so please put it in.
|
|
<h2> What are
|
|
those macros doing for me anyway?</h2>
|
|
|
|
<p><br>
|
|
<b><tt>NS_IMPL_ADDREF</tt></b>
|
|
has this additional line in it: </p>
|
|
<blockquote><b><tt>
|
|
NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this));</tt></b></blockquote>
|
|
What this is doing is logging the addref call using xpcom's nsTraceRefcnt
|
|
class. The implementation of that macro is: <br>
|
|
|
|
<blockquote><b><tt>
|
|
#define NS_LOG_ADDREF(_p, _rc, _type, _size) \</tt></b> <br>
|
|
<b><tt>
|
|
nsTraceRefcnt::LogAddRef((_p), (_rc), (_type), (PRUint32) (_size))</tt></b></blockquote>
|
|
Which as you can see just passes the buck to nsTraceRefcnt. nsTraceRefcnt
|
|
implements the logging support and will track addref/release/ctor/dtor calls
|
|
in a database that it builds up as the program is executing. In a similar
|
|
manner, NS_IMPL_RELEASE uses NS_LOG_RELEASE which uses nsTraceRefcnt::LogRelease.
|
|
<p>For the
|
|
MOZ_DECL_CTOR_COUNTER, MOZ_COUNT_CTOR and MOZ_COUNT_DTOR macros the expansion
|
|
boils down to calls to nsTraceRefcnt::LogCtor and nsTraceRefcnt::LogDtor calls.
|
|
Again, the type of the object is passed in as well as the sizeof of all the
|
|
data type. </p>
|
|
<blockquote><b><tt>
|
|
#define MOZ_COUNT_CTOR(_type)
|
|
\</tt></b> <br>
|
|
<b><tt>
|
|
PR_BEGIN_MACRO
|
|
\</tt></b> <br>
|
|
<b><tt>
|
|
nsTraceRefcnt::LogCtor((void*)this, #_type, sizeof(*this)); \</tt></b>
|
|
<br>
|
|
<b><tt>
|
|
PR_END_MACRO</tt></b>
|
|
<p><b><tt>
|
|
#define MOZ_COUNT_DTOR(_type)
|
|
\</tt></b> <br>
|
|
<b><tt>
|
|
PR_BEGIN_MACRO
|
|
\</tt></b> <br>
|
|
<b><tt>
|
|
nsTraceRefcnt::LogDtor((void*)this, #_type, sizeof(*this)); \</tt></b>
|
|
<br>
|
|
<b><tt>
|
|
PR_END_MACRO</tt></b></p>
|
|
</blockquote>
|
|
|
|
</body>
|
|
</html>
|