1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef js_MemoryMetrics_h
8 #define js_MemoryMetrics_h
10 // These declarations are highly likely to change in the future. Depend on them
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/PodOperations.h"
21 #include "js/HashTable.h"
22 #include "js/Utility.h"
23 #include "js/Vector.h"
25 class nsISupports
; // Needed for ObjectPrivateVisitor.
38 TabSizes() { mozilla::PodZero(this); }
40 void add(Kind kind
, size_t n
) {
42 case Objects
: objects
+= n
; break;
43 case Strings
: strings
+= n
; break;
44 case Private
: private_
+= n
; break;
45 case Other
: other
+= n
; break;
46 default: MOZ_CRASH("bad TabSizes kind");
60 // In memory reporting, we have concept of "sundries", line items which are too
61 // small to be worth reporting individually. Under some circumstances, a memory
62 // reporter gets tossed into the sundries bucket if it's smaller than
63 // MemoryReportingSundriesThreshold() bytes.
65 // We need to define this value here, rather than in the code which actually
66 // generates the memory reports, because NotableStringInfo uses this value.
67 JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
69 // This hash policy avoids flattening ropes (which perturbs the site being
70 // measured and requires a JSContext) at the expense of doing a FULL ROPE COPY
71 // on every hash and match! Beware.
72 struct InefficientNonFlatteningStringHashPolicy
74 typedef JSString
* Lookup
;
75 static HashNumber
hash(const Lookup
& l
);
76 static bool match(const JSString
* const& k
, const Lookup
& l
);
79 struct CStringHashPolicy
81 typedef const char* Lookup
;
82 static HashNumber
hash(const Lookup
& l
);
83 static bool match(const char* const& k
, const Lookup
& l
);
86 // This file features many classes with numerous size_t fields, and each such
87 // class has one or more methods that need to operate on all of these fields.
88 // Writing these individually is error-prone -- it's easy to add a new field
89 // without updating all the required methods. So we define a single macro list
90 // in each class to name the fields (and notable characteristics of them), and
91 // then use the following macros to transform those lists into the required
94 // In some classes, one or more of the macro arguments aren't used. We use '_'
97 #define DECL_SIZE(kind, gc, mSize) size_t mSize;
98 #define ZERO_SIZE(kind, gc, mSize) mSize(0),
99 #define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize),
100 #define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize;
101 #define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \
102 mSize -= other.mSize;
103 #define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize;
104 #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0;
105 #define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize);
107 // Used to annotate which size_t fields measure live GC things and which don't.
109 NotLiveGCThing
= false,
119 #define FOR_EACH_SIZE(macro) \
120 macro(Objects, IsLiveGCThing, objectsGCHeap) \
121 macro(Objects, NotLiveGCThing, objectsMallocHeapSlots) \
122 macro(Objects, NotLiveGCThing, objectsMallocHeapElementsNonAsmJS) \
123 macro(Objects, NotLiveGCThing, objectsMallocHeapElementsAsmJS) \
124 macro(Objects, NotLiveGCThing, objectsNonHeapElementsAsmJS) \
125 macro(Objects, NotLiveGCThing, objectsNonHeapElementsMapped) \
126 macro(Objects, NotLiveGCThing, objectsNonHeapCodeAsmJS) \
127 macro(Objects, NotLiveGCThing, objectsMallocHeapMisc) \
129 macro(Other, IsLiveGCThing, shapesGCHeapTree) \
130 macro(Other, IsLiveGCThing, shapesGCHeapDict) \
131 macro(Other, IsLiveGCThing, shapesGCHeapBase) \
132 macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \
133 macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \
134 macro(Other, NotLiveGCThing, shapesMallocHeapTreeKids) \
137 : FOR_EACH_SIZE(ZERO_SIZE
)
141 void add(const ClassInfo
& other
) {
142 FOR_EACH_SIZE(ADD_OTHER_SIZE
)
145 void subtract(const ClassInfo
& other
) {
146 FOR_EACH_SIZE(SUB_OTHER_SIZE
)
149 bool isNotable() const {
150 static const size_t NotabilityThreshold
= 16 * 1024;
152 FOR_EACH_SIZE(ADD_SIZE_TO_N
)
153 return n
>= NotabilityThreshold
;
156 size_t sizeOfLiveGCThings() const {
158 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING
)
162 void addToTabSizes(TabSizes
* sizes
) const {
163 FOR_EACH_SIZE(ADD_TO_TAB_SIZES
)
166 FOR_EACH_SIZE(DECL_SIZE
)
167 int dummy
; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
172 // Holds data about a notable class (one whose combined object and shape
173 // instances use more than a certain amount of memory) so we can report it
176 // The only difference between this class and ClassInfo is that this class
177 // holds a copy of the filename.
178 struct NotableClassInfo
: public ClassInfo
181 NotableClassInfo(const char* className
, const ClassInfo
& info
);
182 NotableClassInfo(NotableClassInfo
&& info
);
183 NotableClassInfo
& operator=(NotableClassInfo
&& info
);
185 ~NotableClassInfo() {
192 NotableClassInfo(const NotableClassInfo
& info
) = delete;
195 // Data for tracking JIT-code memory usage.
198 #define FOR_EACH_SIZE(macro) \
200 macro(_, _, baseline) \
201 macro(_, _, regexp) \
206 : FOR_EACH_SIZE(ZERO_SIZE
)
210 FOR_EACH_SIZE(DECL_SIZE
)
211 int dummy
; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
216 // Data for tracking GC memory usage.
219 #define FOR_EACH_SIZE(macro) \
220 macro(_, _, marker) \
221 macro(_, _, nurseryCommitted) \
222 macro(_, _, nurseryDecommitted) \
223 macro(_, _, nurseryHugeSlots) \
224 macro(_, _, storeBufferVals) \
225 macro(_, _, storeBufferCells) \
226 macro(_, _, storeBufferSlots) \
227 macro(_, _, storeBufferWholeCells) \
228 macro(_, _, storeBufferRelocVals) \
229 macro(_, _, storeBufferRelocCells) \
230 macro(_, _, storeBufferGenerics)
233 : FOR_EACH_SIZE(ZERO_SIZE
)
237 FOR_EACH_SIZE(DECL_SIZE
)
238 int dummy
; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
243 // This class holds information about the memory taken up by identical copies of
244 // a particular string. Multiple JSStrings may have their sizes aggregated
245 // together into one StringInfo object. Note that two strings with identical
246 // chars will not be aggregated together if one is a short string and the other
250 #define FOR_EACH_SIZE(macro) \
251 macro(Strings, IsLiveGCThing, gcHeapLatin1) \
252 macro(Strings, IsLiveGCThing, gcHeapTwoByte) \
253 macro(Strings, NotLiveGCThing, mallocHeapLatin1) \
254 macro(Strings, NotLiveGCThing, mallocHeapTwoByte)
257 : FOR_EACH_SIZE(ZERO_SIZE
)
261 void add(const StringInfo
& other
) {
262 FOR_EACH_SIZE(ADD_OTHER_SIZE
);
266 void subtract(const StringInfo
& other
) {
267 FOR_EACH_SIZE(SUB_OTHER_SIZE
);
271 bool isNotable() const {
272 static const size_t NotabilityThreshold
= 16 * 1024;
274 FOR_EACH_SIZE(ADD_SIZE_TO_N
)
275 return n
>= NotabilityThreshold
;
278 size_t sizeOfLiveGCThings() const {
280 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING
)
284 void addToTabSizes(TabSizes
* sizes
) const {
285 FOR_EACH_SIZE(ADD_TO_TAB_SIZES
)
288 FOR_EACH_SIZE(DECL_SIZE
)
289 uint32_t numCopies
; // How many copies of the string have we seen?
294 // Holds data about a notable string (one which, counting all duplicates, uses
295 // more than a certain amount of memory) so we can report it individually.
297 // The only difference between this class and StringInfo is that
298 // NotableStringInfo holds a copy of some or all of the string's chars.
299 struct NotableStringInfo
: public StringInfo
301 static const size_t MAX_SAVED_CHARS
= 1024;
304 NotableStringInfo(JSString
* str
, const StringInfo
& info
);
305 NotableStringInfo(NotableStringInfo
&& info
);
306 NotableStringInfo
& operator=(NotableStringInfo
&& info
);
308 ~NotableStringInfo() {
316 NotableStringInfo(const NotableStringInfo
& info
) = delete;
319 // This class holds information about the memory taken up by script sources
320 // from a particular file.
321 struct ScriptSourceInfo
323 #define FOR_EACH_SIZE(macro) \
324 macro(_, _, compressed) \
325 macro(_, _, uncompressed) \
329 : FOR_EACH_SIZE(ZERO_SIZE
)
333 void add(const ScriptSourceInfo
& other
) {
334 FOR_EACH_SIZE(ADD_OTHER_SIZE
)
338 void subtract(const ScriptSourceInfo
& other
) {
339 FOR_EACH_SIZE(SUB_OTHER_SIZE
)
343 bool isNotable() const {
344 static const size_t NotabilityThreshold
= 16 * 1024;
346 FOR_EACH_SIZE(ADD_SIZE_TO_N
)
347 return n
>= NotabilityThreshold
;
350 FOR_EACH_SIZE(DECL_SIZE
)
351 uint32_t numScripts
; // How many ScriptSources come from this file? (It
352 // can be more than one in XML files that have
353 // multiple scripts in CDATA sections.)
357 // Holds data about a notable script source file (one whose combined
358 // script sources use more than a certain amount of memory) so we can report it
361 // The only difference between this class and ScriptSourceInfo is that this
362 // class holds a copy of the filename.
363 struct NotableScriptSourceInfo
: public ScriptSourceInfo
365 NotableScriptSourceInfo();
366 NotableScriptSourceInfo(const char* filename
, const ScriptSourceInfo
& info
);
367 NotableScriptSourceInfo(NotableScriptSourceInfo
&& info
);
368 NotableScriptSourceInfo
& operator=(NotableScriptSourceInfo
&& info
);
370 ~NotableScriptSourceInfo() {
377 NotableScriptSourceInfo(const NotableScriptSourceInfo
& info
) = delete;
380 // These measurements relate directly to the JSRuntime, and not to zones and
381 // compartments within it.
384 #define FOR_EACH_SIZE(macro) \
385 macro(_, _, object) \
386 macro(_, _, atomsTable) \
387 macro(_, _, contexts) \
389 macro(_, _, temporary) \
390 macro(_, _, interpreterStack) \
391 macro(_, _, mathCache) \
392 macro(_, _, uncompressedSourceCache) \
393 macro(_, _, compressedSourceSet) \
394 macro(_, _, scriptData) \
397 : FOR_EACH_SIZE(ZERO_SIZE
)
401 notableScriptSources()
403 allScriptSources
= js_new
<ScriptSourcesHashMap
>();
404 if (!allScriptSources
|| !allScriptSources
->init())
409 // |allScriptSources| is usually deleted and set to nullptr before this
410 // destructor runs. But there are failure cases due to OOMs that may
411 // prevent that, so it doesn't hurt to try again here.
412 js_delete(allScriptSources
);
415 // The script source measurements in |scriptSourceInfo| are initially for
416 // all script sources. At the end, if the measurement granularity is
417 // FineGrained, we subtract the measurements of the notable script sources
418 // and move them into |notableScriptSources|.
419 FOR_EACH_SIZE(DECL_SIZE
)
420 ScriptSourceInfo scriptSourceInfo
;
424 typedef js::HashMap
<const char*, ScriptSourceInfo
,
425 js::CStringHashPolicy
,
426 js::SystemAllocPolicy
> ScriptSourcesHashMap
;
428 // |allScriptSources| is only used transiently. During the reporting phase
429 // it is filled with info about every script source in the runtime. It's
430 // then used to fill in |notableScriptSources| (which actually gets
431 // reported), and immediately discarded afterwards.
432 ScriptSourcesHashMap
* allScriptSources
;
433 js::Vector
<NotableScriptSourceInfo
, 0, js::SystemAllocPolicy
> notableScriptSources
;
440 #define FOR_EACH_SIZE(macro) \
441 macro(Other, IsLiveGCThing, symbolsGCHeap) \
442 macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
443 macro(Other, NotLiveGCThing, unusedGCThings) \
444 macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \
445 macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \
446 macro(Other, IsLiveGCThing, jitCodesGCHeap) \
447 macro(Other, IsLiveGCThing, typeObjectsGCHeap) \
448 macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \
449 macro(Other, NotLiveGCThing, typePool) \
450 macro(Other, NotLiveGCThing, baselineStubsOptimized) \
453 : FOR_EACH_SIZE(ZERO_SIZE
)
461 ZoneStats(ZoneStats
&& other
)
462 : FOR_EACH_SIZE(COPY_OTHER_SIZE
)
463 stringInfo(mozilla::Move(other
.stringInfo
)),
465 allStrings(other
.allStrings
),
466 notableStrings(mozilla::Move(other
.notableStrings
)),
467 isTotals(other
.isTotals
)
469 other
.allStrings
= nullptr;
470 MOZ_ASSERT(!other
.isTotals
);
474 // |allStrings| is usually deleted and set to nullptr before this
475 // destructor runs. But there are failure cases due to OOMs that may
476 // prevent that, so it doesn't hurt to try again here.
477 js_delete(allStrings
);
480 bool initStrings(JSRuntime
* rt
);
482 void addSizes(const ZoneStats
& other
) {
483 MOZ_ASSERT(isTotals
);
484 FOR_EACH_SIZE(ADD_OTHER_SIZE
)
485 stringInfo
.add(other
.stringInfo
);
488 size_t sizeOfLiveGCThings() const {
489 MOZ_ASSERT(isTotals
);
491 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING
)
492 n
+= stringInfo
.sizeOfLiveGCThings();
496 void addToTabSizes(JS::TabSizes
* sizes
) const {
497 MOZ_ASSERT(isTotals
);
498 FOR_EACH_SIZE(ADD_TO_TAB_SIZES
)
499 stringInfo
.addToTabSizes(sizes
);
502 // These string measurements are initially for all strings. At the end,
503 // if the measurement granularity is FineGrained, we subtract the
504 // measurements of the notable script sources and move them into
506 FOR_EACH_SIZE(DECL_SIZE
)
507 StringInfo stringInfo
;
508 void* extra
; // This field can be used by embedders.
510 typedef js::HashMap
<JSString
*, StringInfo
,
511 js::InefficientNonFlatteningStringHashPolicy
,
512 js::SystemAllocPolicy
> StringsHashMap
;
514 // |allStrings| is only used transiently. During the zone traversal it is
515 // filled with info about every string in the zone. It's then used to fill
516 // in |notableStrings| (which actually gets reported), and immediately
517 // discarded afterwards.
518 StringsHashMap
* allStrings
;
519 js::Vector
<NotableStringInfo
, 0, js::SystemAllocPolicy
> notableStrings
;
525 struct CompartmentStats
527 #define FOR_EACH_SIZE(macro) \
528 macro(Private, NotLiveGCThing, objectsPrivate) \
529 macro(Other, IsLiveGCThing, scriptsGCHeap) \
530 macro(Other, NotLiveGCThing, scriptsMallocHeapData) \
531 macro(Other, NotLiveGCThing, baselineData) \
532 macro(Other, NotLiveGCThing, baselineStubsFallback) \
533 macro(Other, NotLiveGCThing, ionData) \
534 macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \
535 macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \
536 macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \
537 macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \
538 macro(Other, NotLiveGCThing, compartmentObject) \
539 macro(Other, NotLiveGCThing, compartmentTables) \
540 macro(Other, NotLiveGCThing, innerViewsTable) \
541 macro(Other, NotLiveGCThing, lazyArrayBuffersTable) \
542 macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
543 macro(Other, NotLiveGCThing, regexpCompartment) \
544 macro(Other, NotLiveGCThing, savedStacksSet)
547 : FOR_EACH_SIZE(ZERO_SIZE
)
555 CompartmentStats(CompartmentStats
&& other
)
556 : FOR_EACH_SIZE(COPY_OTHER_SIZE
)
557 classInfo(mozilla::Move(other
.classInfo
)),
559 allClasses(other
.allClasses
),
560 notableClasses(mozilla::Move(other
.notableClasses
)),
561 isTotals(other
.isTotals
)
563 other
.allClasses
= nullptr;
564 MOZ_ASSERT(!other
.isTotals
);
567 ~CompartmentStats() {
568 // |allClasses| is usually deleted and set to nullptr before this
569 // destructor runs. But there are failure cases due to OOMs that may
570 // prevent that, so it doesn't hurt to try again here.
571 js_delete(allClasses
);
574 bool initClasses(JSRuntime
* rt
);
576 void addSizes(const CompartmentStats
& other
) {
577 MOZ_ASSERT(isTotals
);
578 FOR_EACH_SIZE(ADD_OTHER_SIZE
)
579 classInfo
.add(other
.classInfo
);
582 size_t sizeOfLiveGCThings() const {
583 MOZ_ASSERT(isTotals
);
585 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING
)
586 n
+= classInfo
.sizeOfLiveGCThings();
590 void addToTabSizes(TabSizes
* sizes
) const {
591 MOZ_ASSERT(isTotals
);
592 FOR_EACH_SIZE(ADD_TO_TAB_SIZES
);
593 classInfo
.addToTabSizes(sizes
);
596 // The class measurements in |classInfo| are initially for all classes. At
597 // the end, if the measurement granularity is FineGrained, we subtract the
598 // measurements of the notable classes and move them into |notableClasses|.
599 FOR_EACH_SIZE(DECL_SIZE
)
601 void* extra
; // This field can be used by embedders.
603 typedef js::HashMap
<const char*, ClassInfo
,
604 js::CStringHashPolicy
,
605 js::SystemAllocPolicy
> ClassesHashMap
;
607 // These are similar to |allStrings| and |notableStrings| in ZoneStats.
608 ClassesHashMap
* allClasses
;
609 js::Vector
<NotableClassInfo
, 0, js::SystemAllocPolicy
> notableClasses
;
615 typedef js::Vector
<CompartmentStats
, 0, js::SystemAllocPolicy
> CompartmentStatsVector
;
616 typedef js::Vector
<ZoneStats
, 0, js::SystemAllocPolicy
> ZoneStatsVector
;
620 #define FOR_EACH_SIZE(macro) \
621 macro(_, _, gcHeapChunkTotal) \
622 macro(_, _, gcHeapDecommittedArenas) \
623 macro(_, _, gcHeapUnusedChunks) \
624 macro(_, _, gcHeapUnusedArenas) \
625 macro(_, _, gcHeapChunkAdmin) \
626 macro(_, _, gcHeapGCThings) \
628 explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
629 : FOR_EACH_SIZE(ZERO_SIZE
)
633 compartmentStatsVector(),
635 currZoneStats(nullptr),
636 mallocSizeOf_(mallocSizeOf
)
639 // Here's a useful breakdown of the GC heap.
641 // - rtStats.gcHeapChunkTotal
642 // - decommitted bytes
643 // - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks)
645 // - rtStats.gcHeapUnusedChunks (empty chunks)
646 // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
647 // - rtStats.zTotals.unusedGCThings (empty GC thing slots within non-empty arenas)
649 // - rtStats.gcHeapChunkAdmin
650 // - rtStats.zTotals.gcHeapArenaAdmin
651 // - rtStats.gcHeapGCThings (in-use GC things)
652 // == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings()
654 // It's possible that some arenas in empty chunks may be decommitted, but
655 // we don't count those under rtStats.gcHeapDecommittedArenas because (a)
656 // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
657 // multiple of the chunk size, which is good.
659 FOR_EACH_SIZE(DECL_SIZE
)
661 RuntimeSizes runtime
;
663 CompartmentStats cTotals
; // The sum of this runtime's compartments' measurements.
664 ZoneStats zTotals
; // The sum of this runtime's zones' measurements.
666 CompartmentStatsVector compartmentStatsVector
;
667 ZoneStatsVector zoneStatsVector
;
669 ZoneStats
* currZoneStats
;
671 mozilla::MallocSizeOf mallocSizeOf_
;
673 virtual void initExtraCompartmentStats(JSCompartment
* c
, CompartmentStats
* cstats
) = 0;
674 virtual void initExtraZoneStats(JS::Zone
* zone
, ZoneStats
* zstats
) = 0;
679 class ObjectPrivateVisitor
682 // Within CollectRuntimeStats, this method is called for each JS object
683 // that has an nsISupports pointer.
684 virtual size_t sizeOfIncludingThis(nsISupports
* aSupports
) = 0;
686 // A callback that gets a JSObject's nsISupports pointer, if it has one.
687 // Note: this function does *not* addref |iface|.
688 typedef bool(*GetISupportsFun
)(JSObject
* obj
, nsISupports
** iface
);
689 GetISupportsFun getISupports_
;
691 explicit ObjectPrivateVisitor(GetISupportsFun getISupports
)
692 : getISupports_(getISupports
)
696 extern JS_PUBLIC_API(bool)
697 CollectRuntimeStats(JSRuntime
* rt
, RuntimeStats
* rtStats
, ObjectPrivateVisitor
* opv
, bool anonymize
);
699 extern JS_PUBLIC_API(size_t)
700 SystemCompartmentCount(JSRuntime
* rt
);
702 extern JS_PUBLIC_API(size_t)
703 UserCompartmentCount(JSRuntime
* rt
);
705 extern JS_PUBLIC_API(size_t)
706 PeakSizeOfTemporary(const JSRuntime
* rt
);
708 extern JS_PUBLIC_API(bool)
709 AddSizeOfTab(JSRuntime
* rt
, JS::HandleObject obj
, mozilla::MallocSizeOf mallocSizeOf
,
710 ObjectPrivateVisitor
* opv
, TabSizes
* sizes
);
716 #undef COPY_OTHER_SIZE
717 #undef ADD_OTHER_SIZE
718 #undef SUB_OTHER_SIZE
720 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
721 #undef ADD_TO_TAB_SIZES
723 #endif /* js_MemoryMetrics_h */