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:
More description on each of these will be provided below.
Also see a newer overview of what leak tools we have.
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:
The log environment variables are:
__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.
In addition, the following variable may be set to a list of class names:
env XPCOM_MEM_LOG_CLASSES=nsWebShell XPCOM_MEM_REFCNT_LOG=1 ./apprunner
You may use an object's serial number with the following variable to further restrict the reference count tracing:
1,37-42,73,165 (serial numbers start from 1, not 0). When this is set, along with XPCOM_MEM_LOG_CLASSES and XPCOM_MEM_REFCNT_LOG, a stack track will be generated for only the specific objects that you list. For example,
env XPCOM_MEM_LOG_CLASSES=nsWebShell XPCOM_MEM_LOG_OBJECTS=2 XPCOM_MEM_REFCNT_LOG=1 ./apprunner
nsWebShell object that gets allocated, and nothing else.
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:
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)
Here's how you interpret the columns:
Interesting things to look for:
You can also dump out bloat statistics interactively by typing about:bloat 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 about:bloat?new to get a log since the last time you called it, or about:bloat?clear 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:
bloatlogs/all-1999-10-16-010302.txt (a complete log resulting from the about:bloat command) bloatlogs/new-1999-10-16-010423.txt (an incremental log resulting from the about:bloat?new command)
You can view one or more bloat logs in your browser by running the following program:
perlmozilla/tools/memory/bloattable.pllog1 log2 ... logn>htmlfile
This will produce an HTML file that contains a table similar to the following (but with added JavaScript so you can sort the data by column).
Byte Bloats
Name File Date blank blank.txt Tue Aug 29 14:17:40 2000 mozilla mozilla.txt Tue Aug 29 14:18:42 2000 yahoo yahoo.txt Tue Aug 29 14:19:32 2000 netscape netscape.txt Tue Aug 29 14:20:14 2000 The numbers do not include
malloc'd data such as string contents.Click on a column heading to sort by that column. Click on a class name to see details for that class.
Class Name Instance Size Bytes allocated Bytes allocated but not freed blank
mozilla
yahoo
netscape
Total blank
mozilla
yahoo
netscape
Total
TOTAL
1754408 432556 179828 404184 2770976 nsStr
20 6261600 3781900 1120920 1791340 12955760 222760 48760 13280 76160 360960 nsHashKey
8 610568 1842400 2457872 1134592 6045432 32000 536 568 1216 34320 nsTextTransformer
548 8220 469088 1414936 1532756 3425000 0 0 0 0 0 nsStyleContextData
736 259808 325312 489440 338560 1413120 141312 220800 -11040 94944 446016 nsLineLayout
1100 2200 225500 402600 562100 1192400 0 0 0 0 0 nsLocalFile
424 558832 19928 1696 1272 581728 72080 1272 424 -424 73352
The first set of columns, Bytes allocated, shows the amount of memory allocated for the first log file (blank.txt), the difference between the first log file and the second (mozilla.txt), the difference between the second log file and the third (yahoo.txt), the difference between the third log file and the fourth (netscape.txt), 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.
The second set of columns, Bytes allocated but not freed, shows the net memory gain or loss by subtracting the amount of memory freed from the amount allocated.
The Show Objects and Show References buttons show the same statistics but counting objects or AddRef'd references rather than bytes.
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:
perl mozilla/tools/tinderbox/bloatdiff.pl <previous-log> <current-log>
This will give you output of the form:
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%
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 (negative numbers are better!). 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.
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!
| warren | L C L:-3 B: 21 |
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.
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.
See Refcount Balancer for more information. Also see the tutorial on finding leaks of XPCOM objects.
TraceMalloc captures stack traces of all malloc, calloc , realloc, and free calls (this currently covers all operator new and delete calls in Mozilla, too). To enable TraceMalloc in your build, configure with --enable-trace-malloc. Run the resulting mozilla with --trace-malloc filename as an argument, and a binary log of events and callsite relations will be written to filename . If filename 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.
Tools such as bloatblame (tools/trace-malloc/bloatblame.c) can be used to process filename. Try running with the unified output format option, -u. The output is a large HTML file that hyperlinks ancestor and descendent libraries, classes, and functions that call into one another, attributing malloc blame up and down each graph. Bloatblame accumulates allocation counts, and ignores free calls.
If you run with --trace-malloc -, your code can call NS_TraceMallocDumpAllocations(pathname) at opportune times, and a human-readable listing of the current heap, including stack traces for every allocation, will be written to pathname. This file can be post-processed by tools in mozilla/tools/trace-malloc as follows:
---- 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
Also, your JavaScript can call the following DOM window methods:
See nsTraceMalloc.h for detailed comments on the log file format.
First, setup these environment variables:
setenv LD_PRELOAD ../lib/libleaky.so #assumes you execute apprunner/viewer in the dist/bin directory setenv LIBMALLOC_LOG 8 #tells leaky to log addref/release calls setenv XPCOM_MEM_LEAKY_LOG 1 #use leaky setenv XPCOM_MEM_LOG_CLASSES "a,b,c" #the list of types you care about
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 something's wrong.
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:
leaky -dRq <viewer|apprunner> malloc-log > /tmp/log
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).
Here is what you see when you enable some logging with XPCOM_MEM_LOG_CLASSES set to something:
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
Here is what you see when you use the leaky tool to dump out addref/release leaks:
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
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:
The remaining discussion assumes that one way or another that xpcom has been built with refcnt/memory logging enabled.
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.
If your object is not an xpcom object then some manual editing is in order. The following sample code shows what must be done:
MyType::MyType()
{
MOZ_COUNT_CTOR(MyType);
}
MyType::~MyType()
{
MOZ_COUNT_DTOR(MyType);
}
NS_IMPL_ADDREF has this additional line in it:
NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this));
What this is doing is logging the AddRef call using XPCOM's nsTraceRefcnt class. The implementation of that macro is:
#define NS_LOG_ADDREF(_p, _rc, _type, _size) \
nsTraceRefcnt::LogAddRef((_p), (_rc), (_type), (PRUint32) (_size))
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.
For the 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.
#define MOZ_COUNT_CTOR(_type) \
PR_BEGIN_MACRO \
nsTraceRefcnt::LogCtor((void*)this, #_type, sizeof(*this)); \
PR_END_MACRO
#define MOZ_COUNT_DTOR(_type) \
PR_BEGIN_MACRO \
nsTraceRefcnt::LogDtor((void*)this, #_type, sizeof(*this));
PR_END_MACRO
Page last modified 12:46, 28 Apr 2008 by Jonathan Watt