pjs/xpcom/doc/MemoryTools.html

1167 строки
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>.&nbsp; 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>&nbsp;&nbsp;&nbsp;&nbsp; |&lt;------Class-----&gt;|&lt;-----Bytes------&gt;|&lt;----------------Objects----------------&gt;|&lt;--------------References--------------&gt;|<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Per-Inst&nbsp;&nbsp; Leaked&nbsp;&nbsp;&nbsp; Total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rem&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mean&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdDev&nbsp;&nbsp;&nbsp;&nbsp; Total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rem&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mean&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdDev<br>&nbsp;&nbsp; 0 TOTAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 193&nbsp; 2480436&nbsp;&nbsp; 316271&nbsp;&nbsp;&nbsp; 12852 ( 5377.07 +/-&nbsp; 5376.38)&nbsp;&nbsp; 410590&nbsp;&nbsp;&nbsp; 16079 ( 2850.93 +/-&nbsp; 2849.79)<br>&nbsp;&nbsp; 1 StyleSetImpl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 3.88 +/-&nbsp;&nbsp;&nbsp;&nbsp; 3.15)&nbsp;&nbsp;&nbsp;&nbsp; 6304&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 7.18 +/-&nbsp;&nbsp;&nbsp;&nbsp; 6.63)<br>&nbsp;&nbsp; 2 SinkContext&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 1.87 +/-&nbsp;&nbsp;&nbsp;&nbsp; 1.04)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 0.00 +/-&nbsp;&nbsp;&nbsp;&nbsp; 0.00)<br>&nbsp;&nbsp; 3 nsXPCClasses&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 1.00 +/-&nbsp;&nbsp;&nbsp;&nbsp; 0.71)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 41&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 5.57 +/-&nbsp;&nbsp;&nbsp;&nbsp; 4.98)<br>&nbsp;&nbsp; 4 NameSpaceURIKey&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 72&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 158&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9 (&nbsp;&nbsp;&nbsp; 8.16 +/-&nbsp;&nbsp;&nbsp;&nbsp; 7.62)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 0.00 +/-&nbsp;&nbsp;&nbsp;&nbsp; 0.00)<br>&nbsp;&nbsp; 5 nsSupportsArray&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 36&nbsp;&nbsp;&nbsp; 11304&nbsp;&nbsp;&nbsp;&nbsp; 2581&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 314 (&nbsp; 477.13 +/-&nbsp;&nbsp; 476.53)&nbsp;&nbsp;&nbsp;&nbsp; 9223&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 314 (&nbsp; 579.23 +/-&nbsp;&nbsp; 578.64)<br>&nbsp;&nbsp; 6 nsView&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp; 27.64 +/-&nbsp;&nbsp;&nbsp; 26.98)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 0.00 +/-&nbsp;&nbsp;&nbsp;&nbsp; 0.00)<br>&nbsp;&nbsp; 7 nsEnderDocumentObser&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 0.50 +/-&nbsp;&nbsp;&nbsp;&nbsp; 0.87)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp;&nbsp;&nbsp; 0.50 +/-&nbsp;&nbsp;&nbsp;&nbsp; 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&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
(a complete log resulting from the about:bloat command) <br>
bloatlogs/new-1999-10-16-010423.txt&nbsp;&nbsp;&nbsp;&nbsp; (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>&nbsp;<code>
mozilla/tools/memory/bloattable.pl</code>&nbsp;<i>log1</i>&nbsp;<i>log2</i>
&nbsp;...&nbsp;<i>logn</i>&nbsp;<code>&gt;</code>&nbsp;<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, '&amp;');",
"s = s.replace(/\\x3C/g, '&lt;');",
"s = s.replace(/>/g, '&gt;');",
"s = s.replace(/ /g, '&nbsp;');",
"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, '&amp;');
s = s.replace(/\x3C/g, '&lt;');
s = s.replace(/>/g, '&gt;');
s = s.replace(/ /g, '&nbsp;');
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&nbsp;Aug&nbsp;29&nbsp;14:17:40&nbsp;2000</td>
</tr>
<tr>
<td>mozilla</td>
<td><tt>mozilla.txt</tt></td>
<td>Tue&nbsp;Aug&nbsp;29&nbsp;14:18:42&nbsp;2000</td>
</tr>
<tr>
<td>yahoo</td>
<td><tt>yahoo.txt</tt></td>
<td>Tue&nbsp;Aug&nbsp;29&nbsp;14:19:32&nbsp;2000</td>
</tr>
<tr>
<td>netscape</td>
<td><tt>netscape.txt</tt></td>
<td>Tue&nbsp;Aug&nbsp;29&nbsp;14:20:14&nbsp;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(&apos;TOTAL&apos;)">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(&apos;nsStr&apos;)">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(&apos;nsHashKey&apos;)">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(&apos;nsTextTransformer&apos;)">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(&apos;nsStyleContextData&apos;)">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(&apos;nsLineLayout&apos;)">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(&apos;nsLocalFile&apos;)">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
&lt;previous-log&gt; &lt;current-log&gt;</blockquote>
This will give you output of the form:
<blockquote> <pre>Bloat/Leak Delta Report<br>Current file:&nbsp; 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&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LEAKS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delta&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BLOAT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delta<br>--------------------------------------------------------------------------<br>TOTAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6113530&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.79%&nbsp;&nbsp; 67064808&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9.18%<br>StyleContextImpl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 265440&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 81.19%&nbsp;&nbsp;&nbsp;&nbsp; 283584&nbsp;&nbsp;&nbsp;&nbsp; -26.99%<br>CToken&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 236500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17.32%&nbsp;&nbsp;&nbsp;&nbsp; 306676&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20.64%<br>nsStr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 217760&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14.94%&nbsp;&nbsp;&nbsp; 5817060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.63%<br>nsXULAttribute&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 113048&nbsp;&nbsp;&nbsp;&nbsp; -70.92%&nbsp;&nbsp;&nbsp;&nbsp; 113568&nbsp;&nbsp;&nbsp;&nbsp; -71.16%<br>LiteralImpl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 53280&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26.62%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 75840&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 19.40%<br>nsXULElement&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 51648&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.00%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 51648&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.00%<br>nsProfile&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 51224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.00%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 51224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.00%<br>nsFrame&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 47568&nbsp;&nbsp;&nbsp;&nbsp; -26.15%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 48096&nbsp;&nbsp;&nbsp;&nbsp; -50.49%<br>CSSDeclarationImpl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 42984&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.67%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 43488&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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>
&nbsp; <br>
&nbsp;
<center>
<table border="1" cols="2" width="100" height="100">
<tbody>
<tr>
<td>&nbsp;<u><font color="#3366ff">warren</font></u></td>
<td bgcolor="#00cc00">&nbsp;&nbsp; <u><font color="#3333ff">L</font></u>
<u><font color="#3333ff">C</font></u> <br>
&nbsp;&nbsp; L:-3&nbsp; <br>
&nbsp;B:+21&nbsp;</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).&nbsp; To enable
TraceMalloc in your build, configure with --enable-trace-malloc.&nbsp; 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>
.&nbsp; 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 (xpcom/base/bloatblame.c) can be used to process
<i>filename</i>.&nbsp; Try running
with the unified output format option, -u.&nbsp; 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.&nbsp;
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>.&nbsp; 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.&nbsp; 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 &lt;viewer|apprunner&gt;
malloc-log &gt; /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&lt;nsIContentViewerContainer&gt;::~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&nbsp;&nbsp;&nbsp;&nbsp;
082cccc8&nbsp;&nbsp;&nbsp;&nbsp; 0 00000001 --&gt; CViewSourceHTML::AddRef(void)
CViewSourceHTML::QueryInterface(nsID &amp;, 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 &amp;, SheetLoadData *) CSSLoaderImpl::LoadChildSheet(nsICSSStyleSheet
*, nsIURI *, nsString &amp;, int, int) CSSParserImpl::ProcessImport(int &amp;,
nsString &amp;, nsString &amp;) CSSParserImpl::ParseImportRule(int &amp;)
CSSParserImpl::ParseAtRule(int &amp;) CSSParserImpl::Parse(nsIUnicharInputStream
*, nsIURI *, nsICSSStyleSheet *&amp;) CSSLoaderImpl::ParseSheet(nsIUnicharInputStream
*, SheetLoadData *, int &amp;, nsICSSStyleSheet *&amp;) CSSLoaderImpl::LoadAgentSheet(nsIURI
*, nsICSSStyleSheet *&amp;, int &amp;, void (*)(nsICSSStyleSheet *, void *),
void *) nsLayoutModule::Initialize(void) nsLayoutModule::GetClassObject(nsIComponentManager
*, nsID &amp;, nsID &amp;, void **) nsNativeComponentLoader::GetFactoryFromModule(nsDll
*, nsID &amp;, nsIFactory **) nsNativeComponentLoader::GetFactory(nsID &amp;,
char *, char *, nsIFactory **) .LM1381 nsComponentManagerImpl::FindFactory(nsID
&amp;, nsIFactory **) nsComponentManagerImpl::CreateInstance(nsID &amp;, nsISupports
*, nsID &amp;, void **) nsComponentManager::CreateInstance(nsID &amp;, nsISupports
*, nsID &amp;, 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
&amp;) 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>&nbsp;
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>&nbsp;
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>
&nbsp;
<blockquote><b><tt>
#define NS_LOG_ADDREF(_p, _rc, _type, _size) \</tt></b> <br>
<b><tt>&nbsp;
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)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
\</tt></b> <br>
<b><tt>
PR_BEGIN_MACRO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
\</tt></b> <br>
<b><tt>
&nbsp; 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)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
\</tt></b> <br>
<b><tt>
PR_BEGIN_MACRO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
\</tt></b> <br>
<b><tt>
&nbsp; nsTraceRefcnt::LogDtor((void*)this, #_type, sizeof(*this)); \</tt></b>
<br>
<b><tt>
PR_END_MACRO</tt></b></p>
</blockquote>
</body>
</html>