зеркало из https://github.com/mozilla/pjs.git
1083 строки
50 KiB
HTML
1083 строки
50 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>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>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.</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
|
|
|
|
|<------Class----->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
|
|
Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev
|
|
0 TOTAL 193 2480436 316271 12852 ( 5377.07 +/- 5376.38) 410590 16079 ( 2850.93 +/- 2849.79)
|
|
1 StyleSetImpl 32 0 8 0 ( 3.88 +/- 3.15) 6304 0 ( 7.18 +/- 6.63)
|
|
2 SinkContext 32 0 19 0 ( 1.87 +/- 1.04) 0 0 ( 0.00 +/- 0.00)
|
|
3 nsXPCClasses 12 0 2 0 ( 1.00 +/- 0.71) 41 0 ( 5.57 +/- 4.98)
|
|
4 NameSpaceURIKey 8 72 158 9 ( 8.16 +/- 7.62) 0 0 ( 0.00 +/- 0.00)
|
|
5 nsSupportsArray 36 11304 2581 314 ( 477.13 +/- 476.53) 9223 314 ( 579.23 +/- 578.64)
|
|
6 nsView 96 0 57 0 ( 27.64 +/- 26.98) 0 0 ( 0.00 +/- 0.00)
|
|
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>
|
|
<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>
|
|
</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>
|
|
<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></TD>
|
|
<TD class=neg></TD>
|
|
<TD class=neg></TD>
|
|
<TD class=neg></TD>
|
|
<TD class=neg></TD>
|
|
<TD class=num></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>
|
|
</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
|
|
Current file: dist/win32_D.OBJ/bin/bloatlogs/all-1999-10-22-133450.txt
|
|
Previous file: dist/win32_D.OBJ/bin/bloatlogs/all-1999-10-16-010302.txt
|
|
--------------------------------------------------------------------------
|
|
CLASS LEAKS delta BLOAT delta
|
|
--------------------------------------------------------------------------
|
|
TOTAL 6113530 2.79% 67064808 9.18%
|
|
StyleContextImpl 265440 81.19% 283584 -26.99%
|
|
CToken 236500 17.32% 306676 20.64%
|
|
nsStr 217760 14.94% 5817060 7.63%
|
|
nsXULAttribute 113048 -70.92% 113568 -71.16%
|
|
LiteralImpl 53280 26.62% 75840 19.40%
|
|
nsXULElement 51648 0.00% 51648 0.00%
|
|
nsProfile 51224 0.00% 51224 0.00%
|
|
nsFrame 47568 -26.15% 48096 -50.49%
|
|
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 cols=2 width="100" height="100" >
|
|
<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>
|
|
</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>
|
|
<HR width="100%">
|
|
<H2>
|
|
<A name="Boehm"></A>2. Boehm GC Leak Detector</H2>
|
|
more...
|
|
<BR>
|
|
<HR width="100%">
|
|
<H2>
|
|
<A name="RefcountTracing"></A>3. Refcount Tracing</H2>
|
|
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>See <A href="http://www.mozilla.org/performance/refcnt-balancer.html">Refcount
|
|
Balancer</A> for more information.
|
|
<P>
|
|
<HR width="100%">
|
|
<H2>
|
|
<A name="Leaky"></A>4. 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:
|
|
<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
|
|
nsWebShell::Release(void)+0x59
|
|
nsCOMPtr<nsIContentViewerContainer>::~nsCOMPtr(void)+0x34
|
|
nsChannelListener::OnStartRequest(nsIChannel *, nsISupports *)+0x550
|
|
nsFileChannel::OnStartRequest(nsIChannel *, nsISupports *)+0x7b
|
|
nsOnStartRequestEvent::HandleEvent(void)+0x46
|
|
nsStreamListenerEvent::HandlePLEvent(PLEvent *)+0x62
|
|
PL_HandleEvent+0x57
|
|
PL_ProcessPendingEvents+0x90
|
|
nsEventQueueImpl::ProcessPendingEvents(void)+0x1d
|
|
nsAppShell::SetDispatchListener(nsDispatchListener *)+0x3e
|
|
gdk_get_show_events+0xbb
|
|
g_io_add_watch+0xaa
|
|
g_get_current_time+0x136
|
|
g_get_current_time+0x6f1
|
|
g_main_run+0x81
|
|
gtk_main+0xb9
|
|
nsAppShell::Run(void)+0x245
|
|
nsAppShell::Run(void)+0xc7a92ede
|
|
nsAppShell::Run(void)+0xc7a9317c
|
|
__libc_start_main+0xeb</PRE>
|
|
<P>Here is what you see when you use the leaky tool to dump out addref/release
|
|
leaks:
|
|
<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>
|
|
<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:
|
|
<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><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></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:
|
|
<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.
|
|
<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></BLOCKQUOTE>
|
|
|
|
</BODY>
|
|
</HTML>
|