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