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, objectsMallocHeapElementsNonAsmJS) \ 170 macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \ 171 macro(Objects, NonHeap, objectsNonHeapElementsAsmJS) \ 172 macro(Objects, NonHeap, objectsNonHeapElementsMapped) \ 173 macro(Objects, NonHeap, objectsNonHeapCodeAsmJS) \ 174 macro(Objects, MallocHeap, objectsMallocHeapMisc) \ 175 \ 176 macro(Other, GCHeapUsed, shapesGCHeapTree) \ 177 macro(Other, GCHeapUsed, shapesGCHeapDict) \ 178 macro(Other, GCHeapUsed, shapesGCHeapBase) \ 179 macro(Other, MallocHeap, shapesMallocHeapTreeTables) \ 180 macro(Other, MallocHeap, shapesMallocHeapDictTables) \ 181 macro(Other, MallocHeap, shapesMallocHeapTreeKids) 182 ClassInfoClassInfo183 ClassInfo() 184 : FOR_EACH_SIZE(ZERO_SIZE) 185 dummy() 186 {} 187 addClassInfo188 void add(const ClassInfo& other) { 189 FOR_EACH_SIZE(ADD_OTHER_SIZE) 190 } 191 subtractClassInfo192 void subtract(const ClassInfo& other) { 193 FOR_EACH_SIZE(SUB_OTHER_SIZE) 194 } 195 sizeOfAllThingsClassInfo196 size_t sizeOfAllThings() const { 197 size_t n = 0; 198 FOR_EACH_SIZE(ADD_SIZE_TO_N) 199 return n; 200 } 201 isNotableClassInfo202 bool isNotable() const { 203 static const size_t NotabilityThreshold = 16 * 1024; 204 return sizeOfAllThings() >= NotabilityThreshold; 205 } 206 sizeOfLiveGCThingsClassInfo207 size_t sizeOfLiveGCThings() const { 208 size_t n = 0; 209 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 210 return n; 211 } 212 addToTabSizesClassInfo213 void addToTabSizes(TabSizes* sizes) const { 214 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 215 } 216 addToServoSizesClassInfo217 void addToServoSizes(ServoSizes *sizes) const { 218 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 219 } 220 221 FOR_EACH_SIZE(DECL_SIZE) 222 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 223 224 #undef FOR_EACH_SIZE 225 }; 226 227 /** 228 * Holds data about a notable class (one whose combined object and shape 229 * instances use more than a certain amount of memory) so we can report it 230 * individually. 231 * 232 * The only difference between this class and ClassInfo is that this class 233 * holds a copy of the filename. 234 */ 235 struct NotableClassInfo : public ClassInfo 236 { 237 NotableClassInfo(); 238 NotableClassInfo(const char* className, const ClassInfo& info); 239 NotableClassInfo(NotableClassInfo&& info); 240 NotableClassInfo& operator=(NotableClassInfo&& info); 241 ~NotableClassInfoNotableClassInfo242 ~NotableClassInfo() { 243 js_free(className_); 244 } 245 246 char* className_; 247 248 private: 249 NotableClassInfo(const NotableClassInfo& info) = delete; 250 }; 251 252 /** Data for tracking JIT-code memory usage. */ 253 struct CodeSizes 254 { 255 #define FOR_EACH_SIZE(macro) \ 256 macro(_, NonHeap, ion) \ 257 macro(_, NonHeap, baseline) \ 258 macro(_, NonHeap, regexp) \ 259 macro(_, NonHeap, other) \ 260 macro(_, NonHeap, unused) 261 CodeSizesCodeSizes262 CodeSizes() 263 : FOR_EACH_SIZE(ZERO_SIZE) 264 dummy() 265 {} 266 addToServoSizesCodeSizes267 void addToServoSizes(ServoSizes *sizes) const { 268 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 269 } 270 271 FOR_EACH_SIZE(DECL_SIZE) 272 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 273 274 #undef FOR_EACH_SIZE 275 }; 276 277 /** Data for tracking GC memory usage. */ 278 struct GCSizes 279 { 280 // |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted 281 // because we don't consider the nursery to be part of the GC heap. 282 #define FOR_EACH_SIZE(macro) \ 283 macro(_, MallocHeap, marker) \ 284 macro(_, NonHeap, nurseryCommitted) \ 285 macro(_, NonHeap, nurseryDecommitted) \ 286 macro(_, MallocHeap, nurseryMallocedBuffers) \ 287 macro(_, MallocHeap, storeBufferVals) \ 288 macro(_, MallocHeap, storeBufferCells) \ 289 macro(_, MallocHeap, storeBufferSlots) \ 290 macro(_, MallocHeap, storeBufferWholeCells) \ 291 macro(_, MallocHeap, storeBufferGenerics) 292 GCSizesGCSizes293 GCSizes() 294 : FOR_EACH_SIZE(ZERO_SIZE) 295 dummy() 296 {} 297 addToServoSizesGCSizes298 void addToServoSizes(ServoSizes *sizes) const { 299 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 300 } 301 302 FOR_EACH_SIZE(DECL_SIZE) 303 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 304 305 #undef FOR_EACH_SIZE 306 }; 307 308 /** 309 * This class holds information about the memory taken up by identical copies of 310 * a particular string. Multiple JSStrings may have their sizes aggregated 311 * together into one StringInfo object. Note that two strings with identical 312 * chars will not be aggregated together if one is a short string and the other 313 * is not. 314 */ 315 struct StringInfo 316 { 317 #define FOR_EACH_SIZE(macro) \ 318 macro(Strings, GCHeapUsed, gcHeapLatin1) \ 319 macro(Strings, GCHeapUsed, gcHeapTwoByte) \ 320 macro(Strings, MallocHeap, mallocHeapLatin1) \ 321 macro(Strings, MallocHeap, mallocHeapTwoByte) 322 StringInfoStringInfo323 StringInfo() 324 : FOR_EACH_SIZE(ZERO_SIZE) 325 numCopies(0) 326 {} 327 addStringInfo328 void add(const StringInfo& other) { 329 FOR_EACH_SIZE(ADD_OTHER_SIZE); 330 numCopies++; 331 } 332 subtractStringInfo333 void subtract(const StringInfo& other) { 334 FOR_EACH_SIZE(SUB_OTHER_SIZE); 335 numCopies--; 336 } 337 isNotableStringInfo338 bool isNotable() const { 339 static const size_t NotabilityThreshold = 16 * 1024; 340 size_t n = 0; 341 FOR_EACH_SIZE(ADD_SIZE_TO_N) 342 return n >= NotabilityThreshold; 343 } 344 sizeOfLiveGCThingsStringInfo345 size_t sizeOfLiveGCThings() const { 346 size_t n = 0; 347 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 348 return n; 349 } 350 addToTabSizesStringInfo351 void addToTabSizes(TabSizes* sizes) const { 352 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 353 } 354 addToServoSizesStringInfo355 void addToServoSizes(ServoSizes *sizes) const { 356 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 357 } 358 359 FOR_EACH_SIZE(DECL_SIZE) 360 uint32_t numCopies; // How many copies of the string have we seen? 361 362 #undef FOR_EACH_SIZE 363 }; 364 365 /** 366 * Holds data about a notable string (one which, counting all duplicates, uses 367 * more than a certain amount of memory) so we can report it individually. 368 * 369 * The only difference between this class and StringInfo is that 370 * NotableStringInfo holds a copy of some or all of the string's chars. 371 */ 372 struct NotableStringInfo : public StringInfo 373 { 374 static const size_t MAX_SAVED_CHARS = 1024; 375 376 NotableStringInfo(); 377 NotableStringInfo(JSString* str, const StringInfo& info); 378 NotableStringInfo(NotableStringInfo&& info); 379 NotableStringInfo& operator=(NotableStringInfo&& info); 380 ~NotableStringInfoNotableStringInfo381 ~NotableStringInfo() { 382 js_free(buffer); 383 } 384 385 char* buffer; 386 size_t length; 387 388 private: 389 NotableStringInfo(const NotableStringInfo& info) = delete; 390 }; 391 392 /** 393 * This class holds information about the memory taken up by script sources 394 * from a particular file. 395 */ 396 struct ScriptSourceInfo 397 { 398 #define FOR_EACH_SIZE(macro) \ 399 macro(_, MallocHeap, compressed) \ 400 macro(_, MallocHeap, uncompressed) \ 401 macro(_, MallocHeap, misc) 402 ScriptSourceInfoScriptSourceInfo403 ScriptSourceInfo() 404 : FOR_EACH_SIZE(ZERO_SIZE) 405 numScripts(0) 406 {} 407 addScriptSourceInfo408 void add(const ScriptSourceInfo& other) { 409 FOR_EACH_SIZE(ADD_OTHER_SIZE) 410 numScripts++; 411 } 412 subtractScriptSourceInfo413 void subtract(const ScriptSourceInfo& other) { 414 FOR_EACH_SIZE(SUB_OTHER_SIZE) 415 numScripts--; 416 } 417 addToServoSizesScriptSourceInfo418 void addToServoSizes(ServoSizes *sizes) const { 419 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 420 } 421 isNotableScriptSourceInfo422 bool isNotable() const { 423 static const size_t NotabilityThreshold = 16 * 1024; 424 size_t n = 0; 425 FOR_EACH_SIZE(ADD_SIZE_TO_N) 426 return n >= NotabilityThreshold; 427 } 428 429 FOR_EACH_SIZE(DECL_SIZE) 430 uint32_t numScripts; // How many ScriptSources come from this file? (It 431 // can be more than one in XML files that have 432 // multiple scripts in CDATA sections.) 433 #undef FOR_EACH_SIZE 434 }; 435 436 /** 437 * Holds data about a notable script source file (one whose combined 438 * script sources use more than a certain amount of memory) so we can report it 439 * individually. 440 * 441 * The only difference between this class and ScriptSourceInfo is that this 442 * class holds a copy of the filename. 443 */ 444 struct NotableScriptSourceInfo : public ScriptSourceInfo 445 { 446 NotableScriptSourceInfo(); 447 NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info); 448 NotableScriptSourceInfo(NotableScriptSourceInfo&& info); 449 NotableScriptSourceInfo& operator=(NotableScriptSourceInfo&& info); 450 ~NotableScriptSourceInfoNotableScriptSourceInfo451 ~NotableScriptSourceInfo() { 452 js_free(filename_); 453 } 454 455 char* filename_; 456 457 private: 458 NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete; 459 }; 460 461 /** 462 * These measurements relate directly to the JSRuntime, and not to zones and 463 * compartments within it. 464 */ 465 struct RuntimeSizes 466 { 467 #define FOR_EACH_SIZE(macro) \ 468 macro(_, MallocHeap, object) \ 469 macro(_, MallocHeap, atomsTable) \ 470 macro(_, MallocHeap, contexts) \ 471 macro(_, MallocHeap, dtoa) \ 472 macro(_, MallocHeap, temporary) \ 473 macro(_, MallocHeap, interpreterStack) \ 474 macro(_, MallocHeap, mathCache) \ 475 macro(_, MallocHeap, uncompressedSourceCache) \ 476 macro(_, MallocHeap, compressedSourceSet) \ 477 macro(_, MallocHeap, scriptData) 478 RuntimeSizesRuntimeSizes479 RuntimeSizes() 480 : FOR_EACH_SIZE(ZERO_SIZE) 481 scriptSourceInfo(), 482 code(), 483 gc(), 484 notableScriptSources() 485 { 486 allScriptSources = js_new<ScriptSourcesHashMap>(); 487 if (!allScriptSources || !allScriptSources->init()) 488 MOZ_CRASH("oom"); 489 } 490 ~RuntimeSizesRuntimeSizes491 ~RuntimeSizes() { 492 // |allScriptSources| is usually deleted and set to nullptr before this 493 // destructor runs. But there are failure cases due to OOMs that may 494 // prevent that, so it doesn't hurt to try again here. 495 js_delete(allScriptSources); 496 } 497 addToServoSizesRuntimeSizes498 void addToServoSizes(ServoSizes *sizes) const { 499 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 500 scriptSourceInfo.addToServoSizes(sizes); 501 code.addToServoSizes(sizes); 502 gc.addToServoSizes(sizes); 503 } 504 505 // The script source measurements in |scriptSourceInfo| are initially for 506 // all script sources. At the end, if the measurement granularity is 507 // FineGrained, we subtract the measurements of the notable script sources 508 // and move them into |notableScriptSources|. 509 FOR_EACH_SIZE(DECL_SIZE) 510 ScriptSourceInfo scriptSourceInfo; 511 CodeSizes code; 512 GCSizes gc; 513 514 typedef js::HashMap<const char*, ScriptSourceInfo, 515 js::CStringHashPolicy, 516 js::SystemAllocPolicy> ScriptSourcesHashMap; 517 518 // |allScriptSources| is only used transiently. During the reporting phase 519 // it is filled with info about every script source in the runtime. It's 520 // then used to fill in |notableScriptSources| (which actually gets 521 // reported), and immediately discarded afterwards. 522 ScriptSourcesHashMap* allScriptSources; 523 js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources; 524 525 #undef FOR_EACH_SIZE 526 }; 527 528 struct UnusedGCThingSizes 529 { 530 #define FOR_EACH_SIZE(macro) \ 531 macro(Other, GCHeapUnused, object) \ 532 macro(Other, GCHeapUnused, script) \ 533 macro(Other, GCHeapUnused, lazyScript) \ 534 macro(Other, GCHeapUnused, shape) \ 535 macro(Other, GCHeapUnused, baseShape) \ 536 macro(Other, GCHeapUnused, objectGroup) \ 537 macro(Other, GCHeapUnused, string) \ 538 macro(Other, GCHeapUnused, symbol) \ 539 macro(Other, GCHeapUnused, jitcode) \ 540 UnusedGCThingSizesUnusedGCThingSizes541 UnusedGCThingSizes() 542 : FOR_EACH_SIZE(ZERO_SIZE) 543 dummy() 544 {} 545 UnusedGCThingSizesUnusedGCThingSizes546 UnusedGCThingSizes(UnusedGCThingSizes&& other) 547 : FOR_EACH_SIZE(COPY_OTHER_SIZE) 548 dummy() 549 {} 550 addToKindUnusedGCThingSizes551 void addToKind(JS::TraceKind kind, intptr_t n) { 552 switch (kind) { 553 case JS::TraceKind::Object: object += n; break; 554 case JS::TraceKind::String: string += n; break; 555 case JS::TraceKind::Symbol: symbol += n; break; 556 case JS::TraceKind::Script: script += n; break; 557 case JS::TraceKind::Shape: shape += n; break; 558 case JS::TraceKind::BaseShape: baseShape += n; break; 559 case JS::TraceKind::JitCode: jitcode += n; break; 560 case JS::TraceKind::LazyScript: lazyScript += n; break; 561 case JS::TraceKind::ObjectGroup: objectGroup += n; break; 562 default: 563 MOZ_CRASH("Bad trace kind for UnusedGCThingSizes"); 564 } 565 } 566 addSizesUnusedGCThingSizes567 void addSizes(const UnusedGCThingSizes& other) { 568 FOR_EACH_SIZE(ADD_OTHER_SIZE) 569 } 570 totalSizeUnusedGCThingSizes571 size_t totalSize() const { 572 size_t n = 0; 573 FOR_EACH_SIZE(ADD_SIZE_TO_N) 574 return n; 575 } 576 addToTabSizesUnusedGCThingSizes577 void addToTabSizes(JS::TabSizes *sizes) const { 578 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 579 } 580 addToServoSizesUnusedGCThingSizes581 void addToServoSizes(JS::ServoSizes *sizes) const { 582 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 583 } 584 585 FOR_EACH_SIZE(DECL_SIZE) 586 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) 587 588 #undef FOR_EACH_SIZE 589 }; 590 591 struct ZoneStats 592 { 593 #define FOR_EACH_SIZE(macro) \ 594 macro(Other, GCHeapUsed, symbolsGCHeap) \ 595 macro(Other, GCHeapAdmin, gcHeapArenaAdmin) \ 596 macro(Other, GCHeapUsed, lazyScriptsGCHeap) \ 597 macro(Other, MallocHeap, lazyScriptsMallocHeap) \ 598 macro(Other, GCHeapUsed, jitCodesGCHeap) \ 599 macro(Other, GCHeapUsed, objectGroupsGCHeap) \ 600 macro(Other, MallocHeap, objectGroupsMallocHeap) \ 601 macro(Other, MallocHeap, typePool) \ 602 macro(Other, MallocHeap, baselineStubsOptimized) \ 603 macro(Other, MallocHeap, uniqueIdMap) 604 ZoneStatsZoneStats605 ZoneStats() 606 : FOR_EACH_SIZE(ZERO_SIZE) 607 unusedGCThings(), 608 stringInfo(), 609 extra(), 610 allStrings(nullptr), 611 notableStrings(), 612 isTotals(true) 613 {} 614 ZoneStatsZoneStats615 ZoneStats(ZoneStats&& other) 616 : FOR_EACH_SIZE(COPY_OTHER_SIZE) 617 unusedGCThings(mozilla::Move(other.unusedGCThings)), 618 stringInfo(mozilla::Move(other.stringInfo)), 619 extra(other.extra), 620 allStrings(other.allStrings), 621 notableStrings(mozilla::Move(other.notableStrings)), 622 isTotals(other.isTotals) 623 { 624 other.allStrings = nullptr; 625 MOZ_ASSERT(!other.isTotals); 626 } 627 ~ZoneStatsZoneStats628 ~ZoneStats() { 629 // |allStrings| is usually deleted and set to nullptr before this 630 // destructor runs. But there are failure cases due to OOMs that may 631 // prevent that, so it doesn't hurt to try again here. 632 js_delete(allStrings); 633 } 634 635 bool initStrings(JSRuntime* rt); 636 addSizesZoneStats637 void addSizes(const ZoneStats& other) { 638 MOZ_ASSERT(isTotals); 639 FOR_EACH_SIZE(ADD_OTHER_SIZE) 640 unusedGCThings.addSizes(other.unusedGCThings); 641 stringInfo.add(other.stringInfo); 642 } 643 sizeOfLiveGCThingsZoneStats644 size_t sizeOfLiveGCThings() const { 645 MOZ_ASSERT(isTotals); 646 size_t n = 0; 647 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 648 n += stringInfo.sizeOfLiveGCThings(); 649 return n; 650 } 651 addToTabSizesZoneStats652 void addToTabSizes(JS::TabSizes* sizes) const { 653 MOZ_ASSERT(isTotals); 654 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) 655 unusedGCThings.addToTabSizes(sizes); 656 stringInfo.addToTabSizes(sizes); 657 } 658 addToServoSizesZoneStats659 void addToServoSizes(JS::ServoSizes *sizes) const { 660 MOZ_ASSERT(isTotals); 661 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 662 unusedGCThings.addToServoSizes(sizes); 663 stringInfo.addToServoSizes(sizes); 664 } 665 666 // These string measurements are initially for all strings. At the end, 667 // if the measurement granularity is FineGrained, we subtract the 668 // measurements of the notable script sources and move them into 669 // |notableStrings|. 670 FOR_EACH_SIZE(DECL_SIZE) 671 UnusedGCThingSizes unusedGCThings; 672 StringInfo stringInfo; 673 void* extra; // This field can be used by embedders. 674 675 typedef js::HashMap<JSString*, StringInfo, 676 js::InefficientNonFlatteningStringHashPolicy, 677 js::SystemAllocPolicy> StringsHashMap; 678 679 // |allStrings| is only used transiently. During the zone traversal it is 680 // filled with info about every string in the zone. It's then used to fill 681 // in |notableStrings| (which actually gets reported), and immediately 682 // discarded afterwards. 683 StringsHashMap* allStrings; 684 js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings; 685 bool isTotals; 686 687 #undef FOR_EACH_SIZE 688 }; 689 690 struct CompartmentStats 691 { 692 // We assume that |objectsPrivate| is on the malloc heap, but it's not 693 // actually guaranteed. But for Servo, at least, it's a moot point because 694 // it doesn't provide an ObjectPrivateVisitor so the value will always be 695 // zero. 696 #define FOR_EACH_SIZE(macro) \ 697 macro(Private, MallocHeap, objectsPrivate) \ 698 macro(Other, GCHeapUsed, scriptsGCHeap) \ 699 macro(Other, MallocHeap, scriptsMallocHeapData) \ 700 macro(Other, MallocHeap, baselineData) \ 701 macro(Other, MallocHeap, baselineStubsFallback) \ 702 macro(Other, MallocHeap, ionData) \ 703 macro(Other, MallocHeap, typeInferenceTypeScripts) \ 704 macro(Other, MallocHeap, typeInferenceAllocationSiteTables) \ 705 macro(Other, MallocHeap, typeInferenceArrayTypeTables) \ 706 macro(Other, MallocHeap, typeInferenceObjectTypeTables) \ 707 macro(Other, MallocHeap, compartmentObject) \ 708 macro(Other, MallocHeap, compartmentTables) \ 709 macro(Other, MallocHeap, innerViewsTable) \ 710 macro(Other, MallocHeap, lazyArrayBuffersTable) \ 711 macro(Other, MallocHeap, objectMetadataTable) \ 712 macro(Other, MallocHeap, crossCompartmentWrappersTable) \ 713 macro(Other, MallocHeap, regexpCompartment) \ 714 macro(Other, MallocHeap, savedStacksSet) \ 715 macro(Other, MallocHeap, nonSyntacticLexicalScopesTable) 716 CompartmentStatsCompartmentStats717 CompartmentStats() 718 : FOR_EACH_SIZE(ZERO_SIZE) 719 classInfo(), 720 extra(), 721 allClasses(nullptr), 722 notableClasses(), 723 isTotals(true) 724 {} 725 CompartmentStatsCompartmentStats726 CompartmentStats(CompartmentStats&& other) 727 : FOR_EACH_SIZE(COPY_OTHER_SIZE) 728 classInfo(mozilla::Move(other.classInfo)), 729 extra(other.extra), 730 allClasses(other.allClasses), 731 notableClasses(mozilla::Move(other.notableClasses)), 732 isTotals(other.isTotals) 733 { 734 other.allClasses = nullptr; 735 MOZ_ASSERT(!other.isTotals); 736 } 737 ~CompartmentStatsCompartmentStats738 ~CompartmentStats() { 739 // |allClasses| is usually deleted and set to nullptr before this 740 // destructor runs. But there are failure cases due to OOMs that may 741 // prevent that, so it doesn't hurt to try again here. 742 js_delete(allClasses); 743 } 744 745 bool initClasses(JSRuntime* rt); 746 addSizesCompartmentStats747 void addSizes(const CompartmentStats& other) { 748 MOZ_ASSERT(isTotals); 749 FOR_EACH_SIZE(ADD_OTHER_SIZE) 750 classInfo.add(other.classInfo); 751 } 752 sizeOfLiveGCThingsCompartmentStats753 size_t sizeOfLiveGCThings() const { 754 MOZ_ASSERT(isTotals); 755 size_t n = 0; 756 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) 757 n += classInfo.sizeOfLiveGCThings(); 758 return n; 759 } 760 addToTabSizesCompartmentStats761 void addToTabSizes(TabSizes* sizes) const { 762 MOZ_ASSERT(isTotals); 763 FOR_EACH_SIZE(ADD_TO_TAB_SIZES); 764 classInfo.addToTabSizes(sizes); 765 } 766 addToServoSizesCompartmentStats767 void addToServoSizes(ServoSizes *sizes) const { 768 MOZ_ASSERT(isTotals); 769 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); 770 classInfo.addToServoSizes(sizes); 771 } 772 773 // The class measurements in |classInfo| are initially for all classes. At 774 // the end, if the measurement granularity is FineGrained, we subtract the 775 // measurements of the notable classes and move them into |notableClasses|. 776 FOR_EACH_SIZE(DECL_SIZE) 777 ClassInfo classInfo; 778 void* extra; // This field can be used by embedders. 779 780 typedef js::HashMap<const char*, ClassInfo, 781 js::CStringHashPolicy, 782 js::SystemAllocPolicy> ClassesHashMap; 783 784 // These are similar to |allStrings| and |notableStrings| in ZoneStats. 785 ClassesHashMap* allClasses; 786 js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses; 787 bool isTotals; 788 789 #undef FOR_EACH_SIZE 790 }; 791 792 typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector; 793 typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector; 794 795 struct RuntimeStats 796 { 797 // |gcHeapChunkTotal| is ignored because it's the sum of all the other 798 // values. |gcHeapGCThings| is ignored because it's the sum of some of the 799 // values from the zones and compartments. Both of those values are not 800 // reported directly, but are just present for sanity-checking other 801 // values. 802 #define FOR_EACH_SIZE(macro) \ 803 macro(_, Ignore, gcHeapChunkTotal) \ 804 macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \ 805 macro(_, GCHeapUnused, gcHeapUnusedChunks) \ 806 macro(_, GCHeapUnused, gcHeapUnusedArenas) \ 807 macro(_, GCHeapAdmin, gcHeapChunkAdmin) \ 808 macro(_, Ignore, gcHeapGCThings) 809 RuntimeStatsRuntimeStats810 explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) 811 : FOR_EACH_SIZE(ZERO_SIZE) 812 runtime(), 813 cTotals(), 814 zTotals(), 815 compartmentStatsVector(), 816 zoneStatsVector(), 817 currZoneStats(nullptr), 818 mallocSizeOf_(mallocSizeOf) 819 {} 820 821 // Here's a useful breakdown of the GC heap. 822 // 823 // - rtStats.gcHeapChunkTotal 824 // - decommitted bytes 825 // - rtStats.gcHeapDecommittedArenas (decommitted arenas 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() (empty GC thing slots within non-empty arenas) 830 // - used bytes 831 // - rtStats.gcHeapChunkAdmin 832 // - rtStats.zTotals.gcHeapArenaAdmin 833 // - rtStats.gcHeapGCThings (in-use GC things) 834 // == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings() 835 // 836 // It's possible that some arenas in empty chunks may be decommitted, but 837 // we don't count those under rtStats.gcHeapDecommittedArenas because (a) 838 // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a 839 // multiple of the chunk size, which is good. 840 addToServoSizesRuntimeStats841 void addToServoSizes(ServoSizes *sizes) const { 842 FOR_EACH_SIZE(ADD_TO_SERVO_SIZES) 843 runtime.addToServoSizes(sizes); 844 } 845 846 FOR_EACH_SIZE(DECL_SIZE) 847 848 RuntimeSizes runtime; 849 850 CompartmentStats cTotals; // The sum of this runtime's compartments' measurements. 851 ZoneStats zTotals; // The sum of this runtime's zones' measurements. 852 853 CompartmentStatsVector compartmentStatsVector; 854 ZoneStatsVector zoneStatsVector; 855 856 ZoneStats* currZoneStats; 857 858 mozilla::MallocSizeOf mallocSizeOf_; 859 860 virtual void initExtraCompartmentStats(JSCompartment* c, CompartmentStats* cstats) = 0; 861 virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats) = 0; 862 863 #undef FOR_EACH_SIZE 864 }; 865 866 class ObjectPrivateVisitor 867 { 868 public: 869 // Within CollectRuntimeStats, this method is called for each JS object 870 // that has an nsISupports pointer. 871 virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0; 872 873 // A callback that gets a JSObject's nsISupports pointer, if it has one. 874 // Note: this function does *not* addref |iface|. 875 typedef bool(*GetISupportsFun)(JSObject* obj, nsISupports** iface); 876 GetISupportsFun getISupports_; 877 ObjectPrivateVisitor(GetISupportsFun getISupports)878 explicit ObjectPrivateVisitor(GetISupportsFun getISupports) 879 : getISupports_(getISupports) 880 {} 881 }; 882 883 extern JS_PUBLIC_API(bool) 884 CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize); 885 886 extern JS_PUBLIC_API(size_t) 887 SystemCompartmentCount(JSRuntime* rt); 888 889 extern JS_PUBLIC_API(size_t) 890 UserCompartmentCount(JSRuntime* rt); 891 892 extern JS_PUBLIC_API(size_t) 893 PeakSizeOfTemporary(const JSRuntime* rt); 894 895 extern JS_PUBLIC_API(bool) 896 AddSizeOfTab(JSRuntime* rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf, 897 ObjectPrivateVisitor* opv, TabSizes* sizes); 898 899 extern JS_PUBLIC_API(bool) 900 AddServoSizeOf(JSRuntime *rt, mozilla::MallocSizeOf mallocSizeOf, 901 ObjectPrivateVisitor *opv, ServoSizes *sizes); 902 903 } // namespace JS 904 905 #undef DECL_SIZE 906 #undef ZERO_SIZE 907 #undef COPY_OTHER_SIZE 908 #undef ADD_OTHER_SIZE 909 #undef SUB_OTHER_SIZE 910 #undef ADD_SIZE_TO_N 911 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING 912 #undef ADD_TO_TAB_SIZES 913 914 #endif /* js_MemoryMetrics_h */ 915