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