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