tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MemoryMetrics.h (29989B)


      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 
     46  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 
     85  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, objectsMallocHeapGlobalData)     \
    190  MACRO(Objects, MallocHeap, objectsMallocHeapMisc)           \
    191  MACRO(Objects, NonHeap, objectsNonHeapElementsNormal)       \
    192  MACRO(Objects, NonHeap, objectsNonHeapElementsShared)       \
    193  MACRO(Objects, NonHeap, objectsNonHeapElementsWasm)         \
    194  MACRO(Objects, NonHeap, objectsNonHeapElementsWasmShared)   \
    195  MACRO(Objects, NonHeap, objectsNonHeapCodeWasm)
    196 
    197  ClassInfo() = default;
    198 
    199  void add(const ClassInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
    200 
    201  void subtract(const ClassInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); }
    202 
    203  size_t sizeOfAllThings() const {
    204    size_t n = 0;
    205    FOR_EACH_SIZE(ADD_SIZE_TO_N);
    206    return n;
    207  }
    208 
    209  bool isNotable() const {
    210    static const size_t NotabilityThreshold = 16 * 1024;
    211    return sizeOfAllThings() >= NotabilityThreshold;
    212  }
    213 
    214  size_t sizeOfLiveGCThings() const {
    215    size_t n = 0;
    216    FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
    217    return n;
    218  }
    219 
    220  void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
    221 
    222  void addToServoSizes(ServoSizes* sizes) const {
    223    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    224  }
    225 
    226  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    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 
    240  void add(const ShapeInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
    241 
    242  void subtract(const ShapeInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); }
    243 
    244  size_t sizeOfAllThings() const {
    245    size_t n = 0;
    246    FOR_EACH_SIZE(ADD_SIZE_TO_N);
    247    return n;
    248  }
    249 
    250  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 
    256  void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
    257 
    258  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 
    296  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, nurseryMallocedBlockCache) \
    314  MACRO(_, MallocHeap, nurseryTrailerBlockSets)   \
    315  MACRO(_, MallocHeap, storeBufferVals)           \
    316  MACRO(_, MallocHeap, storeBufferCells)          \
    317  MACRO(_, MallocHeap, storeBufferSlots)          \
    318  MACRO(_, MallocHeap, storeBufferWasmAnyRefs)    \
    319  MACRO(_, MallocHeap, storeBufferWholeCells)     \
    320  MACRO(_, MallocHeap, storeBufferGenerics)
    321 
    322  GCSizes() = default;
    323 
    324  void addToServoSizes(ServoSizes* sizes) const {
    325    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    326  }
    327 
    328  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    329 
    330 #undef FOR_EACH_SIZE
    331 };
    332 
    333 /**
    334 * This class holds information about the memory taken up by identical copies of
    335 * a particular string.  Multiple JSStrings may have their sizes aggregated
    336 * together into one StringInfo object.  Note that two strings with identical
    337 * chars will not be aggregated together if one is a short string and the other
    338 * is not.
    339 */
    340 struct StringInfo {
    341 #define FOR_EACH_SIZE(MACRO)                   \
    342  MACRO(Strings, GCHeapUsed, gcHeapLatin1)     \
    343  MACRO(Strings, GCHeapUsed, gcHeapTwoByte)    \
    344  MACRO(Strings, MallocHeap, mallocHeapLatin1) \
    345  MACRO(Strings, MallocHeap, mallocHeapTwoByte)
    346 
    347  StringInfo() = default;
    348 
    349  void add(const StringInfo& other) {
    350    FOR_EACH_SIZE(ADD_OTHER_SIZE);
    351    numCopies++;
    352  }
    353 
    354  void subtract(const StringInfo& other) {
    355    FOR_EACH_SIZE(SUB_OTHER_SIZE);
    356    numCopies--;
    357  }
    358 
    359  bool isNotable() const {
    360    static const size_t NotabilityThreshold = 16 * 1024;
    361    size_t n = 0;
    362    FOR_EACH_SIZE(ADD_SIZE_TO_N);
    363    return n >= NotabilityThreshold;
    364  }
    365 
    366  size_t sizeOfLiveGCThings() const {
    367    size_t n = 0;
    368    FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
    369    return n;
    370  }
    371 
    372  void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
    373 
    374  void addToServoSizes(ServoSizes* sizes) const {
    375    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    376  }
    377 
    378  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    379 
    380  uint32_t numCopies = 0;  // How many copies of the string have we seen?
    381 
    382 #undef FOR_EACH_SIZE
    383 };
    384 
    385 /**
    386 * Holds data about a notable string (one which, counting all duplicates, uses
    387 * more than a certain amount of memory) so we can report it individually.
    388 *
    389 * The only difference between this class and StringInfo is that
    390 * NotableStringInfo holds a copy of some or all of the string's chars.
    391 */
    392 struct NotableStringInfo : public StringInfo {
    393  static const size_t MAX_SAVED_CHARS = 1024;
    394 
    395  NotableStringInfo() = default;
    396  NotableStringInfo(NotableStringInfo&&) = default;
    397  NotableStringInfo(const NotableStringInfo&) = delete;
    398 
    399  NotableStringInfo(JSString* str, const StringInfo& info);
    400 
    401  UniqueChars buffer = nullptr;
    402  size_t length = 0;
    403 };
    404 
    405 /**
    406 * This class holds information about the memory taken up by script sources
    407 * from a particular file.
    408 */
    409 struct ScriptSourceInfo {
    410 #define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, misc)
    411 
    412  ScriptSourceInfo() = default;
    413 
    414  void add(const ScriptSourceInfo& other) {
    415    FOR_EACH_SIZE(ADD_OTHER_SIZE);
    416    numScripts++;
    417  }
    418 
    419  void subtract(const ScriptSourceInfo& other) {
    420    FOR_EACH_SIZE(SUB_OTHER_SIZE);
    421    numScripts--;
    422  }
    423 
    424  void addToServoSizes(ServoSizes* sizes) const {
    425    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    426  }
    427 
    428  bool isNotable() const {
    429    static const size_t NotabilityThreshold = 16 * 1024;
    430    size_t n = 0;
    431    FOR_EACH_SIZE(ADD_SIZE_TO_N);
    432    return n >= NotabilityThreshold;
    433  }
    434 
    435  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    436 
    437  uint32_t numScripts = 0;  // How many ScriptSources come from this file? (It
    438                            // can be more than one in XML files that have
    439                            // multiple scripts in CDATA sections.)
    440 #undef FOR_EACH_SIZE
    441 };
    442 
    443 /**
    444 * Holds data about a notable script source file (one whose combined
    445 * script sources use more than a certain amount of memory) so we can report it
    446 * individually.
    447 *
    448 * The only difference between this class and ScriptSourceInfo is that this
    449 * class holds a copy of the filename.
    450 */
    451 struct NotableScriptSourceInfo : public ScriptSourceInfo {
    452  NotableScriptSourceInfo() = default;
    453  NotableScriptSourceInfo(NotableScriptSourceInfo&&) = default;
    454  NotableScriptSourceInfo(const NotableScriptSourceInfo&) = delete;
    455 
    456  NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info);
    457 
    458  UniqueChars filename_ = nullptr;
    459 };
    460 
    461 struct HelperThreadStats {
    462 #define FOR_EACH_SIZE(MACRO)           \
    463  MACRO(_, MallocHeap, stateData)      \
    464  MACRO(_, MallocHeap, ionCompileTask) \
    465  MACRO(_, MallocHeap, wasmCompile)    \
    466  MACRO(_, MallocHeap, contexts)
    467 
    468  HelperThreadStats() = default;
    469 
    470  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    471 
    472  unsigned idleThreadCount = 0;
    473  unsigned activeThreadCount = 0;
    474 
    475 #undef FOR_EACH_SIZE
    476 };
    477 
    478 /**
    479 * Measurements that not associated with any individual runtime.
    480 */
    481 struct GlobalStats {
    482  explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
    483      : mallocSizeOf_(mallocSizeOf) {}
    484 
    485  HelperThreadStats helperThread;
    486 
    487  mozilla::MallocSizeOf mallocSizeOf_;
    488 };
    489 
    490 /**
    491 * These measurements relate directly to the JSRuntime, and not to zones,
    492 * compartments, and realms within it.
    493 */
    494 struct RuntimeSizes {
    495 #define FOR_EACH_SIZE(MACRO)                        \
    496  MACRO(_, MallocHeap, object)                      \
    497  MACRO(_, MallocHeap, atomsTable)                  \
    498  MACRO(_, MallocHeap, atomsMarkBitmaps)            \
    499  MACRO(_, MallocHeap, selfHostStencil)             \
    500  MACRO(_, MallocHeap, contexts)                    \
    501  MACRO(_, MallocHeap, temporary)                   \
    502  MACRO(_, MallocHeap, interpreterStack)            \
    503  MACRO(_, MallocHeap, sharedImmutableStringsCache) \
    504  MACRO(_, MallocHeap, sharedIntlData)              \
    505  MACRO(_, MallocHeap, uncompressedSourceCache)     \
    506  MACRO(_, MallocHeap, scriptData)                  \
    507  MACRO(_, MallocHeap, wasmRuntime)                 \
    508  MACRO(_, Ignore, wasmGuardPages)                  \
    509  MACRO(_, MallocHeap, jitLazyLink)
    510 
    511  RuntimeSizes() { allScriptSources.emplace(); }
    512 
    513  void addToServoSizes(ServoSizes* sizes) const {
    514    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    515    scriptSourceInfo.addToServoSizes(sizes);
    516    gc.addToServoSizes(sizes);
    517  }
    518 
    519  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    520 
    521  // The script source measurements in |scriptSourceInfo| are initially for
    522  // all script sources.  At the end, if the measurement granularity is
    523  // FineGrained, we subtract the measurements of the notable script sources
    524  // and move them into |notableScriptSources|.
    525  ScriptSourceInfo scriptSourceInfo;
    526  GCSizes gc;
    527 
    528  typedef js::HashMap<const char*, ScriptSourceInfo, mozilla::CStringHasher,
    529                      js::SystemAllocPolicy>
    530      ScriptSourcesHashMap;
    531 
    532  // |allScriptSources| is only used transiently.  During the reporting phase
    533  // it is filled with info about every script source in the runtime.  It's
    534  // then used to fill in |notableScriptSources| (which actually gets
    535  // reported), and immediately discarded afterwards.
    536  mozilla::Maybe<ScriptSourcesHashMap> allScriptSources;
    537  js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy>
    538      notableScriptSources;
    539 
    540 #undef FOR_EACH_SIZE
    541 };
    542 
    543 struct UnusedGCThingSizes {
    544 #define FOR_EACH_SIZE(MACRO)               \
    545  MACRO(Other, GCHeapUnused, object)       \
    546  MACRO(Other, GCHeapUnused, script)       \
    547  MACRO(Other, GCHeapUnused, shape)        \
    548  MACRO(Other, GCHeapUnused, baseShape)    \
    549  MACRO(Other, GCHeapUnused, getterSetter) \
    550  MACRO(Other, GCHeapUnused, propMap)      \
    551  MACRO(Other, GCHeapUnused, string)       \
    552  MACRO(Other, GCHeapUnused, symbol)       \
    553  MACRO(Other, GCHeapUnused, bigInt)       \
    554  MACRO(Other, GCHeapUnused, jitcode)      \
    555  MACRO(Other, GCHeapUnused, scope)        \
    556  MACRO(Other, GCHeapUnused, regExpShared) \
    557  MACRO(Other, GCHeapUnused, smallBuffer)
    558 
    559  UnusedGCThingSizes() = default;
    560  UnusedGCThingSizes(UnusedGCThingSizes&& other) = default;
    561 
    562  void addToKind(JS::TraceKind kind, intptr_t n) {
    563    switch (kind) {
    564      case JS::TraceKind::Object:
    565        object += n;
    566        break;
    567      case JS::TraceKind::String:
    568        string += n;
    569        break;
    570      case JS::TraceKind::Symbol:
    571        symbol += n;
    572        break;
    573      case JS::TraceKind::BigInt:
    574        bigInt += n;
    575        break;
    576      case JS::TraceKind::Script:
    577        script += n;
    578        break;
    579      case JS::TraceKind::Shape:
    580        shape += n;
    581        break;
    582      case JS::TraceKind::BaseShape:
    583        baseShape += n;
    584        break;
    585      case JS::TraceKind::GetterSetter:
    586        getterSetter += n;
    587        break;
    588      case JS::TraceKind::PropMap:
    589        propMap += n;
    590        break;
    591      case JS::TraceKind::JitCode:
    592        jitcode += n;
    593        break;
    594      case JS::TraceKind::Scope:
    595        scope += n;
    596        break;
    597      case JS::TraceKind::RegExpShared:
    598        regExpShared += n;
    599        break;
    600      default:
    601        MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
    602    }
    603  }
    604 
    605  void addSizes(const UnusedGCThingSizes& other) {
    606    FOR_EACH_SIZE(ADD_OTHER_SIZE);
    607  }
    608 
    609  size_t totalSize() const {
    610    size_t n = 0;
    611    FOR_EACH_SIZE(ADD_SIZE_TO_N);
    612    return n;
    613  }
    614 
    615  void addToTabSizes(JS::TabSizes* sizes) const {
    616    FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
    617  }
    618 
    619  void addToServoSizes(JS::ServoSizes* sizes) const {
    620    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    621  }
    622 
    623  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    624 
    625 #undef FOR_EACH_SIZE
    626 };
    627 
    628 struct GCBufferStats {
    629 #define FOR_EACH_SIZE(MACRO)          \
    630  MACRO(Other, MallocHeap, usedBytes) \
    631  MACRO(Other, MallocHeap, freeBytes) \
    632  MACRO(Other, MallocHeap, adminBytes)
    633 
    634  GCBufferStats() = default;
    635  GCBufferStats(GCBufferStats&& other) = default;
    636 
    637  void addSizes(const GCBufferStats& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
    638 
    639  size_t totalSize() const {
    640    size_t n = 0;
    641    FOR_EACH_SIZE(ADD_SIZE_TO_N);
    642    return n;
    643  }
    644 
    645  void addToTabSizes(JS::TabSizes* sizes) const {
    646    FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
    647  }
    648 
    649  void addToServoSizes(JS::ServoSizes* sizes) const {
    650    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    651  }
    652 
    653  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    654 
    655 #undef FOR_EACH_SIZE
    656 };
    657 
    658 struct ZoneStats {
    659 #define FOR_EACH_SIZE(MACRO)                               \
    660  MACRO(Other, GCHeapUsed, symbolsGCHeap)                  \
    661  MACRO(Other, GCHeapUsed, bigIntsGCHeap)                  \
    662  MACRO(Other, MallocHeap, bigIntsMallocHeap)              \
    663  MACRO(Other, GCHeapAdmin, gcHeapArenaAdmin)              \
    664  MACRO(Other, GCHeapUsed, jitCodesGCHeap)                 \
    665  MACRO(Other, GCHeapUsed, getterSettersGCHeap)            \
    666  MACRO(Other, GCHeapUsed, compactPropMapsGCHeap)          \
    667  MACRO(Other, GCHeapUsed, normalPropMapsGCHeap)           \
    668  MACRO(Other, GCHeapUsed, dictPropMapsGCHeap)             \
    669  MACRO(Other, MallocHeap, propMapChildren)                \
    670  MACRO(Other, MallocHeap, propMapTables)                  \
    671  MACRO(Other, GCHeapUsed, scopesGCHeap)                   \
    672  MACRO(Other, MallocHeap, scopesMallocHeap)               \
    673  MACRO(Other, GCHeapUsed, regExpSharedsGCHeap)            \
    674  MACRO(Other, MallocHeap, regExpSharedsMallocHeap)        \
    675  MACRO(Other, MallocHeap, zoneObject)                     \
    676  MACRO(Other, MallocHeap, regexpZone)                     \
    677  MACRO(Other, MallocHeap, jitZone)                        \
    678  MACRO(Other, MallocHeap, cacheIRStubs)                   \
    679  MACRO(Other, MallocHeap, objectFuses)                    \
    680  MACRO(Other, MallocHeap, uniqueIdMap)                    \
    681  MACRO(Other, MallocHeap, initialPropMapTable)            \
    682  MACRO(Other, MallocHeap, shapeTables)                    \
    683  MACRO(Other, MallocHeap, compartmentObjects)             \
    684  MACRO(Other, MallocHeap, crossCompartmentWrappersTables) \
    685  MACRO(Other, MallocHeap, compartmentsPrivateData)        \
    686  MACRO(Other, MallocHeap, scriptCountsMap)
    687 
    688  ZoneStats() = default;
    689  ZoneStats(ZoneStats&& other) = default;
    690 
    691  void initStrings();
    692 
    693  void addSizes(const ZoneStats& other) {
    694    MOZ_ASSERT(isTotals);
    695    FOR_EACH_SIZE(ADD_OTHER_SIZE);
    696    gcBuffers.addSizes(other.gcBuffers);
    697    unusedGCThings.addSizes(other.unusedGCThings);
    698    stringInfo.add(other.stringInfo);
    699    shapeInfo.add(other.shapeInfo);
    700  }
    701 
    702  size_t sizeOfLiveGCThings() const {
    703    MOZ_ASSERT(isTotals);
    704    size_t n = 0;
    705    FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
    706    n += stringInfo.sizeOfLiveGCThings();
    707    n += shapeInfo.sizeOfLiveGCThings();
    708    return n;
    709  }
    710 
    711  void addToTabSizes(JS::TabSizes* sizes) const {
    712    MOZ_ASSERT(isTotals);
    713    FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
    714    gcBuffers.addToTabSizes(sizes);
    715    unusedGCThings.addToTabSizes(sizes);
    716    stringInfo.addToTabSizes(sizes);
    717    shapeInfo.addToTabSizes(sizes);
    718  }
    719 
    720  void addToServoSizes(JS::ServoSizes* sizes) const {
    721    MOZ_ASSERT(isTotals);
    722    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    723    gcBuffers.addToServoSizes(sizes);
    724    unusedGCThings.addToServoSizes(sizes);
    725    stringInfo.addToServoSizes(sizes);
    726    shapeInfo.addToServoSizes(sizes);
    727    code.addToServoSizes(sizes);
    728  }
    729 
    730  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    731 
    732  GCBufferStats gcBuffers;
    733 
    734  // These string measurements are initially for all strings.  At the end,
    735  // if the measurement granularity is FineGrained, we subtract the
    736  // measurements of the notable script sources and move them into
    737  // |notableStrings|.
    738  UnusedGCThingSizes unusedGCThings;
    739  StringInfo stringInfo;
    740  ShapeInfo shapeInfo;
    741  CodeSizes code;
    742  void* extra = nullptr;  // This field can be used by embedders.
    743 
    744  typedef js::HashMap<JSString*, StringInfo,
    745                      js::InefficientNonFlatteningStringHashPolicy,
    746                      js::SystemAllocPolicy>
    747      StringsHashMap;
    748 
    749  // |allStrings| is only used transiently.  During the zone traversal it is
    750  // filled with info about every string in the zone.  It's then used to fill
    751  // in |notableStrings| (which actually gets reported), and immediately
    752  // discarded afterwards.
    753  mozilla::Maybe<StringsHashMap> allStrings;
    754  js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
    755  bool isTotals = true;
    756 
    757 #undef FOR_EACH_SIZE
    758 };
    759 
    760 struct RealmStats {
    761  // We assume that |objectsPrivate| is on the malloc heap, but it's not
    762  // actually guaranteed. But for Servo, at least, it's a moot point because
    763  // it doesn't provide an ObjectPrivateVisitor so the value will always be
    764  // zero.
    765 #define FOR_EACH_SIZE(MACRO)                      \
    766  MACRO(Private, MallocHeap, objectsPrivate)      \
    767  MACRO(Other, GCHeapUsed, scriptsGCHeap)         \
    768  MACRO(Other, MallocHeap, scriptsMallocHeapData) \
    769  MACRO(Other, MallocHeap, baselineData)          \
    770  MACRO(Other, MallocHeap, allocSites)            \
    771  MACRO(Other, MallocHeap, ionData)               \
    772  MACRO(Other, MallocHeap, jitScripts)            \
    773  MACRO(Other, MallocHeap, realmObject)           \
    774  MACRO(Other, MallocHeap, realmTables)           \
    775  MACRO(Other, MallocHeap, innerViewsTable)       \
    776  MACRO(Other, MallocHeap, objectMetadataTable)   \
    777  MACRO(Other, MallocHeap, savedStacksSet)        \
    778  MACRO(Other, MallocHeap, nonSyntacticLexicalScopesTable)
    779 
    780  RealmStats() = default;
    781  RealmStats(RealmStats&& other) = default;
    782 
    783  RealmStats(const RealmStats&) = delete;  // disallow copying
    784 
    785  void initClasses();
    786 
    787  void addSizes(const RealmStats& other) {
    788    MOZ_ASSERT(isTotals);
    789    FOR_EACH_SIZE(ADD_OTHER_SIZE);
    790    classInfo.add(other.classInfo);
    791  }
    792 
    793  size_t sizeOfLiveGCThings() const {
    794    MOZ_ASSERT(isTotals);
    795    size_t n = 0;
    796    FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
    797    n += classInfo.sizeOfLiveGCThings();
    798    return n;
    799  }
    800 
    801  void addToTabSizes(TabSizes* sizes) const {
    802    MOZ_ASSERT(isTotals);
    803    FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
    804    classInfo.addToTabSizes(sizes);
    805  }
    806 
    807  void addToServoSizes(ServoSizes* sizes) const {
    808    MOZ_ASSERT(isTotals);
    809    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    810    classInfo.addToServoSizes(sizes);
    811  }
    812 
    813  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    814 
    815  // The class measurements in |classInfo| are initially for all classes.  At
    816  // the end, if the measurement granularity is FineGrained, we subtract the
    817  // measurements of the notable classes and move them into |notableClasses|.
    818  ClassInfo classInfo;
    819  void* extra = nullptr;  // This field can be used by embedders.
    820 
    821  typedef js::HashMap<const char*, ClassInfo, mozilla::CStringHasher,
    822                      js::SystemAllocPolicy>
    823      ClassesHashMap;
    824 
    825  // These are similar to |allStrings| and |notableStrings| in ZoneStats.
    826  mozilla::Maybe<ClassesHashMap> allClasses;
    827  js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
    828  bool isTotals = true;
    829 
    830 #undef FOR_EACH_SIZE
    831 };
    832 
    833 typedef js::Vector<RealmStats, 0, js::SystemAllocPolicy> RealmStatsVector;
    834 typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
    835 
    836 struct RuntimeStats {
    837  // |gcHeapChunkTotal| is ignored because it's the sum of all the other
    838  // values. |gcHeapGCThings| is ignored because it's the sum of some of the
    839  // values from the zones and compartments. Both of those values are not
    840  // reported directly, but are just present for sanity-checking other
    841  // values.
    842 #define FOR_EACH_SIZE(MACRO)                          \
    843  MACRO(_, Ignore, gcHeapChunkTotal)                  \
    844  MACRO(_, GCHeapDecommitted, gcHeapDecommittedPages) \
    845  MACRO(_, GCHeapUnused, gcHeapUnusedChunks)          \
    846  MACRO(_, GCHeapUnused, gcHeapUnusedArenas)          \
    847  MACRO(_, GCHeapAdmin, gcHeapChunkAdmin)             \
    848  MACRO(_, Ignore, gcHeapGCThings)
    849 
    850  explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
    851      : mallocSizeOf_(mallocSizeOf) {}
    852 
    853  // Here's a useful breakdown of the GC heap.
    854  //
    855  // - rtStats.gcHeapChunkTotal
    856  //   - decommitted bytes
    857  //     - rtStats.gcHeapDecommittedPages
    858  //         (decommitted pages in non-empty chunks)
    859  //   - unused bytes
    860  //     - rtStats.gcHeapUnusedChunks (empty chunks)
    861  //     - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
    862  //     - rtStats.zTotals.unusedGCThings.totalSize()
    863  //         (empty GC thing slots within non-empty arenas)
    864  //   - used bytes
    865  //     - rtStats.gcHeapChunkAdmin
    866  //     - rtStats.zTotals.gcHeapArenaAdmin
    867  //     - rtStats.gcHeapGCThings (in-use GC things)
    868  //       == (rtStats.zTotals.sizeOfLiveGCThings() +
    869  //           rtStats.cTotals.sizeOfLiveGCThings())
    870  //
    871  // It's possible that some pages in empty chunks may be decommitted, but
    872  // we don't count those under rtStats.gcHeapDecommittedPages because (a)
    873  // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
    874  // multiple of the chunk size, which is good.
    875 
    876  void addToServoSizes(ServoSizes* sizes) const {
    877    FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
    878    runtime.addToServoSizes(sizes);
    879  }
    880 
    881  FOR_EACH_SIZE(DECL_SIZE_ZERO);
    882 
    883  RuntimeSizes runtime;
    884 
    885  RealmStats realmTotals;  // The sum of this runtime's realms' measurements.
    886  ZoneStats zTotals;       // The sum of this runtime's zones' measurements.
    887 
    888  RealmStatsVector realmStatsVector;
    889  ZoneStatsVector zoneStatsVector;
    890 
    891  ZoneStats* currZoneStats = nullptr;
    892 
    893  mozilla::MallocSizeOf mallocSizeOf_;
    894 
    895  virtual void initExtraRealmStats(JS::Realm* realm, RealmStats* rstats,
    896                                   const JS::AutoRequireNoGC& nogc) = 0;
    897  virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats,
    898                                  const JS::AutoRequireNoGC& nogc) = 0;
    899 
    900 #undef FOR_EACH_SIZE
    901 };
    902 
    903 class ObjectPrivateVisitor {
    904 public:
    905  // Within CollectRuntimeStats, this method is called for each JS object
    906  // that has an nsISupports pointer.
    907  virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0;
    908 
    909  // A callback that gets a JSObject's nsISupports pointer, if it has one.
    910  // Note: this function does *not* addref |iface|.
    911  typedef bool (*GetISupportsFun)(JSObject* obj, nsISupports** iface);
    912  GetISupportsFun getISupports_;
    913 
    914  explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
    915      : getISupports_(getISupports) {}
    916 };
    917 
    918 extern JS_PUBLIC_API bool CollectGlobalStats(GlobalStats* gStats);
    919 
    920 extern JS_PUBLIC_API bool CollectRuntimeStats(JSContext* cx,
    921                                              RuntimeStats* rtStats,
    922                                              ObjectPrivateVisitor* opv,
    923                                              bool anonymize);
    924 
    925 extern JS_PUBLIC_API size_t SystemCompartmentCount(JSContext* cx);
    926 extern JS_PUBLIC_API size_t UserCompartmentCount(JSContext* cx);
    927 
    928 extern JS_PUBLIC_API size_t SystemRealmCount(JSContext* cx);
    929 extern JS_PUBLIC_API size_t UserRealmCount(JSContext* cx);
    930 
    931 extern JS_PUBLIC_API size_t PeakSizeOfTemporary(const JSContext* cx);
    932 
    933 extern JS_PUBLIC_API bool AddSizeOfTab(JSContext* cx, JS::Zone* zone,
    934                                       mozilla::MallocSizeOf mallocSizeOf,
    935                                       ObjectPrivateVisitor* opv,
    936                                       TabSizes* sizes,
    937                                       const JS::AutoRequireNoGC& nogc);
    938 
    939 }  // namespace JS
    940 
    941 #undef DECL_SIZE_ZERO
    942 #undef ADD_OTHER_SIZE
    943 #undef SUB_OTHER_SIZE
    944 #undef ADD_SIZE_TO_N
    945 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
    946 #undef ADD_TO_TAB_SIZES
    947 
    948 #endif /* js_MemoryMetrics_h */