1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sts=4 et sw=4 tw=99: 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/MemoryReporting.h" 14 #include "mozilla/PodOperations.h" 15 #include "mozilla/TypeTraits.h" 16 17 #include <string.h> 18 19 #include "jsalloc.h" 20 #include "jspubtd.h" 21 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 { 33 enum Kind { 34 Objects, 35 Strings, 36 Private, 37 Other 38 }; 39 TabSizesTabSizes40 TabSizes() { mozilla::PodZero(this); } 41 addTabSizes42 void add(Kind kind, size_t n) { 43 switch (kind) { 44 case Objects: objects += n; break; 45 case Strings: strings += n; break; 46 case Private: private_ += n; break; 47 case Other: other += n; break; 48 default: MOZ_CRASH("bad TabSizes kind"); 49 } 50 } 51 52 size_t objects; 53 size_t strings; 54 size_t private_; 55 size_t other; 56 }; 57 58 /** These are the measurements used by Servo. */ 59 struct ServoSizes 60 { 61 enum Kind { 62 GCHeapUsed, 63 GCHeapUnused, 64 GCHeapAdmin, 65 GCHeapDecommitted, 66 MallocHeap, 67 NonHeap, 68 Ignore 69 }; 70 ServoSizesServoSizes71 ServoSizes() { mozilla::PodZero(this); } 72 addServoSizes73 void add(Kind kind, size_t n) { 74 switch (kind) { 75 case GCHeapUsed: gcHeapUsed += n; break; 76 case GCHeapUnused: gcHeapUnused += n; break; 77 case GCHeapAdmin: gcHeapAdmin += n; break; 78 case GCHeapDecommitted: gcHeapDecommitted += n; break; 79 case MallocHeap: mallocHeap += n; break; 80 case NonHeap: nonHeap += n; break; 81 case Ignore: /* do nothing */ break; 82 default: MOZ_CRASH("bad ServoSizes kind"); 83 } 84 } 85 86 size_t gcHeapUsed; 87 size_t gcHeapUnused; 88 size_t gcHeapAdmin; 89 size_t gcHeapDecommitted; 90 size_t mallocHeap; 91 size_t nonHeap; 92 }; 93 94 } // namespace JS 95 96 namespace js { 97 98 /** 99 * In memory reporting, we have concept of "sundries", line items which are too 100 * small to be worth reporting individually. Under some circumstances, a memory 101 * reporter gets tossed into the sundries bucket if it's smaller than 102 * MemoryReportingSundriesThreshold() bytes. 103 * 104 * We need to define this value here, rather than in the code which actually 105 * generates the memory reports, because NotableStringInfo uses this value. 106 */ 107 JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold(); 108 109 /** 110 * This hash policy avoids flattening ropes (which perturbs the site being 111 * measured and requires a JSContext) at the expense of doing a FULL ROPE COPY 112 * on every hash and match! Beware. 113 */ 114 struct InefficientNonFlatteningStringHashPolicy 115 { 116 typedef JSString* Lookup; 117 static HashNumber hash(const Lookup& l); 118 static bool match(const JSString* const& k, const Lookup& l); 119 }; 120 121 struct CStringHashPolicy 122 { 123 typedef const char* Lookup; 124 static HashNumber hash(const Lookup& l); 125 static bool match(const char* const& k, const Lookup& l); 126 }; 127 128 // This file features many classes with numerous size_t fields, and each such 129 // class has one or more methods that need to operate on all of these fields. 130 // Writing these individually is error-prone -- it's easy to add a new field 131 // without updating all the required methods. So we define a single macro list 132 // in each class to name the fields (and notable characteristics of them), and 133 // then use the following macros to transform those lists into the required 134 // methods. 135 // 136 // - The |tabKind| value is used when measuring TabSizes. 137 // 138 // - The |servoKind| value is used when measuring ServoSizes and also for 139 // the various sizeOfLiveGCThings() methods. 140 // 141 // In some classes, one or more of the macro arguments aren't used. We use '_' 142 // for those. 143 // 144 #define DECL_SIZE(tabKind, servoKind, mSize) size_t mSize; 145 #define ZERO_SIZE(tabKind, servoKind, mSize) mSize(0), 146 #define COPY_OTHER_SIZE(tabKind, servoKind, mSize) mSize(other.mSize), 147 #define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize; 148 #define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \ 149 MOZ_ASSERT(mSize >= other.mSize); \ 150 mSize -= other.mSize; 151 #define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize; 152 #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) \ 153 /* Avoid self-comparison warnings by comparing enums indirectly. */ \ 154 n += (mozilla::IsSame<int[ServoSizes::servoKind], int[ServoSizes::GCHeapUsed]>::value) \ 155 ? mSize \ 156 : 0; 157 #define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) sizes->add(JS::TabSizes::tabKind, mSize); 158 #define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) sizes->add(JS::ServoSizes::servoKind, mSize); 159 160 } // namespace js 161 162 namespace JS { 163 164 struct ClassInfo 165 { 166 #define FOR_EACH_SIZE(macro) \ 167 macro(Objects, GCHeapUsed, objectsGCHeap) \ 168 macro(Objects, MallocHeap, objectsMallocHeapSlots) \ 169 macro(Objects, MallocHeap, objectsMallocHeapElementsNormal) \ 170 macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \ 171 macro(Objects, MallocHeap, objectsMallocHeapMisc) \ 172 macro(Objects, NonHeap, objectsNonHeapElementsNormal) \ 173 macro(Objects, NonHeap, objectsNonHeapElementsShared) \ 174 macro(Objects, NonHeap, objectsNonHeapElementsWasm) \ 175 macro(Objects, NonHeap, objectsNonHeapCodeWasm) 176 ClassInfoClassInfo177 ClassInfo() 178 : FOR_EACH_SIZE(ZERO_SIZE) 179 wasmGuardPages(0) 180 {} 181 addClassInfo182 void add(const ClassInfo& other) { 183 FOR_EACH_SIZE(ADD_OTHER_SIZE) 184 } 185 subtractClassInfo186 void subtract(const ClassInfo& other) { 187 FOR_EACH_SIZE(SUB_OTHER_SIZE) 188 } 189 sizeOfAllThingsClassInfo190 size_t sizeOfAllThings() const { 191 size_t n = 0; 192 FOR_EACH_SIZE(ADD_SIZE_TO_N) 193 return n; 194 } 195 isNotableClassInfo196 bool isNotable() const { 197 static const size_t NotabilityThreshold = 16 * 1024; 198 return sizeOfAllThings() >= NotabilityThreshold; 199 } 200 sizeOfLiveGCThingsClassInfo201 size_t sizeOfLiveGCThings() const { 202 size_t n = 0; 203 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 204 return n; 205 } 206 addToTabSizesClassInfo207 void addToTabSizes(TabSizes* sizes) const { 208 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 209 } 210 addToServoSizesClassInfo211 void addToServoSizes(ServoSizes *sizes) const { 212 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 213 } 214 215 FOR_EACH_SIZE(DECL_SIZE) 216 size_t wasmGuardPages; 217 218 #undef FOR_EACH_SIZE 219 }; 220 221 struct ShapeInfo 222 { 223 #define FOR_EACH_SIZE(macro) \ 224 macro(Other, GCHeapUsed, shapesGCHeapTree) \ 225 macro(Other, GCHeapUsed, shapesGCHeapDict) \ 226 macro(Other, GCHeapUsed, shapesGCHeapBase) \ 227 macro(Other, MallocHeap, shapesMallocHeapTreeTables) \ 228 macro(Other, MallocHeap, shapesMallocHeapDictTables) \ 229 macro(Other, MallocHeap, shapesMallocHeapTreeKids) 230 ShapeInfoShapeInfo231 ShapeInfo() 232 : FOR_EACH_SIZE(ZERO_SIZE) 233 dummy() 234 {} 235 addShapeInfo236 void add(const ShapeInfo& other) { 237 FOR_EACH_SIZE(ADD_OTHER_SIZE) 238 } 239 subtractShapeInfo240 void subtract(const ShapeInfo& other) { 241 FOR_EACH_SIZE(SUB_OTHER_SIZE) 242 } 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 { 257 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 258 } 259 addToServoSizesShapeInfo260 void addToServoSizes(ServoSizes *sizes) const { 261 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 262 } 263 264 FOR_EACH_SIZE(DECL_SIZE) 265 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 266 267 #undef FOR_EACH_SIZE 268 }; 269 270 /** 271 * Holds data about a notable class (one whose combined object and shape 272 * instances use more than a certain amount of memory) so we can report it 273 * individually. 274 * 275 * The only difference between this class and ClassInfo is that this class 276 * holds a copy of the filename. 277 */ 278 struct NotableClassInfo : public ClassInfo 279 { 280 NotableClassInfo(); 281 NotableClassInfo(const char* className, const ClassInfo& info); 282 NotableClassInfo(NotableClassInfo&& info); 283 NotableClassInfo& operator=(NotableClassInfo&& info); 284 ~NotableClassInfoNotableClassInfo285 ~NotableClassInfo() { 286 js_free(className_); 287 } 288 289 char* className_; 290 291 private: 292 NotableClassInfo(const NotableClassInfo& info) = delete; 293 }; 294 295 /** Data for tracking JIT-code memory usage. */ 296 struct CodeSizes 297 { 298 #define FOR_EACH_SIZE(macro) \ 299 macro(_, NonHeap, ion) \ 300 macro(_, NonHeap, baseline) \ 301 macro(_, NonHeap, regexp) \ 302 macro(_, NonHeap, other) \ 303 macro(_, NonHeap, unused) 304 CodeSizesCodeSizes305 CodeSizes() 306 : FOR_EACH_SIZE(ZERO_SIZE) 307 dummy() 308 {} 309 addToServoSizesCodeSizes310 void addToServoSizes(ServoSizes *sizes) const { 311 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 312 } 313 314 FOR_EACH_SIZE(DECL_SIZE) 315 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 316 317 #undef FOR_EACH_SIZE 318 }; 319 320 /** Data for tracking GC memory usage. */ 321 struct GCSizes 322 { 323 // |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted 324 // because we don't consider the nursery to be part of the GC heap. 325 #define FOR_EACH_SIZE(macro) \ 326 macro(_, MallocHeap, marker) \ 327 macro(_, NonHeap, nurseryCommitted) \ 328 macro(_, MallocHeap, nurseryMallocedBuffers) \ 329 macro(_, MallocHeap, storeBufferVals) \ 330 macro(_, MallocHeap, storeBufferCells) \ 331 macro(_, MallocHeap, storeBufferSlots) \ 332 macro(_, MallocHeap, storeBufferWholeCells) \ 333 macro(_, MallocHeap, storeBufferGenerics) 334 GCSizesGCSizes335 GCSizes() 336 : FOR_EACH_SIZE(ZERO_SIZE) 337 dummy() 338 {} 339 addToServoSizesGCSizes340 void addToServoSizes(ServoSizes *sizes) const { 341 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 342 } 343 344 FOR_EACH_SIZE(DECL_SIZE) 345 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 346 347 #undef FOR_EACH_SIZE 348 }; 349 350 /** 351 * This class holds information about the memory taken up by identical copies of 352 * a particular string. Multiple JSStrings may have their sizes aggregated 353 * together into one StringInfo object. Note that two strings with identical 354 * chars will not be aggregated together if one is a short string and the other 355 * is not. 356 */ 357 struct StringInfo 358 { 359 #define FOR_EACH_SIZE(macro) \ 360 macro(Strings, GCHeapUsed, gcHeapLatin1) \ 361 macro(Strings, GCHeapUsed, gcHeapTwoByte) \ 362 macro(Strings, MallocHeap, mallocHeapLatin1) \ 363 macro(Strings, MallocHeap, mallocHeapTwoByte) 364 StringInfoStringInfo365 StringInfo() 366 : FOR_EACH_SIZE(ZERO_SIZE) 367 numCopies(0) 368 {} 369 addStringInfo370 void add(const StringInfo& other) { 371 FOR_EACH_SIZE(ADD_OTHER_SIZE); 372 numCopies++; 373 } 374 subtractStringInfo375 void subtract(const StringInfo& other) { 376 FOR_EACH_SIZE(SUB_OTHER_SIZE); 377 numCopies--; 378 } 379 isNotableStringInfo380 bool isNotable() const { 381 static const size_t NotabilityThreshold = 16 * 1024; 382 size_t n = 0; 383 FOR_EACH_SIZE(ADD_SIZE_TO_N) 384 return n >= NotabilityThreshold; 385 } 386 sizeOfLiveGCThingsStringInfo387 size_t sizeOfLiveGCThings() const { 388 size_t n = 0; 389 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 390 return n; 391 } 392 addToTabSizesStringInfo393 void addToTabSizes(TabSizes* sizes) const { 394 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 395 } 396 addToServoSizesStringInfo397 void addToServoSizes(ServoSizes *sizes) const { 398 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 399 } 400 401 FOR_EACH_SIZE(DECL_SIZE) 402 uint32_t numCopies; // How many copies of the string have we seen? 403 404 #undef FOR_EACH_SIZE 405 }; 406 407 /** 408 * Holds data about a notable string (one which, counting all duplicates, uses 409 * more than a certain amount of memory) so we can report it individually. 410 * 411 * The only difference between this class and StringInfo is that 412 * NotableStringInfo holds a copy of some or all of the string's chars. 413 */ 414 struct NotableStringInfo : public StringInfo 415 { 416 static const size_t MAX_SAVED_CHARS = 1024; 417 418 NotableStringInfo(); 419 NotableStringInfo(JSString* str, const StringInfo& info); 420 NotableStringInfo(NotableStringInfo&& info); 421 NotableStringInfo& operator=(NotableStringInfo&& info); 422 ~NotableStringInfoNotableStringInfo423 ~NotableStringInfo() { 424 js_free(buffer); 425 } 426 427 char* buffer; 428 size_t length; 429 430 private: 431 NotableStringInfo(const NotableStringInfo& info) = delete; 432 }; 433 434 /** 435 * This class holds information about the memory taken up by script sources 436 * from a particular file. 437 */ 438 struct ScriptSourceInfo 439 { 440 #define FOR_EACH_SIZE(macro) \ 441 macro(_, MallocHeap, misc) 442 ScriptSourceInfoScriptSourceInfo443 ScriptSourceInfo() 444 : FOR_EACH_SIZE(ZERO_SIZE) 445 numScripts(0) 446 {} 447 addScriptSourceInfo448 void add(const ScriptSourceInfo& other) { 449 FOR_EACH_SIZE(ADD_OTHER_SIZE) 450 numScripts++; 451 } 452 subtractScriptSourceInfo453 void subtract(const ScriptSourceInfo& other) { 454 FOR_EACH_SIZE(SUB_OTHER_SIZE) 455 numScripts--; 456 } 457 addToServoSizesScriptSourceInfo458 void addToServoSizes(ServoSizes *sizes) const { 459 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 460 } 461 isNotableScriptSourceInfo462 bool isNotable() const { 463 static const size_t NotabilityThreshold = 16 * 1024; 464 size_t n = 0; 465 FOR_EACH_SIZE(ADD_SIZE_TO_N) 466 return n >= NotabilityThreshold; 467 } 468 469 FOR_EACH_SIZE(DECL_SIZE) 470 uint32_t numScripts; // How many ScriptSources come from this file? (It 471 // can be more than one in XML files that have 472 // multiple scripts in CDATA sections.) 473 #undef FOR_EACH_SIZE 474 }; 475 476 /** 477 * Holds data about a notable script source file (one whose combined 478 * script sources use more than a certain amount of memory) so we can report it 479 * individually. 480 * 481 * The only difference between this class and ScriptSourceInfo is that this 482 * class holds a copy of the filename. 483 */ 484 struct NotableScriptSourceInfo : public ScriptSourceInfo 485 { 486 NotableScriptSourceInfo(); 487 NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info); 488 NotableScriptSourceInfo(NotableScriptSourceInfo&& info); 489 NotableScriptSourceInfo& operator=(NotableScriptSourceInfo&& info); 490 ~NotableScriptSourceInfoNotableScriptSourceInfo491 ~NotableScriptSourceInfo() { 492 js_free(filename_); 493 } 494 495 char* filename_; 496 497 private: 498 NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete; 499 }; 500 501 /** 502 * These measurements relate directly to the JSRuntime, and not to zones and 503 * compartments within it. 504 */ 505 struct RuntimeSizes 506 { 507 #define FOR_EACH_SIZE(macro) \ 508 macro(_, MallocHeap, object) \ 509 macro(_, MallocHeap, atomsTable) \ 510 macro(_, MallocHeap, contexts) \ 511 macro(_, MallocHeap, temporary) \ 512 macro(_, MallocHeap, interpreterStack) \ 513 macro(_, MallocHeap, mathCache) \ 514 macro(_, MallocHeap, sharedImmutableStringsCache) \ 515 macro(_, MallocHeap, sharedIntlData) \ 516 macro(_, MallocHeap, uncompressedSourceCache) \ 517 macro(_, MallocHeap, scriptData) 518 RuntimeSizesRuntimeSizes519 RuntimeSizes() 520 : FOR_EACH_SIZE(ZERO_SIZE) 521 scriptSourceInfo(), 522 code(), 523 gc(), 524 notableScriptSources() 525 { 526 allScriptSources = js_new<ScriptSourcesHashMap>(); 527 if (!allScriptSources || !allScriptSources->init()) 528 MOZ_CRASH("oom"); 529 } 530 ~RuntimeSizesRuntimeSizes531 ~RuntimeSizes() { 532 // |allScriptSources| is usually deleted and set to nullptr before this 533 // destructor runs. But there are failure cases due to OOMs that may 534 // prevent that, so it doesn't hurt to try again here. 535 js_delete(allScriptSources); 536 } 537 addToServoSizesRuntimeSizes538 void addToServoSizes(ServoSizes *sizes) const { 539 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 540 scriptSourceInfo.addToServoSizes(sizes); 541 code.addToServoSizes(sizes); 542 gc.addToServoSizes(sizes); 543 } 544 545 // The script source measurements in |scriptSourceInfo| are initially for 546 // all script sources. At the end, if the measurement granularity is 547 // FineGrained, we subtract the measurements of the notable script sources 548 // and move them into |notableScriptSources|. 549 FOR_EACH_SIZE(DECL_SIZE) 550 ScriptSourceInfo scriptSourceInfo; 551 CodeSizes code; 552 GCSizes gc; 553 554 typedef js::HashMap<const char*, ScriptSourceInfo, 555 js::CStringHashPolicy, 556 js::SystemAllocPolicy> ScriptSourcesHashMap; 557 558 // |allScriptSources| is only used transiently. During the reporting phase 559 // it is filled with info about every script source in the runtime. It's 560 // then used to fill in |notableScriptSources| (which actually gets 561 // reported), and immediately discarded afterwards. 562 ScriptSourcesHashMap* allScriptSources; 563 js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources; 564 565 #undef FOR_EACH_SIZE 566 }; 567 568 struct UnusedGCThingSizes 569 { 570 #define FOR_EACH_SIZE(macro) \ 571 macro(Other, GCHeapUnused, object) \ 572 macro(Other, GCHeapUnused, script) \ 573 macro(Other, GCHeapUnused, lazyScript) \ 574 macro(Other, GCHeapUnused, shape) \ 575 macro(Other, GCHeapUnused, baseShape) \ 576 macro(Other, GCHeapUnused, objectGroup) \ 577 macro(Other, GCHeapUnused, string) \ 578 macro(Other, GCHeapUnused, symbol) \ 579 macro(Other, GCHeapUnused, jitcode) \ 580 macro(Other, GCHeapUnused, scope) 581 UnusedGCThingSizesUnusedGCThingSizes582 UnusedGCThingSizes() 583 : FOR_EACH_SIZE(ZERO_SIZE) 584 dummy() 585 {} 586 UnusedGCThingSizesUnusedGCThingSizes587 UnusedGCThingSizes(UnusedGCThingSizes&& other) 588 : FOR_EACH_SIZE(COPY_OTHER_SIZE) 589 dummy() 590 {} 591 addToKindUnusedGCThingSizes592 void addToKind(JS::TraceKind kind, intptr_t n) { 593 switch (kind) { 594 case JS::TraceKind::Object: object += n; break; 595 case JS::TraceKind::String: string += n; break; 596 case JS::TraceKind::Symbol: symbol += n; break; 597 case JS::TraceKind::Script: script += n; break; 598 case JS::TraceKind::Shape: shape += n; break; 599 case JS::TraceKind::BaseShape: baseShape += n; break; 600 case JS::TraceKind::JitCode: jitcode += n; break; 601 case JS::TraceKind::LazyScript: lazyScript += n; break; 602 case JS::TraceKind::ObjectGroup: objectGroup += n; break; 603 case JS::TraceKind::Scope: scope += n; break; 604 default: 605 MOZ_CRASH("Bad trace kind for UnusedGCThingSizes"); 606 } 607 } 608 addSizesUnusedGCThingSizes609 void addSizes(const UnusedGCThingSizes& other) { 610 FOR_EACH_SIZE(ADD_OTHER_SIZE) 611 } 612 totalSizeUnusedGCThingSizes613 size_t totalSize() const { 614 size_t n = 0; 615 FOR_EACH_SIZE(ADD_SIZE_TO_N) 616 return n; 617 } 618 addToTabSizesUnusedGCThingSizes619 void addToTabSizes(JS::TabSizes *sizes) const { 620 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 621 } 622 addToServoSizesUnusedGCThingSizes623 void addToServoSizes(JS::ServoSizes *sizes) const { 624 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 625 } 626 627 FOR_EACH_SIZE(DECL_SIZE) 628 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 629 630 #undef FOR_EACH_SIZE 631 }; 632 633 struct ZoneStats 634 { 635 #define FOR_EACH_SIZE(macro) \ 636 macro(Other, GCHeapUsed, symbolsGCHeap) \ 637 macro(Other, GCHeapAdmin, gcHeapArenaAdmin) \ 638 macro(Other, GCHeapUsed, lazyScriptsGCHeap) \ 639 macro(Other, MallocHeap, lazyScriptsMallocHeap) \ 640 macro(Other, GCHeapUsed, jitCodesGCHeap) \ 641 macro(Other, GCHeapUsed, objectGroupsGCHeap) \ 642 macro(Other, MallocHeap, objectGroupsMallocHeap) \ 643 macro(Other, GCHeapUsed, scopesGCHeap) \ 644 macro(Other, MallocHeap, scopesMallocHeap) \ 645 macro(Other, MallocHeap, typePool) \ 646 macro(Other, MallocHeap, baselineStubsOptimized) \ 647 macro(Other, MallocHeap, uniqueIdMap) \ 648 macro(Other, MallocHeap, shapeTables) 649 ZoneStatsZoneStats650 ZoneStats() 651 : FOR_EACH_SIZE(ZERO_SIZE) 652 unusedGCThings(), 653 stringInfo(), 654 shapeInfo(), 655 extra(), 656 allStrings(nullptr), 657 notableStrings(), 658 isTotals(true) 659 {} 660 ZoneStatsZoneStats661 ZoneStats(ZoneStats&& other) 662 : FOR_EACH_SIZE(COPY_OTHER_SIZE) 663 unusedGCThings(mozilla::Move(other.unusedGCThings)), 664 stringInfo(mozilla::Move(other.stringInfo)), 665 shapeInfo(mozilla::Move(other.shapeInfo)), 666 extra(other.extra), 667 allStrings(other.allStrings), 668 notableStrings(mozilla::Move(other.notableStrings)), 669 isTotals(other.isTotals) 670 { 671 other.allStrings = nullptr; 672 MOZ_ASSERT(!other.isTotals); 673 } 674 ~ZoneStatsZoneStats675 ~ZoneStats() { 676 // |allStrings| is usually deleted and set to nullptr before this 677 // destructor runs. But there are failure cases due to OOMs that may 678 // prevent that, so it doesn't hurt to try again here. 679 js_delete(allStrings); 680 } 681 682 bool initStrings(JSRuntime* rt); 683 addSizesZoneStats684 void addSizes(const ZoneStats& other) { 685 MOZ_ASSERT(isTotals); 686 FOR_EACH_SIZE(ADD_OTHER_SIZE) 687 unusedGCThings.addSizes(other.unusedGCThings); 688 stringInfo.add(other.stringInfo); 689 shapeInfo.add(other.shapeInfo); 690 } 691 sizeOfLiveGCThingsZoneStats692 size_t sizeOfLiveGCThings() const { 693 MOZ_ASSERT(isTotals); 694 size_t n = 0; 695 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 696 n += stringInfo.sizeOfLiveGCThings(); 697 n += shapeInfo.sizeOfLiveGCThings(); 698 return n; 699 } 700 addToTabSizesZoneStats701 void addToTabSizes(JS::TabSizes* sizes) const { 702 MOZ_ASSERT(isTotals); 703 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 704 unusedGCThings.addToTabSizes(sizes); 705 stringInfo.addToTabSizes(sizes); 706 shapeInfo.addToTabSizes(sizes); 707 } 708 addToServoSizesZoneStats709 void addToServoSizes(JS::ServoSizes *sizes) const { 710 MOZ_ASSERT(isTotals); 711 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 712 unusedGCThings.addToServoSizes(sizes); 713 stringInfo.addToServoSizes(sizes); 714 shapeInfo.addToServoSizes(sizes); 715 } 716 717 // These string measurements are initially for all strings. At the end, 718 // if the measurement granularity is FineGrained, we subtract the 719 // measurements of the notable script sources and move them into 720 // |notableStrings|. 721 FOR_EACH_SIZE(DECL_SIZE) 722 UnusedGCThingSizes unusedGCThings; 723 StringInfo stringInfo; 724 ShapeInfo shapeInfo; 725 void* extra; // This field can be used by embedders. 726 727 typedef js::HashMap<JSString*, StringInfo, 728 js::InefficientNonFlatteningStringHashPolicy, 729 js::SystemAllocPolicy> StringsHashMap; 730 731 // |allStrings| is only used transiently. During the zone traversal it is 732 // filled with info about every string in the zone. It's then used to fill 733 // in |notableStrings| (which actually gets reported), and immediately 734 // discarded afterwards. 735 StringsHashMap* allStrings; 736 js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings; 737 bool isTotals; 738 739 #undef FOR_EACH_SIZE 740 }; 741 742 struct CompartmentStats 743 { 744 // We assume that |objectsPrivate| is on the malloc heap, but it's not 745 // actually guaranteed. But for Servo, at least, it's a moot point because 746 // it doesn't provide an ObjectPrivateVisitor so the value will always be 747 // zero. 748 #define FOR_EACH_SIZE(macro) \ 749 macro(Private, MallocHeap, objectsPrivate) \ 750 macro(Other, GCHeapUsed, scriptsGCHeap) \ 751 macro(Other, MallocHeap, scriptsMallocHeapData) \ 752 macro(Other, MallocHeap, baselineData) \ 753 macro(Other, MallocHeap, baselineStubsFallback) \ 754 macro(Other, MallocHeap, ionData) \ 755 macro(Other, MallocHeap, typeInferenceTypeScripts) \ 756 macro(Other, MallocHeap, typeInferenceAllocationSiteTables) \ 757 macro(Other, MallocHeap, typeInferenceArrayTypeTables) \ 758 macro(Other, MallocHeap, typeInferenceObjectTypeTables) \ 759 macro(Other, MallocHeap, compartmentObject) \ 760 macro(Other, MallocHeap, compartmentTables) \ 761 macro(Other, MallocHeap, innerViewsTable) \ 762 macro(Other, MallocHeap, lazyArrayBuffersTable) \ 763 macro(Other, MallocHeap, objectMetadataTable) \ 764 macro(Other, MallocHeap, crossCompartmentWrappersTable) \ 765 macro(Other, MallocHeap, regexpCompartment) \ 766 macro(Other, MallocHeap, savedStacksSet) \ 767 macro(Other, MallocHeap, varNamesSet) \ 768 macro(Other, MallocHeap, nonSyntacticLexicalScopesTable) \ 769 macro(Other, MallocHeap, jitCompartment) \ 770 macro(Other, MallocHeap, privateData) 771 CompartmentStatsCompartmentStats772 CompartmentStats() 773 : FOR_EACH_SIZE(ZERO_SIZE) 774 classInfo(), 775 extra(), 776 allClasses(nullptr), 777 notableClasses(), 778 isTotals(true) 779 {} 780 CompartmentStatsCompartmentStats781 CompartmentStats(CompartmentStats&& other) 782 : FOR_EACH_SIZE(COPY_OTHER_SIZE) 783 classInfo(mozilla::Move(other.classInfo)), 784 extra(other.extra), 785 allClasses(other.allClasses), 786 notableClasses(mozilla::Move(other.notableClasses)), 787 isTotals(other.isTotals) 788 { 789 other.allClasses = nullptr; 790 MOZ_ASSERT(!other.isTotals); 791 } 792 793 CompartmentStats(const CompartmentStats&) = delete; // disallow copying 794 ~CompartmentStatsCompartmentStats795 ~CompartmentStats() { 796 // |allClasses| is usually deleted and set to nullptr before this 797 // destructor runs. But there are failure cases due to OOMs that may 798 // prevent that, so it doesn't hurt to try again here. 799 js_delete(allClasses); 800 } 801 802 bool initClasses(JSRuntime* rt); 803 addSizesCompartmentStats804 void addSizes(const CompartmentStats& other) { 805 MOZ_ASSERT(isTotals); 806 FOR_EACH_SIZE(ADD_OTHER_SIZE) 807 classInfo.add(other.classInfo); 808 } 809 sizeOfLiveGCThingsCompartmentStats810 size_t sizeOfLiveGCThings() const { 811 MOZ_ASSERT(isTotals); 812 size_t n = 0; 813 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 814 n += classInfo.sizeOfLiveGCThings(); 815 return n; 816 } 817 addToTabSizesCompartmentStats818 void addToTabSizes(TabSizes* sizes) const { 819 MOZ_ASSERT(isTotals); 820 FOR_EACH_SIZE(ADD_TO_TAB_SIZES); 821 classInfo.addToTabSizes(sizes); 822 } 823 addToServoSizesCompartmentStats824 void addToServoSizes(ServoSizes *sizes) const { 825 MOZ_ASSERT(isTotals); 826 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); 827 classInfo.addToServoSizes(sizes); 828 } 829 830 // The class measurements in |classInfo| are initially for all classes. At 831 // the end, if the measurement granularity is FineGrained, we subtract the 832 // measurements of the notable classes and move them into |notableClasses|. 833 FOR_EACH_SIZE(DECL_SIZE) 834 ClassInfo classInfo; 835 void* extra; // This field can be used by embedders. 836 837 typedef js::HashMap<const char*, ClassInfo, 838 js::CStringHashPolicy, 839 js::SystemAllocPolicy> ClassesHashMap; 840 841 // These are similar to |allStrings| and |notableStrings| in ZoneStats. 842 ClassesHashMap* allClasses; 843 js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses; 844 bool isTotals; 845 846 #undef FOR_EACH_SIZE 847 }; 848 849 typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector; 850 typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector; 851 852 struct RuntimeStats 853 { 854 // |gcHeapChunkTotal| is ignored because it's the sum of all the other 855 // values. |gcHeapGCThings| is ignored because it's the sum of some of the 856 // values from the zones and compartments. Both of those values are not 857 // reported directly, but are just present for sanity-checking other 858 // values. 859 #define FOR_EACH_SIZE(macro) \ 860 macro(_, Ignore, gcHeapChunkTotal) \ 861 macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \ 862 macro(_, GCHeapUnused, gcHeapUnusedChunks) \ 863 macro(_, GCHeapUnused, gcHeapUnusedArenas) \ 864 macro(_, GCHeapAdmin, gcHeapChunkAdmin) \ 865 macro(_, Ignore, gcHeapGCThings) 866 RuntimeStatsRuntimeStats867 explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) 868 : FOR_EACH_SIZE(ZERO_SIZE) 869 runtime(), 870 cTotals(), 871 zTotals(), 872 compartmentStatsVector(), 873 zoneStatsVector(), 874 currZoneStats(nullptr), 875 mallocSizeOf_(mallocSizeOf) 876 {} 877 878 // Here's a useful breakdown of the GC heap. 879 // 880 // - rtStats.gcHeapChunkTotal 881 // - decommitted bytes 882 // - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks) 883 // - unused bytes 884 // - rtStats.gcHeapUnusedChunks (empty chunks) 885 // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks) 886 // - rtStats.zTotals.unusedGCThings.totalSize() (empty GC thing slots within non-empty arenas) 887 // - used bytes 888 // - rtStats.gcHeapChunkAdmin 889 // - rtStats.zTotals.gcHeapArenaAdmin 890 // - rtStats.gcHeapGCThings (in-use GC things) 891 // == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings() 892 // 893 // It's possible that some arenas in empty chunks may be decommitted, but 894 // we don't count those under rtStats.gcHeapDecommittedArenas because (a) 895 // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a 896 // multiple of the chunk size, which is good. 897 addToServoSizesRuntimeStats898 void addToServoSizes(ServoSizes *sizes) const { 899 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 900 runtime.addToServoSizes(sizes); 901 } 902 903 FOR_EACH_SIZE(DECL_SIZE) 904 905 RuntimeSizes runtime; 906 907 CompartmentStats cTotals; // The sum of this runtime's compartments' measurements. 908 ZoneStats zTotals; // The sum of this runtime's zones' measurements. 909 910 CompartmentStatsVector compartmentStatsVector; 911 ZoneStatsVector zoneStatsVector; 912 913 ZoneStats* currZoneStats; 914 915 mozilla::MallocSizeOf mallocSizeOf_; 916 917 virtual void initExtraCompartmentStats(JSCompartment* c, CompartmentStats* cstats) = 0; 918 virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats) = 0; 919 920 #undef FOR_EACH_SIZE 921 }; 922 923 class ObjectPrivateVisitor 924 { 925 public: 926 // Within CollectRuntimeStats, this method is called for each JS object 927 // that has an nsISupports pointer. 928 virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0; 929 930 // A callback that gets a JSObject's nsISupports pointer, if it has one. 931 // Note: this function does *not* addref |iface|. 932 typedef bool(*GetISupportsFun)(JSObject* obj, nsISupports** iface); 933 GetISupportsFun getISupports_; 934 ObjectPrivateVisitor(GetISupportsFun getISupports)935 explicit ObjectPrivateVisitor(GetISupportsFun getISupports) 936 : getISupports_(getISupports) 937 {} 938 }; 939 940 extern JS_PUBLIC_API(bool) 941 CollectRuntimeStats(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize); 942 943 extern JS_PUBLIC_API(size_t) 944 SystemCompartmentCount(JSContext* cx); 945 946 extern JS_PUBLIC_API(size_t) 947 UserCompartmentCount(JSContext* cx); 948 949 extern JS_PUBLIC_API(size_t) 950 PeakSizeOfTemporary(const JSContext* cx); 951 952 extern JS_PUBLIC_API(bool) 953 AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf, 954 ObjectPrivateVisitor* opv, TabSizes* sizes); 955 956 extern JS_PUBLIC_API(bool) 957 AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf, 958 ObjectPrivateVisitor *opv, ServoSizes *sizes); 959 960 } // namespace JS 961 962 #undef DECL_SIZE 963 #undef ZERO_SIZE 964 #undef COPY_OTHER_SIZE 965 #undef ADD_OTHER_SIZE 966 #undef SUB_OTHER_SIZE 967 #undef ADD_SIZE_TO_N 968 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING 969 #undef ADD_TO_TAB_SIZES 970 971 #endif /* js_MemoryMetrics_h */ 972