зеркало из https://github.com/mozilla/gecko-dev.git
266 строки
17 KiB
HTML
266 строки
17 KiB
HTML
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<meta name="GENERATOR" content="Mozilla/4.61 [en] (X11; I; Linux 2.2.5-22 i686) [Netscape]">
|
|
</head>
|
|
<body>
|
|
|
|
<center>
|
|
<h1>
|
|
How to debug memory leaks/refcnt leaks</h1></center>
|
|
|
|
<center>Last update: October 8th, 1999</center>
|
|
|
|
<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:
|
|
<p>FORCE_BUILD_REFCNT_LOGGING
|
|
<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>
|
|
The remaining discussion assumes that one way or another that xpcom has
|
|
been built with refcnt/memory logging enabled.
|
|
<h2>
|
|
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><b><tt></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><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><b><tt></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>
|
|
|
|
<h2>
|
|
What's it good for?</h2>
|
|
Once you have an instrumented build, you have a fair amount of control
|
|
over what xpcom will do with the logging calls. Here are the options that
|
|
you can set using NSPR_LOG_MODULES. The log variable we use is "xpcomrefcnt" and
|
|
unlike other nspr log variables, it's setting value is a bitfield that
|
|
controls a set of independent options:
|
|
<blockquote>XPCOM_REFCNT_TRACK_BLOAT (0x1)
|
|
<blockquote>If this bit is set then xpcom will use the "bloat" trackers.
|
|
The bloat trackers gather data for the mega turbo bloat vision output that
|
|
occurs when the program exits or a call to nsTraceRefcnt::DumpStatistics
|
|
is done.
|
|
<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 bit will cause the mega turbo bloat vision
|
|
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
|
|
MOZ_DUMP_LEAKS.</blockquote>
|
|
XPCOM_REFCNT_LOG_ALL (0x2)
|
|
<blockquote>Only enable this for severe pain (unless you are using 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?
|
|
<br> </blockquote>
|
|
XPCOM_REFCNT_LOG_SOME (0x4)
|
|
<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 MOZ_TRACE_REFCNT_TYPES 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:
|
|
<p>env MOZ_TRACE_REFCNT_TYPES="nsWebShell" NSPR_LOG_MODULES="xpcomrefcnt:4"
|
|
./apprunner
|
|
<p>will show you just the addref/release calls to instances of nsWebShell
|
|
while running apprunner.</blockquote>
|
|
XPCOM_REFCNT_LOG_TO_LEAKY (0x8)
|
|
<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>
|
|
XPCOM_REFCNT_LOG_CALLS (0x10)
|
|
<br>XPCOM_REFCNT_LOG_NEW (0x20)
|
|
<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>
|
|
</blockquote>
|
|
|
|
<h2>
|
|
Using this stuff with leaky</h2>
|
|
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 NSPR_LOG_MODULES "xpcomrefcnt:12" (some logging, use leaky)
|
|
<br>setenv MOZ_TRACE_REFCNT_TYPES "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).
|
|
<h2>
|
|
List of environment variables</h2>
|
|
|
|
<blockquote>NSPR_LOG_MODULES
|
|
<blockquote>Set this to include "xpcomrefcnt:<value>"</blockquote>
|
|
MOZ_TRACE_REFCNT_TYPES
|
|
<blockquote>Set this to the list of types that you want traced. Only used
|
|
when bit 4 is set in xpcomrefcnt</blockquote>
|
|
<br>
|
|
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>
|
|
MOZ_DUMP_LEAKS
|
|
<blockquote>Set this to anything to change the output from turbo mega bloat
|
|
vision from dumping everything to just dumping the objects that actually
|
|
leaked.</blockquote>
|
|
</blockquote>
|
|
|
|
<h2>
|
|
Sample output</h2>
|
|
Here is what you get out of turbo mega bloat vision:
|
|
<pre> Bloaty: Refcounting and Memory Bloat Statistics
|
|
|<-------Name------>|<--------------References-------------->|<----------------Objects---------------->|<------Size----->|
|
|
Rem Total Mean StdDev Rem Total Mean StdDev Per-Class Rem
|
|
1 nsAttributeContent 3 25 ( 5.70 +/- 5.14) 3 3 ( 2.00 +/- 1.29) 44 132
|
|
4 NameSpaceURIKey 0 0 ( nan +/- nan) 8 153 ( 0.00 +/- nan) 8 64
|
|
5 nsSupportsArray 25 11690 ( 674.48 +/- 673.88) 25 2572 ( 492.72 +/- 492.07) 36 900
|
|
9 nsXPCWrappedJS 1 4 ( 1.71 +/- 0.97) 1 1 ( 1.00 +/- 0.00) 28 28</pre>
|
|
|
|
<p><br>Here is what you see when you enable some logging with MOZ_TRACE_REFCNT_TYPES set
|
|
to something:
|
|
<p>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
|
|
<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
|
|
<br> GetSharedObjects(void) nsParser::RegisterDTD(nsIDTD *) RDFXMLDataSourceImpl::Refresh(int)
|
|
nsChromeRegistry::InitRegistry(void) nsChromeProtocolHandler::New
|
|
<br>Channel(char *, nsIURI *, nsILoadGroup *, nsIEventSinkGetter *, nsIChannel
|
|
**) nsIOService::NewChannelFromURI(char *, nsIURI *, nsILoadGroup *, nsIEventSink
|
|
<br>Getter *, nsIChannel **) NS_OpenURI(nsIChannel **, nsIURI *, nsILoadGroup
|
|
*, nsIEventSinkGetter *) NS_OpenURI(nsIInputStream **, nsIURI *) CSSLoaderImpl::Lo
|
|
<br>adSheet(URLKey &, SheetLoadData *) CSSLoaderImpl::LoadChildSheet(nsICSSStyleSheet
|
|
*, nsIURI *, nsString &, int, int) CSSParserImpl::ProcessImport(int
|
|
&, nsS
|
|
<br>tring &, nsString &) CSSParserImpl::ParseImportRule(int &)
|
|
CSSParserImpl::ParseAtRule(int &) CSSParserImpl::Parse(nsIUnicharInputStream
|
|
*, nsIURI *, nsICSSS
|
|
<br>tyleSheet *&) CSSLoaderImpl::ParseSheet(nsIUnicharInputStream *,
|
|
SheetLoadData *, int &, nsICSSStyleSheet *&) CSSLoaderImpl::LoadAgentSheet(nsIURI
|
|
*, nsICSS
|
|
<br>StyleSheet *&, int &, void (*)(nsICSSStyleSheet *, void *),
|
|
void *) nsLayoutModule::Initialize(void) nsLayoutModule::GetClassObject(nsIComponentManager
|
|
*, n
|
|
<br>sID &, nsID &, void **) nsNativeComponentLoader::GetFactoryFromModule(nsDll
|
|
*, nsID &, nsIFactory **) nsNativeComponentLoader::GetFactory(nsID
|
|
&, char *, ch
|
|
<br>ar *, nsIFactory **) .LM1381 nsComponentManagerImpl::FindFactory(nsID
|
|
&, nsIFactory **) nsComponentManagerImpl::CreateInstance(nsID &,
|
|
nsISupports *, nsID &
|
|
<br>, void **) nsComponentManager::CreateInstance(nsID &, nsISupports
|
|
*, nsID &, void **) RDFXMLDataSourceImpl::Refresh(int) nsChromeRegistry::InitRegistry(void
|
|
<br>) nsChromeProtocolHandler::NewChannel(char *, nsIURI *, nsILoadGroup
|
|
*, nsIEventSinkGetter *, nsIChannel **) nsIOService::NewChannelFromURI(char
|
|
*, nsIURI *
|
|
<br>, nsILoadGroup *, nsIEventSinkGetter *, nsIChannel **) NS_OpenURI(nsIChannel
|
|
**, nsIURI *, nsILoadGroup *, nsIEventSinkGetter *) nsDocumentBindInfo::Bind(ns
|
|
<br>IURI *, nsILoadGroup *, nsIInputStream *, unsigned short *) nsDocLoaderImpl::LoadDocument(nsIURI
|
|
*, char *, nsIContentViewerContainer *, nsIInputStream *, n
|
|
<br>sISupports *, unsigned int, unsigned int, unsigned short *) nsWebShell::DoLoadURL(nsIURI
|
|
*, char *, nsIInputStream *, unsigned int, unsigned int, unsigned s
|
|
<br>hort *) nsWebShell::LoadURI(nsIURI *, char *, nsIInputStream *, int,
|
|
unsigned int, unsigned int, nsISupports *, unsigned short *) nsWebShell::LoadURL(unsign
|
|
<br>ed short *, char *, nsIInputStream *, int, unsigned int, unsigned int,
|
|
nsISupports *, unsigned short *) nsWebShell::LoadURL(unsigned short *,
|
|
nsIInputStream
|
|
<br> *, int, unsigned int, unsigned int, nsISupports *, unsigned short
|
|
*) nsWebShellWindow::Initialize(nsIWebShellWindow *, nsIAppShell *, nsIURI
|
|
*, int, int, n
|
|
<br>sIXULWindowCallbacks *, int, int, nsWidgetInitData &) nsAppShellService::JustCreateTopWindow(nsIWebShellWindow
|
|
*, nsIURI *, int, int, unsigned int, nsIXULWi
|
|
<br>ndowCallbacks *, int, int, nsIWebShellWindow **) nsAppShellService::CreateTopLevelWindow(nsIWebShellWindow
|
|
*, nsIURI *, int, int, unsigned int, nsIXULWindow
|
|
<br>Callbacks *, int, int, nsIWebShellWindow **) OpenChromURL(char *, int,
|
|
int) HandleBrowserStartup(nsICmdLineService *, nsIPref *, int) DoCommandLines(nsICmdL
|
|
<br>ineService *, int) main1(int, char **) main __libc_start_main
|
|
<br>
|
|
</body>
|
|
</html>
|