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 */