1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
6 
7 #ifndef js_MemoryMetrics_h
8 #define js_MemoryMetrics_h
9 
10 // These declarations are highly likely to change in the future. Depend on them
11 // at your own risk.
12 
13 #include "mozilla/Maybe.h"
14 #include "mozilla/MemoryReporting.h"
15 
16 #include <string.h>
17 #include <type_traits>
18 
19 #include "jstypes.h"
20 
21 #include "js/AllocPolicy.h"
22 #include "js/HashTable.h"
23 #include "js/TraceKind.h"
24 #include "js/TypeDecls.h"
25 #include "js/Utility.h"
26 #include "js/Vector.h"
27 
28 class nsISupports;  // Needed for ObjectPrivateVisitor.
29 
30 namespace js {
31 class SystemAllocPolicy;
32 }
33 
34 namespace mozilla {
35 struct CStringHasher;
36 }
37 
38 namespace JS {
39 class JS_PUBLIC_API AutoRequireNoGC;
40 
41 struct TabSizes {
42   TabSizes() = default;
43 
44   enum Kind { Objects, Strings, Private, Other };
45 
addTabSizes46   void add(Kind kind, size_t n) {
47     switch (kind) {
48       case Objects:
49         objects_ += n;
50         break;
51       case Strings:
52         strings_ += n;
53         break;
54       case Private:
55         private_ += n;
56         break;
57       case Other:
58         other_ += n;
59         break;
60       default:
61         MOZ_CRASH("bad TabSizes kind");
62     }
63   }
64 
65   size_t objects_ = 0;
66   size_t strings_ = 0;
67   size_t private_ = 0;
68   size_t other_ = 0;
69 };
70 
71 /** These are the measurements used by Servo. */
72 struct ServoSizes {
73   ServoSizes() = default;
74 
75   enum Kind {
76     GCHeapUsed,
77     GCHeapUnused,
78     GCHeapAdmin,
79     GCHeapDecommitted,
80     MallocHeap,
81     NonHeap,
82     Ignore
83   };
84 
addServoSizes85   void add(Kind kind, size_t n) {
86     switch (kind) {
87       case GCHeapUsed:
88         gcHeapUsed += n;
89         break;
90       case GCHeapUnused:
91         gcHeapUnused += n;
92         break;
93       case GCHeapAdmin:
94         gcHeapAdmin += n;
95         break;
96       case GCHeapDecommitted:
97         gcHeapDecommitted += n;
98         break;
99       case MallocHeap:
100         mallocHeap += n;
101         break;
102       case NonHeap:
103         nonHeap += n;
104         break;
105       case Ignore: /* do nothing */
106         break;
107       default:
108         MOZ_CRASH("bad ServoSizes kind");
109     }
110   }
111 
112   size_t gcHeapUsed = 0;
113   size_t gcHeapUnused = 0;
114   size_t gcHeapAdmin = 0;
115   size_t gcHeapDecommitted = 0;
116   size_t mallocHeap = 0;
117   size_t nonHeap = 0;
118 };
119 
120 }  // namespace JS
121 
122 namespace js {
123 
124 /**
125  * In memory reporting, we have concept of "sundries", line items which are too
126  * small to be worth reporting individually.  Under some circumstances, a memory
127  * reporter gets tossed into the sundries bucket if it's smaller than
128  * MemoryReportingSundriesThreshold() bytes.
129  *
130  * We need to define this value here, rather than in the code which actually
131  * generates the memory reports, because NotableStringInfo uses this value.
132  */
133 JS_PUBLIC_API size_t MemoryReportingSundriesThreshold();
134 
135 /**
136  * This hash policy avoids flattening ropes (which perturbs the site being
137  * measured and requires a JSContext) at the expense of doing a FULL ROPE COPY
138  * on every hash and match! Beware.
139  */
140 struct InefficientNonFlatteningStringHashPolicy {
141   typedef JSString* Lookup;
142   static HashNumber hash(const Lookup& l);
143   static bool match(const JSString* const& k, const Lookup& l);
144 };
145 
146 // This file features many classes with numerous size_t fields, and each such
147 // class has one or more methods that need to operate on all of these fields.
148 // Writing these individually is error-prone -- it's easy to add a new field
149 // without updating all the required methods.  So we define a single macro list
150 // in each class to name the fields (and notable characteristics of them), and
151 // then use the following macros to transform those lists into the required
152 // methods.
153 //
154 // - The |tabKind| value is used when measuring TabSizes.
155 //
156 // - The |servoKind| value is used when measuring ServoSizes and also for
157 //   the various sizeOfLiveGCThings() methods.
158 //
159 // In some classes, one or more of the macro arguments aren't used.  We use '_'
160 // for those.
161 //
162 #define DECL_SIZE_ZERO(tabKind, servoKind, mSize) size_t mSize = 0;
163 #define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize;
164 #define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \
165   MOZ_ASSERT(mSize >= other.mSize);               \
166   mSize -= other.mSize;
167 #define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize;
168 #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize)     \
169   /* Avoid self-comparison warnings by comparing enums indirectly. */ \
170   n += (std::is_same_v<int[ServoSizes::servoKind],                    \
171                        int[ServoSizes::GCHeapUsed]>)                  \
172            ? mSize                                                    \
173            : 0;
174 #define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) \
175   sizes->add(JS::TabSizes::tabKind, mSize);
176 #define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) \
177   sizes->add(JS::ServoSizes::servoKind, mSize);
178 
179 }  // namespace js
180 
181 namespace JS {
182 
183 struct ClassInfo {
184 #define FOR_EACH_SIZE(MACRO)                                  \
185   MACRO(Objects, GCHeapUsed, objectsGCHeap)                   \
186   MACRO(Objects, MallocHeap, objectsMallocHeapSlots)          \
187   MACRO(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
188   MACRO(Objects, MallocHeap, objectsMallocHeapElementsAsmJS)  \
189   MACRO(Objects, MallocHeap, objectsMallocHeapMisc)           \
190   MACRO(Objects, NonHeap, objectsNonHeapElementsNormal)       \
191   MACRO(Objects, NonHeap, objectsNonHeapElementsShared)       \
192   MACRO(Objects, NonHeap, objectsNonHeapElementsWasm)         \
193   MACRO(Objects, NonHeap, objectsNonHeapCodeWasm)
194 
195   ClassInfo() = default;
196 
addClassInfo197   void add(const ClassInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
198 
subtractClassInfo199   void subtract(const ClassInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); }
200 
sizeOfAllThingsClassInfo201   size_t sizeOfAllThings() const {
202     size_t n = 0;
203     FOR_EACH_SIZE(ADD_SIZE_TO_N);
204     return n;
205   }
206 
isNotableClassInfo207   bool isNotable() const {
208     static const size_t NotabilityThreshold = 16 * 1024;
209     return sizeOfAllThings() >= NotabilityThreshold;
210   }
211 
sizeOfLiveGCThingsClassInfo212   size_t sizeOfLiveGCThings() const {
213     size_t n = 0;
214     FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
215     return n;
216   }
217 
addToTabSizesClassInfo218   void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
219 
addToServoSizesClassInfo220   void addToServoSizes(ServoSizes* sizes) const {
221     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
222   }
223 
224   FOR_EACH_SIZE(DECL_SIZE_ZERO);
225 
226   size_t wasmGuardPages = 0;
227 
228 #undef FOR_EACH_SIZE
229 };
230 
231 struct ShapeInfo {
232 #define FOR_EACH_SIZE(MACRO)                   \
233   MACRO(Other, GCHeapUsed, shapesGCHeapShared) \
234   MACRO(Other, GCHeapUsed, shapesGCHeapDict)   \
235   MACRO(Other, GCHeapUsed, shapesGCHeapBase)   \
236   MACRO(Other, MallocHeap, shapesMallocHeapCache)
237 
238   ShapeInfo() = default;
239 
addShapeInfo240   void add(const ShapeInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
241 
subtractShapeInfo242   void subtract(const ShapeInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); }
243 
sizeOfAllThingsShapeInfo244   size_t sizeOfAllThings() const {
245     size_t n = 0;
246     FOR_EACH_SIZE(ADD_SIZE_TO_N);
247     return n;
248   }
249 
sizeOfLiveGCThingsShapeInfo250   size_t sizeOfLiveGCThings() const {
251     size_t n = 0;
252     FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
253     return n;
254   }
255 
addToTabSizesShapeInfo256   void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
257 
addToServoSizesShapeInfo258   void addToServoSizes(ServoSizes* sizes) const {
259     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
260   }
261 
262   FOR_EACH_SIZE(DECL_SIZE_ZERO);
263 
264 #undef FOR_EACH_SIZE
265 };
266 
267 /**
268  * Holds data about a notable class (one whose combined object and shape
269  * instances use more than a certain amount of memory) so we can report it
270  * individually.
271  *
272  * The only difference between this class and ClassInfo is that this class
273  * holds a copy of the filename.
274  */
275 struct NotableClassInfo : public ClassInfo {
276   NotableClassInfo() = default;
277   NotableClassInfo(NotableClassInfo&&) = default;
278   NotableClassInfo(const NotableClassInfo& info) = delete;
279 
280   NotableClassInfo(const char* className, const ClassInfo& info);
281 
282   UniqueChars className_ = nullptr;
283 };
284 
285 /** Data for tracking JIT-code memory usage. */
286 struct CodeSizes {
287 #define FOR_EACH_SIZE(MACRO)  \
288   MACRO(_, NonHeap, ion)      \
289   MACRO(_, NonHeap, baseline) \
290   MACRO(_, NonHeap, regexp)   \
291   MACRO(_, NonHeap, other)    \
292   MACRO(_, NonHeap, unused)
293 
294   CodeSizes() = default;
295 
addToServoSizesCodeSizes296   void addToServoSizes(ServoSizes* sizes) const {
297     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
298   }
299 
300   FOR_EACH_SIZE(DECL_SIZE_ZERO);
301 
302 #undef FOR_EACH_SIZE
303 };
304 
305 /** Data for tracking GC memory usage. */
306 struct GCSizes {
307   // |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
308   // because we don't consider the nursery to be part of the GC heap.
309 #define FOR_EACH_SIZE(MACRO)                   \
310   MACRO(_, MallocHeap, marker)                 \
311   MACRO(_, NonHeap, nurseryCommitted)          \
312   MACRO(_, MallocHeap, nurseryMallocedBuffers) \
313   MACRO(_, MallocHeap, storeBufferVals)        \
314   MACRO(_, MallocHeap, storeBufferCells)       \
315   MACRO(_, MallocHeap, storeBufferSlots)       \
316   MACRO(_, MallocHeap, storeBufferWholeCells)  \
317   MACRO(_, MallocHeap, storeBufferGenerics)
318 
319   GCSizes() = default;
320 
addToServoSizesGCSizes321   void addToServoSizes(ServoSizes* sizes) const {
322     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
323   }
324 
325   FOR_EACH_SIZE(DECL_SIZE_ZERO);
326 
327 #undef FOR_EACH_SIZE
328 };
329 
330 /**
331  * This class holds information about the memory taken up by identical copies of
332  * a particular string.  Multiple JSStrings may have their sizes aggregated
333  * together into one StringInfo object.  Note that two strings with identical
334  * chars will not be aggregated together if one is a short string and the other
335  * is not.
336  */
337 struct StringInfo {
338 #define FOR_EACH_SIZE(MACRO)                   \
339   MACRO(Strings, GCHeapUsed, gcHeapLatin1)     \
340   MACRO(Strings, GCHeapUsed, gcHeapTwoByte)    \
341   MACRO(Strings, MallocHeap, mallocHeapLatin1) \
342   MACRO(Strings, MallocHeap, mallocHeapTwoByte)
343 
344   StringInfo() = default;
345 
addStringInfo346   void add(const StringInfo& other) {
347     FOR_EACH_SIZE(ADD_OTHER_SIZE);
348     numCopies++;
349   }
350 
subtractStringInfo351   void subtract(const StringInfo& other) {
352     FOR_EACH_SIZE(SUB_OTHER_SIZE);
353     numCopies--;
354   }
355 
isNotableStringInfo356   bool isNotable() const {
357     static const size_t NotabilityThreshold = 16 * 1024;
358     size_t n = 0;
359     FOR_EACH_SIZE(ADD_SIZE_TO_N);
360     return n >= NotabilityThreshold;
361   }
362 
sizeOfLiveGCThingsStringInfo363   size_t sizeOfLiveGCThings() const {
364     size_t n = 0;
365     FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
366     return n;
367   }
368 
addToTabSizesStringInfo369   void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
370 
addToServoSizesStringInfo371   void addToServoSizes(ServoSizes* sizes) const {
372     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
373   }
374 
375   FOR_EACH_SIZE(DECL_SIZE_ZERO);
376 
377   uint32_t numCopies = 0;  // How many copies of the string have we seen?
378 
379 #undef FOR_EACH_SIZE
380 };
381 
382 /**
383  * Holds data about a notable string (one which, counting all duplicates, uses
384  * more than a certain amount of memory) so we can report it individually.
385  *
386  * The only difference between this class and StringInfo is that
387  * NotableStringInfo holds a copy of some or all of the string's chars.
388  */
389 struct NotableStringInfo : public StringInfo {
390   static const size_t MAX_SAVED_CHARS = 1024;
391 
392   NotableStringInfo() = default;
393   NotableStringInfo(NotableStringInfo&&) = default;
394   NotableStringInfo(const NotableStringInfo&) = delete;
395 
396   NotableStringInfo(JSString* str, const StringInfo& info);
397 
398   UniqueChars buffer = nullptr;
399   size_t length = 0;
400 };
401 
402 /**
403  * This class holds information about the memory taken up by script sources
404  * from a particular file.
405  */
406 struct ScriptSourceInfo {
407 #define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, misc)
408 
409   ScriptSourceInfo() = default;
410 
addScriptSourceInfo411   void add(const ScriptSourceInfo& other) {
412     FOR_EACH_SIZE(ADD_OTHER_SIZE);
413     numScripts++;
414   }
415 
subtractScriptSourceInfo416   void subtract(const ScriptSourceInfo& other) {
417     FOR_EACH_SIZE(SUB_OTHER_SIZE);
418     numScripts--;
419   }
420 
addToServoSizesScriptSourceInfo421   void addToServoSizes(ServoSizes* sizes) const {
422     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
423   }
424 
isNotableScriptSourceInfo425   bool isNotable() const {
426     static const size_t NotabilityThreshold = 16 * 1024;
427     size_t n = 0;
428     FOR_EACH_SIZE(ADD_SIZE_TO_N);
429     return n >= NotabilityThreshold;
430   }
431 
432   FOR_EACH_SIZE(DECL_SIZE_ZERO);
433 
434   uint32_t numScripts = 0;  // How many ScriptSources come from this file? (It
435                             // can be more than one in XML files that have
436                             // multiple scripts in CDATA sections.)
437 #undef FOR_EACH_SIZE
438 };
439 
440 /**
441  * Holds data about a notable script source file (one whose combined
442  * script sources use more than a certain amount of memory) so we can report it
443  * individually.
444  *
445  * The only difference between this class and ScriptSourceInfo is that this
446  * class holds a copy of the filename.
447  */
448 struct NotableScriptSourceInfo : public ScriptSourceInfo {
449   NotableScriptSourceInfo() = default;
450   NotableScriptSourceInfo(NotableScriptSourceInfo&&) = default;
451   NotableScriptSourceInfo(const NotableScriptSourceInfo&) = delete;
452 
453   NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info);
454 
455   UniqueChars filename_ = nullptr;
456 };
457 
458 struct HelperThreadStats {
459 #define FOR_EACH_SIZE(MACRO)           \
460   MACRO(_, MallocHeap, stateData)      \
461   MACRO(_, MallocHeap, parseTask)      \
462   MACRO(_, MallocHeap, ionCompileTask) \
463   MACRO(_, MallocHeap, wasmCompile)    \
464   MACRO(_, MallocHeap, contexts)
465 
466   HelperThreadStats() = default;
467 
468   FOR_EACH_SIZE(DECL_SIZE_ZERO);
469 
470   unsigned idleThreadCount = 0;
471   unsigned activeThreadCount = 0;
472 
473 #undef FOR_EACH_SIZE
474 };
475 
476 /**
477  * Measurements that not associated with any individual runtime.
478  */
479 struct GlobalStats {
480 #define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, tracelogger)
481 
GlobalStatsGlobalStats482   explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
483       : mallocSizeOf_(mallocSizeOf) {}
484 
485   FOR_EACH_SIZE(DECL_SIZE_ZERO);
486 
487   HelperThreadStats helperThread;
488 
489   mozilla::MallocSizeOf mallocSizeOf_;
490 
491 #undef FOR_EACH_SIZE
492 };
493 
494 /**
495  * These measurements relate directly to the JSRuntime, and not to zones,
496  * compartments, and realms within it.
497  */
498 struct RuntimeSizes {
499 #define FOR_EACH_SIZE(MACRO)                        \
500   MACRO(_, MallocHeap, object)                      \
501   MACRO(_, MallocHeap, atomsTable)                  \
502   MACRO(_, MallocHeap, atomsMarkBitmaps)            \
503   MACRO(_, MallocHeap, contexts)                    \
504   MACRO(_, MallocHeap, temporary)                   \
505   MACRO(_, MallocHeap, interpreterStack)            \
506   MACRO(_, MallocHeap, sharedImmutableStringsCache) \
507   MACRO(_, MallocHeap, sharedIntlData)              \
508   MACRO(_, MallocHeap, uncompressedSourceCache)     \
509   MACRO(_, MallocHeap, scriptData)                  \
510   MACRO(_, MallocHeap, tracelogger)                 \
511   MACRO(_, MallocHeap, wasmRuntime)                 \
512   MACRO(_, MallocHeap, jitLazyLink)
513 
RuntimeSizesRuntimeSizes514   RuntimeSizes() { allScriptSources.emplace(); }
515 
addToServoSizesRuntimeSizes516   void addToServoSizes(ServoSizes* sizes) const {
517     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
518     scriptSourceInfo.addToServoSizes(sizes);
519     gc.addToServoSizes(sizes);
520   }
521 
522   FOR_EACH_SIZE(DECL_SIZE_ZERO);
523 
524   // The script source measurements in |scriptSourceInfo| are initially for
525   // all script sources.  At the end, if the measurement granularity is
526   // FineGrained, we subtract the measurements of the notable script sources
527   // and move them into |notableScriptSources|.
528   ScriptSourceInfo scriptSourceInfo;
529   GCSizes gc;
530 
531   typedef js::HashMap<const char*, ScriptSourceInfo, mozilla::CStringHasher,
532                       js::SystemAllocPolicy>
533       ScriptSourcesHashMap;
534 
535   // |allScriptSources| is only used transiently.  During the reporting phase
536   // it is filled with info about every script source in the runtime.  It's
537   // then used to fill in |notableScriptSources| (which actually gets
538   // reported), and immediately discarded afterwards.
539   mozilla::Maybe<ScriptSourcesHashMap> allScriptSources;
540   js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy>
541       notableScriptSources;
542 
543 #undef FOR_EACH_SIZE
544 };
545 
546 struct UnusedGCThingSizes {
547 #define FOR_EACH_SIZE(MACRO)               \
548   MACRO(Other, GCHeapUnused, object)       \
549   MACRO(Other, GCHeapUnused, script)       \
550   MACRO(Other, GCHeapUnused, shape)        \
551   MACRO(Other, GCHeapUnused, baseShape)    \
552   MACRO(Other, GCHeapUnused, getterSetter) \
553   MACRO(Other, GCHeapUnused, propMap)      \
554   MACRO(Other, GCHeapUnused, string)       \
555   MACRO(Other, GCHeapUnused, symbol)       \
556   MACRO(Other, GCHeapUnused, bigInt)       \
557   MACRO(Other, GCHeapUnused, jitcode)      \
558   MACRO(Other, GCHeapUnused, scope)        \
559   MACRO(Other, GCHeapUnused, regExpShared)
560 
561   UnusedGCThingSizes() = default;
562   UnusedGCThingSizes(UnusedGCThingSizes&& other) = default;
563 
addToKindUnusedGCThingSizes564   void addToKind(JS::TraceKind kind, intptr_t n) {
565     switch (kind) {
566       case JS::TraceKind::Object:
567         object += n;
568         break;
569       case JS::TraceKind::String:
570         string += n;
571         break;
572       case JS::TraceKind::Symbol:
573         symbol += n;
574         break;
575       case JS::TraceKind::BigInt:
576         bigInt += n;
577         break;
578       case JS::TraceKind::Script:
579         script += n;
580         break;
581       case JS::TraceKind::Shape:
582         shape += n;
583         break;
584       case JS::TraceKind::BaseShape:
585         baseShape += n;
586         break;
587       case JS::TraceKind::GetterSetter:
588         getterSetter += n;
589         break;
590       case JS::TraceKind::PropMap:
591         propMap += n;
592         break;
593       case JS::TraceKind::JitCode:
594         jitcode += n;
595         break;
596       case JS::TraceKind::Scope:
597         scope += n;
598         break;
599       case JS::TraceKind::RegExpShared:
600         regExpShared += n;
601         break;
602       default:
603         MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
604     }
605   }
606 
addSizesUnusedGCThingSizes607   void addSizes(const UnusedGCThingSizes& other) {
608     FOR_EACH_SIZE(ADD_OTHER_SIZE);
609   }
610 
totalSizeUnusedGCThingSizes611   size_t totalSize() const {
612     size_t n = 0;
613     FOR_EACH_SIZE(ADD_SIZE_TO_N);
614     return n;
615   }
616 
addToTabSizesUnusedGCThingSizes617   void addToTabSizes(JS::TabSizes* sizes) const {
618     FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
619   }
620 
addToServoSizesUnusedGCThingSizes621   void addToServoSizes(JS::ServoSizes* sizes) const {
622     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
623   }
624 
625   FOR_EACH_SIZE(DECL_SIZE_ZERO);
626 
627 #undef FOR_EACH_SIZE
628 };
629 
630 struct ZoneStats {
631 #define FOR_EACH_SIZE(MACRO)                               \
632   MACRO(Other, GCHeapUsed, symbolsGCHeap)                  \
633   MACRO(Other, GCHeapUsed, bigIntsGCHeap)                  \
634   MACRO(Other, MallocHeap, bigIntsMallocHeap)              \
635   MACRO(Other, GCHeapAdmin, gcHeapArenaAdmin)              \
636   MACRO(Other, GCHeapUsed, jitCodesGCHeap)                 \
637   MACRO(Other, GCHeapUsed, getterSettersGCHeap)            \
638   MACRO(Other, GCHeapUsed, compactPropMapsGCHeap)          \
639   MACRO(Other, GCHeapUsed, normalPropMapsGCHeap)           \
640   MACRO(Other, GCHeapUsed, dictPropMapsGCHeap)             \
641   MACRO(Other, MallocHeap, propMapChildren)                \
642   MACRO(Other, MallocHeap, propMapTables)                  \
643   MACRO(Other, GCHeapUsed, scopesGCHeap)                   \
644   MACRO(Other, MallocHeap, scopesMallocHeap)               \
645   MACRO(Other, GCHeapUsed, regExpSharedsGCHeap)            \
646   MACRO(Other, MallocHeap, regExpSharedsMallocHeap)        \
647   MACRO(Other, MallocHeap, regexpZone)                     \
648   MACRO(Other, MallocHeap, jitZone)                        \
649   MACRO(Other, MallocHeap, baselineStubsOptimized)         \
650   MACRO(Other, MallocHeap, uniqueIdMap)                    \
651   MACRO(Other, MallocHeap, initialPropMapTable)            \
652   MACRO(Other, MallocHeap, shapeTables)                    \
653   MACRO(Other, MallocHeap, compartmentObjects)             \
654   MACRO(Other, MallocHeap, crossCompartmentWrappersTables) \
655   MACRO(Other, MallocHeap, compartmentsPrivateData)        \
656   MACRO(Other, MallocHeap, scriptCountsMap)
657 
658   ZoneStats() = default;
659   ZoneStats(ZoneStats&& other) = default;
660 
661   void initStrings();
662 
addSizesZoneStats663   void addSizes(const ZoneStats& other) {
664     MOZ_ASSERT(isTotals);
665     FOR_EACH_SIZE(ADD_OTHER_SIZE);
666     unusedGCThings.addSizes(other.unusedGCThings);
667     stringInfo.add(other.stringInfo);
668     shapeInfo.add(other.shapeInfo);
669   }
670 
sizeOfLiveGCThingsZoneStats671   size_t sizeOfLiveGCThings() const {
672     MOZ_ASSERT(isTotals);
673     size_t n = 0;
674     FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
675     n += stringInfo.sizeOfLiveGCThings();
676     n += shapeInfo.sizeOfLiveGCThings();
677     return n;
678   }
679 
addToTabSizesZoneStats680   void addToTabSizes(JS::TabSizes* sizes) const {
681     MOZ_ASSERT(isTotals);
682     FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
683     unusedGCThings.addToTabSizes(sizes);
684     stringInfo.addToTabSizes(sizes);
685     shapeInfo.addToTabSizes(sizes);
686   }
687 
addToServoSizesZoneStats688   void addToServoSizes(JS::ServoSizes* sizes) const {
689     MOZ_ASSERT(isTotals);
690     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
691     unusedGCThings.addToServoSizes(sizes);
692     stringInfo.addToServoSizes(sizes);
693     shapeInfo.addToServoSizes(sizes);
694     code.addToServoSizes(sizes);
695   }
696 
697   FOR_EACH_SIZE(DECL_SIZE_ZERO);
698 
699   // These string measurements are initially for all strings.  At the end,
700   // if the measurement granularity is FineGrained, we subtract the
701   // measurements of the notable script sources and move them into
702   // |notableStrings|.
703   UnusedGCThingSizes unusedGCThings;
704   StringInfo stringInfo;
705   ShapeInfo shapeInfo;
706   CodeSizes code;
707   void* extra = nullptr;  // This field can be used by embedders.
708 
709   typedef js::HashMap<JSString*, StringInfo,
710                       js::InefficientNonFlatteningStringHashPolicy,
711                       js::SystemAllocPolicy>
712       StringsHashMap;
713 
714   // |allStrings| is only used transiently.  During the zone traversal it is
715   // filled with info about every string in the zone.  It's then used to fill
716   // in |notableStrings| (which actually gets reported), and immediately
717   // discarded afterwards.
718   mozilla::Maybe<StringsHashMap> allStrings;
719   js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
720   bool isTotals = true;
721 
722 #undef FOR_EACH_SIZE
723 };
724 
725 struct RealmStats {
726   // We assume that |objectsPrivate| is on the malloc heap, but it's not
727   // actually guaranteed. But for Servo, at least, it's a moot point because
728   // it doesn't provide an ObjectPrivateVisitor so the value will always be
729   // zero.
730 #define FOR_EACH_SIZE(MACRO)                               \
731   MACRO(Private, MallocHeap, objectsPrivate)               \
732   MACRO(Other, GCHeapUsed, scriptsGCHeap)                  \
733   MACRO(Other, MallocHeap, scriptsMallocHeapData)          \
734   MACRO(Other, MallocHeap, baselineData)                   \
735   MACRO(Other, MallocHeap, baselineStubsFallback)          \
736   MACRO(Other, MallocHeap, ionData)                        \
737   MACRO(Other, MallocHeap, jitScripts)                     \
738   MACRO(Other, MallocHeap, realmObject)                    \
739   MACRO(Other, MallocHeap, realmTables)                    \
740   MACRO(Other, MallocHeap, innerViewsTable)                \
741   MACRO(Other, MallocHeap, objectMetadataTable)            \
742   MACRO(Other, MallocHeap, savedStacksSet)                 \
743   MACRO(Other, MallocHeap, varNamesSet)                    \
744   MACRO(Other, MallocHeap, nonSyntacticLexicalScopesTable) \
745   MACRO(Other, MallocHeap, jitRealm)
746 
747   RealmStats() = default;
748   RealmStats(RealmStats&& other) = default;
749 
750   RealmStats(const RealmStats&) = delete;  // disallow copying
751 
752   void initClasses();
753 
addSizesRealmStats754   void addSizes(const RealmStats& other) {
755     MOZ_ASSERT(isTotals);
756     FOR_EACH_SIZE(ADD_OTHER_SIZE);
757     classInfo.add(other.classInfo);
758   }
759 
sizeOfLiveGCThingsRealmStats760   size_t sizeOfLiveGCThings() const {
761     MOZ_ASSERT(isTotals);
762     size_t n = 0;
763     FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
764     n += classInfo.sizeOfLiveGCThings();
765     return n;
766   }
767 
addToTabSizesRealmStats768   void addToTabSizes(TabSizes* sizes) const {
769     MOZ_ASSERT(isTotals);
770     FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
771     classInfo.addToTabSizes(sizes);
772   }
773 
addToServoSizesRealmStats774   void addToServoSizes(ServoSizes* sizes) const {
775     MOZ_ASSERT(isTotals);
776     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
777     classInfo.addToServoSizes(sizes);
778   }
779 
780   FOR_EACH_SIZE(DECL_SIZE_ZERO);
781 
782   // The class measurements in |classInfo| are initially for all classes.  At
783   // the end, if the measurement granularity is FineGrained, we subtract the
784   // measurements of the notable classes and move them into |notableClasses|.
785   ClassInfo classInfo;
786   void* extra = nullptr;  // This field can be used by embedders.
787 
788   typedef js::HashMap<const char*, ClassInfo, mozilla::CStringHasher,
789                       js::SystemAllocPolicy>
790       ClassesHashMap;
791 
792   // These are similar to |allStrings| and |notableStrings| in ZoneStats.
793   mozilla::Maybe<ClassesHashMap> allClasses;
794   js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
795   bool isTotals = true;
796 
797 #undef FOR_EACH_SIZE
798 };
799 
800 typedef js::Vector<RealmStats, 0, js::SystemAllocPolicy> RealmStatsVector;
801 typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
802 
803 struct RuntimeStats {
804   // |gcHeapChunkTotal| is ignored because it's the sum of all the other
805   // values. |gcHeapGCThings| is ignored because it's the sum of some of the
806   // values from the zones and compartments. Both of those values are not
807   // reported directly, but are just present for sanity-checking other
808   // values.
809 #define FOR_EACH_SIZE(MACRO)                          \
810   MACRO(_, Ignore, gcHeapChunkTotal)                  \
811   MACRO(_, GCHeapDecommitted, gcHeapDecommittedPages) \
812   MACRO(_, GCHeapUnused, gcHeapUnusedChunks)          \
813   MACRO(_, GCHeapUnused, gcHeapUnusedArenas)          \
814   MACRO(_, GCHeapAdmin, gcHeapChunkAdmin)             \
815   MACRO(_, Ignore, gcHeapGCThings)
816 
RuntimeStatsRuntimeStats817   explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
818       : mallocSizeOf_(mallocSizeOf) {}
819 
820   // Here's a useful breakdown of the GC heap.
821   //
822   // - rtStats.gcHeapChunkTotal
823   //   - decommitted bytes
824   //     - rtStats.gcHeapDecommittedPages
825   //         (decommitted pages in non-empty chunks)
826   //   - unused bytes
827   //     - rtStats.gcHeapUnusedChunks (empty chunks)
828   //     - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
829   //     - rtStats.zTotals.unusedGCThings.totalSize()
830   //         (empty GC thing slots within non-empty arenas)
831   //   - used bytes
832   //     - rtStats.gcHeapChunkAdmin
833   //     - rtStats.zTotals.gcHeapArenaAdmin
834   //     - rtStats.gcHeapGCThings (in-use GC things)
835   //       == (rtStats.zTotals.sizeOfLiveGCThings() +
836   //           rtStats.cTotals.sizeOfLiveGCThings())
837   //
838   // It's possible that some pages in empty chunks may be decommitted, but
839   // we don't count those under rtStats.gcHeapDecommittedPages because (a)
840   // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
841   // multiple of the chunk size, which is good.
842 
addToServoSizesRuntimeStats843   void addToServoSizes(ServoSizes* sizes) const {
844     FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
845     runtime.addToServoSizes(sizes);
846   }
847 
848   FOR_EACH_SIZE(DECL_SIZE_ZERO);
849 
850   RuntimeSizes runtime;
851 
852   RealmStats realmTotals;  // The sum of this runtime's realms' measurements.
853   ZoneStats zTotals;       // The sum of this runtime's zones' measurements.
854 
855   RealmStatsVector realmStatsVector;
856   ZoneStatsVector zoneStatsVector;
857 
858   ZoneStats* currZoneStats = nullptr;
859 
860   mozilla::MallocSizeOf mallocSizeOf_;
861 
862   virtual void initExtraRealmStats(JS::Realm* realm, RealmStats* rstats,
863                                    const JS::AutoRequireNoGC& nogc) = 0;
864   virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats,
865                                   const JS::AutoRequireNoGC& nogc) = 0;
866 
867 #undef FOR_EACH_SIZE
868 };
869 
870 class ObjectPrivateVisitor {
871  public:
872   // Within CollectRuntimeStats, this method is called for each JS object
873   // that has an nsISupports pointer.
874   virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0;
875 
876   // A callback that gets a JSObject's nsISupports pointer, if it has one.
877   // Note: this function does *not* addref |iface|.
878   typedef bool (*GetISupportsFun)(JSObject* obj, nsISupports** iface);
879   GetISupportsFun getISupports_;
880 
ObjectPrivateVisitor(GetISupportsFun getISupports)881   explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
882       : getISupports_(getISupports) {}
883 };
884 
885 extern JS_PUBLIC_API bool CollectGlobalStats(GlobalStats* gStats);
886 
887 extern JS_PUBLIC_API bool CollectRuntimeStats(JSContext* cx,
888                                               RuntimeStats* rtStats,
889                                               ObjectPrivateVisitor* opv,
890                                               bool anonymize);
891 
892 extern JS_PUBLIC_API size_t SystemCompartmentCount(JSContext* cx);
893 extern JS_PUBLIC_API size_t UserCompartmentCount(JSContext* cx);
894 
895 extern JS_PUBLIC_API size_t SystemRealmCount(JSContext* cx);
896 extern JS_PUBLIC_API size_t UserRealmCount(JSContext* cx);
897 
898 extern JS_PUBLIC_API size_t PeakSizeOfTemporary(const JSContext* cx);
899 
900 extern JS_PUBLIC_API bool AddSizeOfTab(JSContext* cx, JS::HandleObject obj,
901                                        mozilla::MallocSizeOf mallocSizeOf,
902                                        ObjectPrivateVisitor* opv,
903                                        TabSizes* sizes);
904 
905 extern JS_PUBLIC_API bool AddServoSizeOf(JSContext* cx,
906                                          mozilla::MallocSizeOf mallocSizeOf,
907                                          ObjectPrivateVisitor* opv,
908                                          ServoSizes* sizes);
909 
910 }  // namespace JS
911 
912 #undef DECL_SIZE_ZERO
913 #undef ADD_OTHER_SIZE
914 #undef SUB_OTHER_SIZE
915 #undef ADD_SIZE_TO_N
916 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
917 #undef ADD_TO_TAB_SIZES
918 
919 #endif /* js_MemoryMetrics_h */
920