michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsISupports.idl" michael@0: michael@0: interface nsIDOMWindow; michael@0: interface nsIRunnable; michael@0: interface nsISimpleEnumerator; michael@0: michael@0: /* michael@0: * Memory reporters measure Firefox's memory usage. They are primarily used to michael@0: * generate the about:memory page. You should read michael@0: * https://wiki.mozilla.org/Memory_Reporting before writing a memory michael@0: * reporter. michael@0: */ michael@0: michael@0: [scriptable, function, uuid(3a61be3b-b93b-461a-a4f8-388214f558b1)] michael@0: interface nsIMemoryReporterCallback : nsISupports michael@0: { michael@0: /* michael@0: * The arguments to the callback are as follows. michael@0: * michael@0: * michael@0: * |process| The name of the process containing this reporter. Each michael@0: * reporter initially has "" in this field, indicating that it applies to the michael@0: * current process. (This is true even for reporters in a child process.) michael@0: * When a reporter from a child process is copied into the main process, the michael@0: * copy has its 'process' field set appropriately. michael@0: * michael@0: * michael@0: * |path| The path that this memory usage should be reported under. Paths michael@0: * are '/'-delimited, eg. "a/b/c". michael@0: * michael@0: * Each reporter can be viewed as representing a leaf node in a tree. michael@0: * Internal nodes of the tree don't have reporters. So, for example, the michael@0: * reporters "explicit/a/b", "explicit/a/c", "explicit/d/e", and michael@0: * "explicit/d/f" define this tree: michael@0: * michael@0: * explicit michael@0: * |--a michael@0: * | |--b [*] michael@0: * | \--c [*] michael@0: * \--d michael@0: * |--e [*] michael@0: * \--f [*] michael@0: * michael@0: * Nodes marked with a [*] have a reporter. Notice that the internal michael@0: * nodes are implicitly defined by the paths. michael@0: * michael@0: * Nodes within a tree should not overlap measurements, otherwise the michael@0: * parent node measurements will be double-counted. So in the example michael@0: * above, |b| should not count any allocations counted by |c|, and vice michael@0: * versa. michael@0: * michael@0: * All nodes within each tree must have the same units. michael@0: * michael@0: * If you want to include a '/' not as a path separator, e.g. because the michael@0: * path contains a URL, you need to convert each '/' in the URL to a '\'. michael@0: * Consumers of the path will undo this change. Any other '\' character michael@0: * in a path will also be changed. This is clumsy but hasn't caused any michael@0: * problems so far. michael@0: * michael@0: * The paths of all reporters form a set of trees. Trees can be michael@0: * "degenerate", i.e. contain a single entry with no '/'. michael@0: * michael@0: * michael@0: * |kind| There are three kinds of memory reporters. michael@0: * michael@0: * - HEAP: reporters measuring memory allocated by the heap allocator, michael@0: * e.g. by calling malloc, calloc, realloc, memalign, operator new, or michael@0: * operator new[]. Reporters in this category must have units michael@0: * UNITS_BYTES. michael@0: * michael@0: * - NONHEAP: reporters measuring memory which the program explicitly michael@0: * allocated, but does not live on the heap. Such memory is commonly michael@0: * allocated by calling one of the OS's memory-mapping functions (e.g. michael@0: * mmap, VirtualAlloc, or vm_allocate). Reporters in this category michael@0: * must have units UNITS_BYTES. michael@0: * michael@0: * - OTHER: reporters which don't fit into either of these categories. michael@0: * They can have any units. michael@0: * michael@0: * The kind only matters for reporters in the "explicit" tree; michael@0: * aboutMemory.js uses it to calculate "heap-unclassified". michael@0: * michael@0: * michael@0: * |units| The units on the reporter's amount. One of the following. michael@0: * michael@0: * - BYTES: The amount contains a number of bytes. michael@0: * michael@0: * - COUNT: The amount is an instantaneous count of things currently in michael@0: * existence. For instance, the number of tabs currently open would have michael@0: * units COUNT. michael@0: * michael@0: * - COUNT_CUMULATIVE: The amount contains the number of times some event michael@0: * has occurred since the application started up. For instance, the michael@0: * number of times the user has opened a new tab would have units michael@0: * COUNT_CUMULATIVE. michael@0: * michael@0: * The amount returned by a reporter with units COUNT_CUMULATIVE must michael@0: * never decrease over the lifetime of the application. michael@0: * michael@0: * - PERCENTAGE: The amount contains a fraction that should be expressed as michael@0: * a percentage. NOTE! The |amount| field should be given a value 100x michael@0: * the actual percentage; this number will be divided by 100 when shown. michael@0: * This allows a fractional percentage to be shown even though |amount| is michael@0: * an integer. E.g. if the actual percentage is 12.34%, |amount| should michael@0: * be 1234. michael@0: * michael@0: * Values greater than 100% are allowed. michael@0: * michael@0: * michael@0: * |amount| The numeric value reported by this memory reporter. Accesses michael@0: * can fail if something goes wrong when getting the amount. michael@0: * michael@0: * michael@0: * |description| A human-readable description of this memory usage report. michael@0: */ michael@0: void callback(in ACString process, in AUTF8String path, in int32_t kind, michael@0: in int32_t units, in int64_t amount, michael@0: in AUTF8String description, in nsISupports data); michael@0: }; michael@0: michael@0: /* michael@0: * An nsIMemoryReporter reports one or more memory measurements via a michael@0: * callback function which is called once for each measurement. michael@0: * michael@0: * An nsIMemoryReporter that reports a single measurement is sometimes called a michael@0: * "uni-reporter". One that reports multiple measurements is sometimes called michael@0: * a "multi-reporter". michael@0: * michael@0: * aboutMemory.js is the most important consumer of memory reports. It michael@0: * places the following constraints on reports. michael@0: * michael@0: * - All reports within a single sub-tree must have the same units. michael@0: * michael@0: * - There may be an "explicit" tree. If present, it represents michael@0: * non-overlapping regions of memory that have been explicitly allocated with michael@0: * an OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a michael@0: * heap-level allocation (e.g. malloc/calloc/operator new). Reporters in michael@0: * this tree must have kind HEAP or NONHEAP, units BYTES. michael@0: * michael@0: * It is preferred, but not required, that report descriptions use complete michael@0: * sentences (i.e. start with a capital letter and end with a period, or michael@0: * similar). michael@0: */ michael@0: [scriptable, uuid(0884cd0f-5829-4381-979b-0f53904030ed)] michael@0: interface nsIMemoryReporter : nsISupports michael@0: { michael@0: /* michael@0: * Run the reporter. michael@0: */ michael@0: void collectReports(in nsIMemoryReporterCallback callback, michael@0: in nsISupports data); michael@0: michael@0: /* michael@0: * Kinds. See the |kind| comment in nsIMemoryReporterCallback. michael@0: */ michael@0: const int32_t KIND_NONHEAP = 0; michael@0: const int32_t KIND_HEAP = 1; michael@0: const int32_t KIND_OTHER = 2; michael@0: michael@0: /* michael@0: * Units. See the |units| comment in nsIMemoryReporterCallback. michael@0: */ michael@0: const int32_t UNITS_BYTES = 0; michael@0: const int32_t UNITS_COUNT = 1; michael@0: const int32_t UNITS_COUNT_CUMULATIVE = 2; michael@0: const int32_t UNITS_PERCENTAGE = 3; michael@0: }; michael@0: michael@0: [scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)] michael@0: interface nsIFinishReportingCallback : nsISupports michael@0: { michael@0: void callback(in nsISupports data); michael@0: }; michael@0: michael@0: [scriptable, builtinclass, uuid(b6e5ec8a-71d9-48db-8ae9-68b4c5bbf2c3)] michael@0: interface nsIMemoryReporterManager : nsISupports michael@0: { michael@0: /* michael@0: * Initialize. michael@0: */ michael@0: void init(); michael@0: michael@0: /* michael@0: * Register the given nsIMemoryReporter. The Manager service will hold a michael@0: * strong reference to the given reporter, and will be responsible for freeing michael@0: * the reporter at shutdown. You may manually unregister the reporter with michael@0: * unregisterStrongReporter() at any point. michael@0: */ michael@0: void registerStrongReporter(in nsIMemoryReporter reporter); michael@0: michael@0: /* michael@0: * Like registerReporter, but the Manager service will hold a weak reference michael@0: * via a raw pointer to the given reporter. The reporter should be michael@0: * unregistered before shutdown. michael@0: * You cannot register JavaScript components with this function! Always michael@0: * register your JavaScript components with registerStrongReporter(). michael@0: */ michael@0: void registerWeakReporter(in nsIMemoryReporter reporter); michael@0: michael@0: /* michael@0: * Unregister the given memory reporter, which must have been registered with michael@0: * registerStrongReporter(). You normally don't need to unregister your michael@0: * strong reporters, as nsIMemoryReporterManager will take care of that at michael@0: * shutdown. michael@0: */ michael@0: void unregisterStrongReporter(in nsIMemoryReporter reporter); michael@0: michael@0: /* michael@0: * Unregister the given memory reporter, which must have been registered with michael@0: * registerWeakReporter(). michael@0: */ michael@0: void unregisterWeakReporter(in nsIMemoryReporter reporter); michael@0: michael@0: /* michael@0: * These functions should only be used for testing purposes. michael@0: */ michael@0: void blockRegistrationAndHideExistingReporters(); michael@0: void unblockRegistrationAndRestoreOriginalReporters(); michael@0: void registerStrongReporterEvenIfBlocked(in nsIMemoryReporter aReporter); michael@0: michael@0: /* michael@0: * Get memory reports for the current process and all child processes. michael@0: * |handleReport| is called for each report, and |finishReporting| is called michael@0: * once all reports have been handled. michael@0: * michael@0: * |finishReporting| is called even if, for example, some child processes michael@0: * fail to report back. However, calls to this method will silently and michael@0: * immediately abort -- and |finishReporting| will not be called -- if a michael@0: * previous getReports() call is still in flight, i.e. if it has not yet michael@0: * finished invoking |finishReporting|. The silent abort is because the michael@0: * in-flight request will finish soon, and the caller would very likely just michael@0: * catch and ignore any error anyway. michael@0: */ michael@0: void getReports(in nsIMemoryReporterCallback handleReport, michael@0: in nsISupports handleReportData, michael@0: in nsIFinishReportingCallback finishReporting, michael@0: in nsISupports finishReportingData); michael@0: michael@0: /* michael@0: * As above, but: If |minimizeMemoryUsage| is true, then each process will michael@0: * minimize its memory usage (see the |minimizeMemoryUsage| method) before michael@0: * gathering its report. If DMD is enabled and |DMDDumpIdent| is non-empty michael@0: * then write a DMD report to a file in the usual temporary directory (see michael@0: * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.) michael@0: */ michael@0: [noscript] void michael@0: getReportsExtended(in nsIMemoryReporterCallback handleReport, michael@0: in nsISupports handleReportData, michael@0: in nsIFinishReportingCallback finishReporting, michael@0: in nsISupports finishReportingData, michael@0: in boolean minimizeMemoryUsage, michael@0: in AString DMDDumpIdent); michael@0: michael@0: /* michael@0: * Get memory reports in the current process only. |handleReport| is called michael@0: * for each report. michael@0: */ michael@0: void getReportsForThisProcess(in nsIMemoryReporterCallback handleReport, michael@0: in nsISupports handleReportData); michael@0: michael@0: /* michael@0: * As above, but if DMD is enabled and |DMDDumpIdent| is non-empty michael@0: * then write a DMD report to a file in the usual temporary directory (see michael@0: * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.) michael@0: */ michael@0: [noscript] void michael@0: getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport, michael@0: in nsISupports handleReportData, michael@0: in AString DMDDumpIdent); michael@0: michael@0: /* michael@0: * The memory reporter manager, for the most part, treats reporters michael@0: * registered with it as a black box. However, there are some michael@0: * "distinguished" amounts (as could be reported by a memory reporter) that michael@0: * the manager provides as attributes, because they are sufficiently michael@0: * interesting that we want external code (e.g. telemetry) to be able to rely michael@0: * on them. michael@0: * michael@0: * Note that these are not reporters and so getReports() and michael@0: * getReportsForThisProcess() do not look at them. However, distinguished michael@0: * amounts can be embedded in a reporter. michael@0: * michael@0: * Access to these attributes can fail. In particular, some of them are not michael@0: * available on all platforms. michael@0: * michael@0: * If you add a new distinguished amount, please update michael@0: * toolkit/components/aboutmemory/tests/test_memoryReporters.xul. michael@0: * michael@0: * |explicit| (UNITS_BYTES) The total size of explicit memory allocations, michael@0: * both at the OS-level (eg. via mmap, VirtualAlloc) and at the heap level michael@0: * (eg. via malloc, calloc, operator new). It covers all heap allocations, michael@0: * but will miss any OS-level ones not covered by memory reporters. michael@0: * michael@0: * |vsize| (UNITS_BYTES) The virtual size, i.e. the amount of address space michael@0: * taken up. michael@0: * michael@0: * |vsizeMaxContiguous| (UNITS_BYTES) The size of the largest contiguous michael@0: * block of virtual memory. michael@0: * michael@0: * |resident| (UNITS_BYTES) The resident size (a.k.a. RSS or physical memory michael@0: * used). michael@0: * michael@0: * |residentFast| (UNITS_BYTES) This is like |resident|, but on Mac OS michael@0: * |resident| can purge pages, which is slow. It also affects the result of michael@0: * |residentFast|, and so |resident| and |residentFast| should not be used michael@0: * together. michael@0: * michael@0: * |heapAllocated| (UNITS_BYTES) Memory mapped by the heap allocator. michael@0: * michael@0: * |heapOverheadRatio| (UNITS_PERCENTAGE) In the heap allocator, this is the michael@0: * ratio of committed, unused bytes to allocated bytes. Like all michael@0: * UNITS_PERCENTAGE measurements, its amount is multiplied by 100x so it can michael@0: * be represented by an int64_t. michael@0: * michael@0: * |JSMainRuntimeGCHeap| (UNITS_BYTES) Size of the main JS runtime's GC michael@0: * heap. michael@0: * michael@0: * |JSMainRuntimeTemporaryPeak| (UNITS_BYTES) Peak size of the transient michael@0: * storage in the main JSRuntime. michael@0: * michael@0: * |JSMainRuntimeCompartments{System,User}| (UNITS_COUNT) The number of michael@0: * {system,user} compartments in the main JS runtime. michael@0: * michael@0: * |imagesContentUsedUncompressed| (UNITS_BYTES) Memory used for decoded michael@0: * images in content. michael@0: * michael@0: * |storageSQLite| (UNITS_BYTES) Memory used by SQLite. michael@0: * michael@0: * |lowMemoryEvents{Virtual,Physical}| (UNITS_COUNT_CUMULATIVE) The number michael@0: * of low-{virtual,physical}-memory events that have occurred since the michael@0: * process started. michael@0: * michael@0: * |ghostWindows| (UNITS_COUNT) The number of ghost windows. michael@0: * michael@0: * |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a. michael@0: * major) page faults that have occurred since the process started. michael@0: */ michael@0: readonly attribute int64_t explicit; michael@0: readonly attribute int64_t vsize; michael@0: readonly attribute int64_t vsizeMaxContiguous; michael@0: readonly attribute int64_t resident; michael@0: readonly attribute int64_t residentFast; michael@0: michael@0: readonly attribute int64_t heapAllocated; michael@0: readonly attribute int64_t heapOverheadRatio; michael@0: michael@0: readonly attribute int64_t JSMainRuntimeGCHeap; michael@0: readonly attribute int64_t JSMainRuntimeTemporaryPeak; michael@0: readonly attribute int64_t JSMainRuntimeCompartmentsSystem; michael@0: readonly attribute int64_t JSMainRuntimeCompartmentsUser; michael@0: michael@0: readonly attribute int64_t imagesContentUsedUncompressed; michael@0: michael@0: readonly attribute int64_t storageSQLite; michael@0: michael@0: readonly attribute int64_t lowMemoryEventsVirtual; michael@0: readonly attribute int64_t lowMemoryEventsPhysical; michael@0: michael@0: readonly attribute int64_t ghostWindows; michael@0: michael@0: readonly attribute int64_t pageFaultsHard; michael@0: michael@0: /* michael@0: * This attribute indicates if moz_malloc_usable_size() works. michael@0: */ michael@0: [infallible] readonly attribute boolean hasMozMallocUsableSize; michael@0: michael@0: /* michael@0: * Run a series of GC/CC's in an attempt to minimize the application's memory michael@0: * usage. When we're finished, we invoke the given runnable if it's not michael@0: * null. michael@0: */ michael@0: void minimizeMemoryUsage(in nsIRunnable callback); michael@0: michael@0: /* michael@0: * Measure the memory that is known to be owned by this tab, split up into michael@0: * several broad categories. Note that this will be an underestimate of the michael@0: * true number, due to imperfect memory reporter coverage (corresponding to michael@0: * about:memory's "heap-unclassified"), and due to some memory shared between michael@0: * tabs not being counted. michael@0: * michael@0: * The time taken for the measurement (split into JS and non-JS parts) is michael@0: * also returned. michael@0: */ michael@0: void sizeOfTab(in nsIDOMWindow window, michael@0: out int64_t jsObjectsSize, out int64_t jsStringsSize, michael@0: out int64_t jsOtherSize, out int64_t domSize, michael@0: out int64_t styleSize, out int64_t otherSize, michael@0: out int64_t totalSize, michael@0: out double jsMilliseconds, out double nonJSMilliseconds); michael@0: }; michael@0: michael@0: %{C++ michael@0: michael@0: #include "js/TypeDecls.h" michael@0: #include "nsStringGlue.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/Atomics.h" michael@0: michael@0: class nsPIDOMWindow; michael@0: michael@0: // nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback michael@0: // around for backwards compatibility. michael@0: typedef nsIMemoryReporterCallback nsIHandleReportCallback; michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Register a memory reporter. The manager service will hold a strong michael@0: // reference to this reporter. michael@0: XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter); michael@0: michael@0: // Register a memory reporter. The manager service will hold a weak reference michael@0: // to this reporter. michael@0: XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter); michael@0: michael@0: // Unregister a weak memory reporter. michael@0: XPCOM_API(nsresult) UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter); michael@0: michael@0: // The memory reporter manager provides access to several distinguished michael@0: // amounts via attributes. Some of these amounts are provided by Gecko michael@0: // components that cannot be accessed directly from XPCOM code. So we provide michael@0: // the following functions for those components to be registered with the michael@0: // manager. michael@0: michael@0: typedef int64_t (*InfallibleAmountFn)(); michael@0: typedef nsresult (*FallibleAmountFn)(int64_t* aAmount); michael@0: michael@0: #define DECL_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \ michael@0: nsresult Register##name##DistinguishedAmount(kind##AmountFn aAmountFn); michael@0: #define DECL_UNREGISTER_DISTINGUISHED_AMOUNT(name) \ michael@0: nsresult Unregister##name##DistinguishedAmount(); michael@0: michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap) michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak) michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem) michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser) michael@0: michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed) michael@0: michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite) michael@0: DECL_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite) michael@0: michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual) michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical) michael@0: michael@0: DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows) michael@0: michael@0: #undef DECL_REGISTER_DISTINGUISHED_AMOUNT michael@0: #undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT michael@0: michael@0: // Likewise for per-tab measurement. michael@0: michael@0: typedef nsresult (*JSSizeOfTabFn)(JSObject* aObj, michael@0: size_t* aJsObjectsSize, michael@0: size_t* aJsStringSize, michael@0: size_t* aJsPrivateSize, michael@0: size_t* aJsOtherSize); michael@0: typedef nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindow* aWindow, michael@0: size_t* aDomSize, michael@0: size_t* aStyleSize, michael@0: size_t* aOtherSize); michael@0: michael@0: nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn); michael@0: nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn); michael@0: michael@0: } michael@0: michael@0: #if defined(MOZ_DMD) michael@0: namespace mozilla { michael@0: namespace dmd { michael@0: // This runs all the memory reporters in the current process but does nothing michael@0: // with the results; i.e. it does the minimal amount of work possible for DMD michael@0: // to do its thing. It does nothing with child processes. michael@0: void RunReportersForThisProcess(); michael@0: } michael@0: } michael@0: michael@0: #if !defined(MOZ_MEMORY) michael@0: #error "MOZ_DMD requires MOZ_MEMORY" michael@0: #endif michael@0: michael@0: #include "DMD.h" michael@0: michael@0: #define MOZ_REPORT(ptr) mozilla::dmd::Report(ptr) michael@0: #define MOZ_REPORT_ON_ALLOC(ptr) mozilla::dmd::ReportOnAlloc(ptr) michael@0: michael@0: #else michael@0: michael@0: #define MOZ_REPORT(ptr) michael@0: #define MOZ_REPORT_ON_ALLOC(ptr) michael@0: michael@0: #endif // defined(MOZ_DMD) michael@0: michael@0: // Functions generated via this macro should be used by all traversal-based michael@0: // memory reporters. Such functions return |moz_malloc_size_of(ptr)|; this michael@0: // will always be zero on some obscure platforms. michael@0: // michael@0: // You might be wondering why we have a macro that creates multiple functions michael@0: // that differ only in their name, instead of a single MallocSizeOf function. michael@0: // It's mostly to help with DMD integration, though it sometimes also helps michael@0: // with debugging and temporary ad hoc profiling. The function name chosen michael@0: // doesn't matter greatly, but it's best to make it similar to the path used by michael@0: // the relevant memory reporter(s). michael@0: #define MOZ_DEFINE_MALLOC_SIZE_OF(fn) \ michael@0: static size_t fn(const void* aPtr) \ michael@0: { \ michael@0: MOZ_REPORT(aPtr); \ michael@0: return moz_malloc_size_of(aPtr); \ michael@0: } michael@0: michael@0: // Functions generated by the next two macros should be used by wrapping michael@0: // allocators that report heap blocks as soon as they are allocated and michael@0: // unreport them as soon as they are freed. Such allocators are used in cases michael@0: // where we have third-party code that we cannot modify. The two functions michael@0: // must always be used in tandem. michael@0: #define MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(fn) \ michael@0: static size_t fn(const void* aPtr) \ michael@0: { \ michael@0: MOZ_REPORT_ON_ALLOC(aPtr); \ michael@0: return moz_malloc_size_of(aPtr); \ michael@0: } michael@0: #define MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(fn) \ michael@0: static size_t fn(const void* aPtr) \ michael@0: { \ michael@0: return moz_malloc_size_of(aPtr); \ michael@0: } michael@0: michael@0: namespace mozilla { michael@0: michael@0: // This CRTP class handles several details of wrapping allocators and should michael@0: // be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC michael@0: // and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory michael@0: // reporter for a particular third party library: michael@0: // michael@0: // class MyMemoryReporter : public CountingAllocatorBase michael@0: // { michael@0: // ... michael@0: // NS_IMETHODIMP michael@0: // CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData) michael@0: // { michael@0: // return MOZ_COLLECT_REPORTER( michael@0: // "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES, michael@0: // MemoryAllocated(), michael@0: // "A description of what we are reporting." michael@0: // } michael@0: // }; michael@0: // michael@0: // ...somewhere later in the code... michael@0: // SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc, michael@0: // MyMemoryReporter::CountingFree); michael@0: template michael@0: class CountingAllocatorBase michael@0: { michael@0: public: michael@0: CountingAllocatorBase() michael@0: { michael@0: #ifdef DEBUG michael@0: // There must be only one instance of this class, due to |sAmount| being michael@0: // static. michael@0: static bool hasRun = false; michael@0: MOZ_ASSERT(!hasRun); michael@0: hasRun = true; michael@0: #endif michael@0: } michael@0: michael@0: static size_t michael@0: MemoryAllocated() michael@0: { michael@0: return sAmount; michael@0: } michael@0: michael@0: static void* michael@0: CountingMalloc(size_t size) michael@0: { michael@0: void* p = malloc(size); michael@0: sAmount += MallocSizeOfOnAlloc(p); michael@0: return p; michael@0: } michael@0: michael@0: static void* michael@0: CountingCalloc(size_t nmemb, size_t size) michael@0: { michael@0: void* p = calloc(nmemb, size); michael@0: sAmount += MallocSizeOfOnAlloc(p); michael@0: return p; michael@0: } michael@0: michael@0: static void* michael@0: CountingRealloc(void* p, size_t size) michael@0: { michael@0: size_t oldsize = MallocSizeOfOnFree(p); michael@0: void *pnew = realloc(p, size); michael@0: if (pnew) { michael@0: size_t newsize = MallocSizeOfOnAlloc(pnew); michael@0: sAmount += newsize - oldsize; michael@0: } else if (size == 0) { michael@0: // We asked for a 0-sized (re)allocation of some existing pointer michael@0: // and received NULL in return. 0-sized allocations are permitted michael@0: // to either return NULL or to allocate a unique object per call (!). michael@0: // For a malloc implementation that chooses the second strategy, michael@0: // that allocation may fail (unlikely, but possible). michael@0: // michael@0: // Given a NULL return value and an allocation size of 0, then, we michael@0: // don't know if that means the original pointer was freed or if michael@0: // the allocation of the unique object failed. If the original michael@0: // pointer was freed, then we have nothing to do here. If the michael@0: // allocation of the unique object failed, the original pointer is michael@0: // still valid and we ought to undo the decrement from above. michael@0: // However, we have no way of knowing how the underlying realloc michael@0: // implementation is behaving. Assuming that the original pointer michael@0: // was freed is the safest course of action. We do, however, need michael@0: // to note that we freed memory. michael@0: sAmount -= oldsize; michael@0: } else { michael@0: // realloc failed. The amount allocated hasn't changed. michael@0: } michael@0: return pnew; michael@0: } michael@0: michael@0: static void michael@0: CountingFree(void* p) michael@0: { michael@0: sAmount -= MallocSizeOfOnFree(p); michael@0: free(p); michael@0: } michael@0: michael@0: private: michael@0: // |sAmount| can be (implicitly) accessed by multiple threads, so it michael@0: // must be thread-safe. michael@0: static Atomic sAmount; michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc) michael@0: MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree) michael@0: }; michael@0: michael@0: } michael@0: michael@0: // This macro assumes the presence of appropriate |aHandleReport| and |aData| michael@0: // variables. michael@0: #define MOZ_COLLECT_REPORT(path, kind, units, amount, description) \ michael@0: aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(path), \ michael@0: kind, units, amount, \ michael@0: NS_LITERAL_CSTRING(description), aData) michael@0: michael@0: %}