GCAPI.cpp (24518B)
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 /* 8 * API functions and methods used by the rest of SpiderMonkey and by embeddings. 9 */ 10 11 #include "mozilla/TimeStamp.h" 12 13 #include "jsapi.h" 14 #include "jsfriendapi.h" 15 16 #include "gc/GC.h" 17 #include "gc/PublicIterators.h" 18 #include "jit/JitZone.h" 19 #include "js/HeapAPI.h" 20 #include "js/Value.h" 21 #include "util/DifferentialTesting.h" 22 #include "vm/HelperThreads.h" 23 #include "vm/Realm.h" 24 #include "vm/Scope.h" 25 26 #include "gc/Marking-inl.h" 27 #include "gc/StableCellHasher-inl.h" 28 #include "vm/GeckoProfiler-inl.h" 29 #include "vm/JSContext-inl.h" 30 31 using namespace js; 32 using namespace js::gc; 33 34 using mozilla::TimeStamp; 35 36 extern JS_PUBLIC_API bool js::AddRawValueRoot(JSContext* cx, Value* vp, 37 const char* name) { 38 MOZ_ASSERT(vp); 39 MOZ_ASSERT(name); 40 bool ok = cx->runtime()->gc.addRoot(vp, name); 41 if (!ok) { 42 JS_ReportOutOfMemory(cx); 43 } 44 return ok; 45 } 46 47 extern JS_PUBLIC_API void js::RemoveRawValueRoot(JSContext* cx, Value* vp) { 48 cx->runtime()->gc.removeRoot(vp); 49 } 50 51 JS_PUBLIC_API JS::HeapState JS::RuntimeHeapState() { 52 return TlsContext.get()->runtime()->gc.heapState(); 53 } 54 55 JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSContext* cx) 56 : cx(cx) { 57 if (!cx->generationalDisabled) { 58 cx->runtime()->gc.evictNursery(JS::GCReason::DISABLE_GENERATIONAL_GC); 59 cx->nursery().disable(); 60 } 61 ++cx->generationalDisabled; 62 MOZ_ASSERT(cx->nursery().isEmpty()); 63 } 64 65 JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() { 66 if (--cx->generationalDisabled == 0 && 67 cx->runtime()->gc.tunables.gcMaxNurseryBytes() > 0) { 68 cx->nursery().enable(); 69 } 70 } 71 72 JS_PUBLIC_API bool JS::IsGenerationalGCEnabled(JSRuntime* rt) { 73 return !rt->mainContextFromOwnThread()->generationalDisabled; 74 } 75 76 AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx) : cx(cx) { 77 ++cx->compactingDisabledCount; 78 if (cx->runtime()->gc.isIncrementalGCInProgress() && 79 cx->runtime()->gc.isCompactingGc()) { 80 FinishGC(cx); 81 } 82 } 83 84 AutoDisableCompactingGC::~AutoDisableCompactingGC() { 85 MOZ_ASSERT(cx->compactingDisabledCount > 0); 86 --cx->compactingDisabledCount; 87 } 88 89 #ifdef DEBUG 90 91 /* Should only be called manually under gdb */ 92 void PreventGCDuringInteractiveDebug() { TlsContext.get()->suppressGC++; } 93 94 #endif 95 96 void js::ReleaseAllJITCode(JS::GCContext* gcx) { 97 js::CancelOffThreadCompile(gcx->runtime()); 98 99 for (ZonesIter zone(gcx->runtime(), SkipAtoms); !zone.done(); zone.next()) { 100 zone->forceDiscardJitCode(gcx); 101 if (jit::JitZone* jitZone = zone->jitZone()) { 102 jitZone->discardStubs(); 103 } 104 } 105 106 gcx->runtime()->clearSelfHostedJitCache(); 107 } 108 109 AutoSuppressGC::AutoSuppressGC(JSContext* cx) 110 : suppressGC_(cx->suppressGC.ref()) { 111 suppressGC_++; 112 } 113 114 #ifdef DEBUG 115 AutoDisableProxyCheck::AutoDisableProxyCheck() { 116 TlsContext.get()->disableStrictProxyChecking(); 117 } 118 119 AutoDisableProxyCheck::~AutoDisableProxyCheck() { 120 TlsContext.get()->enableStrictProxyChecking(); 121 } 122 123 JS_PUBLIC_API void JS::AssertGCThingMustBeTenured(JSObject* obj) { 124 MOZ_ASSERT(obj->isTenured()); 125 MOZ_ASSERT(obj->getClass()->hasFinalize() && 126 !(obj->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE)); 127 } 128 129 JS_PUBLIC_API void JS::AssertGCThingIsNotNurseryAllocable(Cell* cell) { 130 MOZ_ASSERT(cell); 131 MOZ_ASSERT(!cell->is<JSObject>() && !cell->is<JSString>() && 132 !cell->is<JS::BigInt>() && !cell->is<GetterSetter>()); 133 } 134 135 JS_PUBLIC_API void js::gc::AssertGCThingHasType(js::gc::Cell* cell, 136 JS::TraceKind kind) { 137 if (!cell) { 138 MOZ_ASSERT(kind == JS::TraceKind::Null); 139 return; 140 } 141 142 MOZ_ASSERT(IsCellPointerValid(cell)); 143 MOZ_ASSERT(cell->getTraceKind() == kind); 144 } 145 #endif 146 147 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 148 149 JS::AutoAssertNoGC::AutoAssertNoGC(JSContext* maybecx) { 150 if (maybecx) { 151 cx_ = maybecx; 152 } else if (TlsContext.initialized()) { 153 cx_ = TlsContext.get(); 154 } else { 155 cx_ = nullptr; 156 } 157 if (cx_) { 158 cx_->inUnsafeRegion++; 159 } 160 } 161 162 JS::AutoAssertNoGC::~AutoAssertNoGC() { reset(); } 163 164 void JS::AutoAssertNoGC::reset() { 165 if (cx_) { 166 MOZ_ASSERT(cx_->inUnsafeRegion > 0); 167 cx_->inUnsafeRegion--; 168 cx_ = nullptr; 169 } 170 } 171 172 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED 173 174 #ifdef DEBUG 175 176 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime* rt) 177 : runtime_(rt) { 178 MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); 179 MOZ_ASSERT(!JS::RuntimeHeapIsBusy()); 180 runtime_->gc.heapState_ = HeapState::CycleCollecting; 181 } 182 183 JS::AutoEnterCycleCollection::~AutoEnterCycleCollection() { 184 MOZ_ASSERT(JS::RuntimeHeapIsCycleCollecting()); 185 runtime_->gc.heapState_ = HeapState::Idle; 186 } 187 188 JS::AutoAssertGCCallback::AutoAssertGCCallback() : AutoSuppressGCAnalysis() { 189 MOZ_ASSERT(JS::RuntimeHeapIsCollecting()); 190 } 191 192 #endif // DEBUG 193 194 JS_PUBLIC_API const char* JS::GCTraceKindToAscii(JS::TraceKind kind) { 195 switch (kind) { 196 #define MAP_NAME(name, _0, _1, _2) \ 197 case JS::TraceKind::name: \ 198 return "JS " #name; 199 JS_FOR_EACH_TRACEKIND(MAP_NAME); 200 #undef MAP_NAME 201 default: 202 return "Invalid"; 203 } 204 } 205 206 JS_PUBLIC_API size_t JS::GCTraceKindSize(JS::TraceKind kind) { 207 switch (kind) { 208 #define MAP_SIZE(name, type, _0, _1) \ 209 case JS::TraceKind::name: \ 210 return sizeof(type); 211 JS_FOR_EACH_TRACEKIND(MAP_SIZE); 212 #undef MAP_SIZE 213 default: 214 return 0; 215 } 216 } 217 218 JS::GCCellPtr::GCCellPtr(const Value& v) 219 : GCCellPtr(v.toGCThing(), v.traceKind()) {} 220 221 JS::TraceKind JS::GCCellPtr::outOfLineKind() const { 222 MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask); 223 MOZ_ASSERT(asCell()->isTenured()); 224 return MapAllocToTraceKind(asCell()->asTenured().getAllocKind()); 225 } 226 227 JS_PUBLIC_API void JS::PrepareZoneForGC(JSContext* cx, Zone* zone) { 228 AssertHeapIsIdle(); 229 CHECK_THREAD(cx); 230 231 // If we got the zone from a shared atom, we may have the wrong atoms zone 232 // here. 233 if (zone->isAtomsZone()) { 234 zone = cx->runtime()->atomsZone(); 235 } 236 237 MOZ_ASSERT(cx->runtime()->gc.hasZone(zone)); 238 zone->scheduleGC(); 239 } 240 241 JS_PUBLIC_API void JS::PrepareForFullGC(JSContext* cx) { 242 AssertHeapIsIdle(); 243 CHECK_THREAD(cx); 244 245 cx->runtime()->gc.fullGCRequested = true; 246 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { 247 zone->scheduleGC(); 248 } 249 } 250 251 JS_PUBLIC_API void JS::PrepareForIncrementalGC(JSContext* cx) { 252 AssertHeapIsIdle(); 253 CHECK_THREAD(cx); 254 255 if (!JS::IsIncrementalGCInProgress(cx)) { 256 return; 257 } 258 259 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { 260 if (zone->wasGCStarted()) { 261 zone->scheduleGC(); 262 } 263 } 264 } 265 266 JS_PUBLIC_API bool JS::IsGCScheduled(JSContext* cx) { 267 AssertHeapIsIdle(); 268 CHECK_THREAD(cx); 269 270 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { 271 if (zone->isGCScheduled()) { 272 return true; 273 } 274 } 275 276 return false; 277 } 278 279 JS_PUBLIC_API void JS::SkipZoneForGC(JSContext* cx, Zone* zone) { 280 AssertHeapIsIdle(); 281 CHECK_THREAD(cx); 282 MOZ_ASSERT(cx->runtime()->gc.hasZone(zone)); 283 284 cx->runtime()->gc.fullGCRequested = false; 285 zone->unscheduleGC(); 286 } 287 288 static inline void CheckGCOptions(JS::GCOptions options) { 289 MOZ_ASSERT(options == JS::GCOptions::Normal || 290 options == JS::GCOptions::Shrink || 291 options == JS::GCOptions::Shutdown); 292 } 293 294 JS_PUBLIC_API void JS::NonIncrementalGC(JSContext* cx, JS::GCOptions options, 295 GCReason reason) { 296 AssertHeapIsIdle(); 297 CHECK_THREAD(cx); 298 CheckGCOptions(options); 299 300 cx->runtime()->gc.gc(options, reason); 301 302 MOZ_ASSERT(!IsIncrementalGCInProgress(cx)); 303 } 304 305 JS_PUBLIC_API void JS::StartIncrementalGC(JSContext* cx, JS::GCOptions options, 306 GCReason reason, 307 const JS::SliceBudget& budget) { 308 AssertHeapIsIdle(); 309 CHECK_THREAD(cx); 310 CheckGCOptions(options); 311 312 cx->runtime()->gc.startGC(options, reason, budget); 313 } 314 315 JS_PUBLIC_API void JS::IncrementalGCSlice(JSContext* cx, GCReason reason, 316 const JS::SliceBudget& budget) { 317 AssertHeapIsIdle(); 318 CHECK_THREAD(cx); 319 320 cx->runtime()->gc.gcSlice(reason, budget); 321 } 322 323 JS_PUBLIC_API bool JS::IncrementalGCHasForegroundWork(JSContext* cx) { 324 AssertHeapIsIdle(); 325 CHECK_THREAD(cx); 326 327 return cx->runtime()->gc.hasForegroundWork(); 328 } 329 330 JS_PUBLIC_API void JS::FinishIncrementalGC(JSContext* cx, GCReason reason) { 331 AssertHeapIsIdle(); 332 CHECK_THREAD(cx); 333 334 cx->runtime()->gc.finishGC(reason); 335 } 336 337 JS_PUBLIC_API void JS::AbortIncrementalGC(JSContext* cx) { 338 AssertHeapIsIdle(); 339 CHECK_THREAD(cx); 340 341 if (IsIncrementalGCInProgress(cx)) { 342 cx->runtime()->gc.abortGC(); 343 } 344 } 345 346 char16_t* JS::GCDescription::formatSliceMessage(JSContext* cx) const { 347 UniqueChars cstr = cx->runtime()->gc.stats().formatCompactSliceMessage(); 348 349 size_t nchars = strlen(cstr.get()); 350 UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1)); 351 if (!out) { 352 return nullptr; 353 } 354 out.get()[nchars] = 0; 355 356 CopyAndInflateChars(out.get(), cstr.get(), nchars); 357 return out.release(); 358 } 359 360 char16_t* JS::GCDescription::formatSummaryMessage(JSContext* cx) const { 361 UniqueChars cstr = cx->runtime()->gc.stats().formatCompactSummaryMessage(); 362 363 size_t nchars = strlen(cstr.get()); 364 UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1)); 365 if (!out) { 366 return nullptr; 367 } 368 out.get()[nchars] = 0; 369 370 CopyAndInflateChars(out.get(), cstr.get(), nchars); 371 return out.release(); 372 } 373 374 JS::dbg::GarbageCollectionEvent::Ptr JS::GCDescription::toGCEvent( 375 JSContext* cx) const { 376 return JS::dbg::GarbageCollectionEvent::Create( 377 cx->runtime(), cx->runtime()->gc.stats(), 378 cx->runtime()->gc.majorGCCount()); 379 } 380 381 TimeStamp JS::GCDescription::startTime(JSContext* cx) const { 382 return cx->runtime()->gc.stats().start(); 383 } 384 385 TimeStamp JS::GCDescription::endTime(JSContext* cx) const { 386 return cx->runtime()->gc.stats().end(); 387 } 388 389 TimeStamp JS::GCDescription::lastSliceStart(JSContext* cx) const { 390 return cx->runtime()->gc.stats().slices().back().start; 391 } 392 393 TimeStamp JS::GCDescription::lastSliceEnd(JSContext* cx) const { 394 return cx->runtime()->gc.stats().slices().back().end; 395 } 396 397 JS::UniqueChars JS::GCDescription::sliceToJSONProfiler(JSContext* cx) const { 398 size_t slices = cx->runtime()->gc.stats().slices().length(); 399 MOZ_ASSERT(slices > 0); 400 return cx->runtime()->gc.stats().renderJsonSlice(slices - 1); 401 } 402 403 JS::UniqueChars JS::GCDescription::formatJSONProfiler(JSContext* cx) const { 404 return cx->runtime()->gc.stats().renderJsonMessage(); 405 } 406 407 JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) { 408 JSRuntime* rt = cx->runtime(); 409 return rt->gc.stats().renderNurseryJson(); 410 } 411 412 JS_PUBLIC_API JS::GCSliceCallback JS::SetGCSliceCallback( 413 JSContext* cx, GCSliceCallback callback) { 414 return cx->runtime()->gc.setSliceCallback(callback); 415 } 416 417 JS_PUBLIC_API JS::DoCycleCollectionCallback JS::SetDoCycleCollectionCallback( 418 JSContext* cx, JS::DoCycleCollectionCallback callback) { 419 return cx->runtime()->gc.setDoCycleCollectionCallback(callback); 420 } 421 422 JS_PUBLIC_API bool JS::AddGCNurseryCollectionCallback( 423 JSContext* cx, GCNurseryCollectionCallback callback, void* data) { 424 return cx->runtime()->gc.addNurseryCollectionCallback(callback, data); 425 } 426 427 JS_PUBLIC_API void JS::RemoveGCNurseryCollectionCallback( 428 JSContext* cx, GCNurseryCollectionCallback callback, void* data) { 429 return cx->runtime()->gc.removeNurseryCollectionCallback(callback, data); 430 } 431 432 JS_PUBLIC_API void JS::SetLowMemoryState(JSContext* cx, bool newState) { 433 return cx->runtime()->gc.setLowMemoryState(newState); 434 } 435 436 JS_PUBLIC_API bool JS::IsIncrementalGCEnabled(JSContext* cx) { 437 return cx->runtime()->gc.isIncrementalGCEnabled(); 438 } 439 440 JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSContext* cx) { 441 return cx->runtime()->gc.isIncrementalGCInProgress(); 442 } 443 444 JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSRuntime* rt) { 445 return rt->gc.isIncrementalGCInProgress() && 446 !rt->gc.isVerifyPreBarriersEnabled(); 447 } 448 449 JS_PUBLIC_API bool JS::IsIncrementalBarrierNeeded(JSContext* cx) { 450 if (JS::RuntimeHeapIsBusy()) { 451 return false; 452 } 453 454 auto state = cx->runtime()->gc.state(); 455 return state != gc::State::NotActive && state <= gc::State::Sweep; 456 } 457 458 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(JSObject* obj) { 459 if (!obj) { 460 return; 461 } 462 463 AutoGeckoProfilerEntry profilingStackFrame( 464 TlsContext.get(), "IncrementalPreWriteBarrier(JSObject*)", 465 JS::ProfilingCategoryPair::GCCC_Barrier); 466 PreWriteBarrier(obj); 467 } 468 469 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(GCCellPtr thing) { 470 if (!thing) { 471 return; 472 } 473 474 AutoGeckoProfilerEntry profilingStackFrame( 475 TlsContext.get(), "IncrementalPreWriteBarrier(GCCellPtr)", 476 JS::ProfilingCategoryPair::GCCC_Barrier); 477 CellPtrPreWriteBarrier(thing); 478 } 479 480 JS_PUBLIC_API bool JS::WasIncrementalGC(JSRuntime* rt) { 481 return rt->gc.isIncrementalGc(); 482 } 483 484 bool js::gc::CreateUniqueIdForNativeObject(NativeObject* nobj, uint64_t* uidp) { 485 JSRuntime* runtime = nobj->runtimeFromMainThread(); 486 *uidp = NextCellUniqueId(runtime); 487 return nobj->setUniqueId(runtime, *uidp); 488 } 489 490 bool js::gc::CreateUniqueIdForNonNativeObject(Cell* cell, 491 UniqueIdMap::AddPtr ptr, 492 uint64_t* uidp) { 493 // If the cell is in the nursery, hopefully unlikely, then we need to tell the 494 // nursery about it so that it can sweep the uid if the thing does not get 495 // tenured. 496 JSRuntime* runtime = cell->runtimeFromMainThread(); 497 if (IsInsideNursery(cell) && 498 !runtime->gc.nursery().addedUniqueIdToCell(cell)) { 499 return false; 500 } 501 502 // Set a new uid on the cell. 503 *uidp = NextCellUniqueId(runtime); 504 return cell->zone()->uniqueIds().add(ptr, cell, *uidp); 505 } 506 507 uint64_t js::gc::NextCellUniqueId(JSRuntime* rt) { 508 return rt->gc.nextCellUniqueId(); 509 } 510 511 namespace js { 512 513 static const struct GCParamInfo { 514 const char* name; 515 JSGCParamKey key; 516 bool writable; 517 } GCParameters[] = { 518 #define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable}, 519 FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO) 520 #undef DEFINE_PARAM_INFO 521 }; 522 523 bool GetGCParameterInfo(const char* name, JSGCParamKey* keyOut, 524 bool* writableOut) { 525 MOZ_ASSERT(keyOut); 526 MOZ_ASSERT(writableOut); 527 528 for (const GCParamInfo& info : GCParameters) { 529 if (strcmp(name, info.name) == 0) { 530 *keyOut = info.key; 531 *writableOut = info.writable; 532 return true; 533 } 534 } 535 536 return false; 537 } 538 539 namespace gc { 540 namespace MemInfo { 541 542 static bool GCBytesGetter(JSContext* cx, unsigned argc, Value* vp) { 543 CallArgs args = CallArgsFromVp(argc, vp); 544 args.rval().setNumber(double(cx->runtime()->gc.heapSize.bytes())); 545 return true; 546 } 547 548 static bool MallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) { 549 CallArgs args = CallArgsFromVp(argc, vp); 550 size_t bytes = 0; 551 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { 552 bytes += zone->mallocHeapSize.bytes(); 553 } 554 args.rval().setNumber(bytes); 555 return true; 556 } 557 558 static bool GCMaxBytesGetter(JSContext* cx, unsigned argc, Value* vp) { 559 CallArgs args = CallArgsFromVp(argc, vp); 560 args.rval().setNumber(double(cx->runtime()->gc.tunables.gcMaxBytes())); 561 return true; 562 } 563 564 static bool GCHighFreqGetter(JSContext* cx, unsigned argc, Value* vp) { 565 CallArgs args = CallArgsFromVp(argc, vp); 566 args.rval().setBoolean( 567 cx->runtime()->gc.schedulingState.inHighFrequencyGCMode()); 568 return true; 569 } 570 571 static bool GCNumberGetter(JSContext* cx, unsigned argc, Value* vp) { 572 CallArgs args = CallArgsFromVp(argc, vp); 573 args.rval().setNumber(double(cx->runtime()->gc.gcNumber())); 574 return true; 575 } 576 577 static bool MajorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) { 578 CallArgs args = CallArgsFromVp(argc, vp); 579 args.rval().setNumber(double(cx->runtime()->gc.majorGCCount())); 580 return true; 581 } 582 583 static bool MinorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) { 584 CallArgs args = CallArgsFromVp(argc, vp); 585 args.rval().setNumber(double(cx->runtime()->gc.minorGCCount())); 586 return true; 587 } 588 589 static bool GCSliceCountGetter(JSContext* cx, unsigned argc, Value* vp) { 590 CallArgs args = CallArgsFromVp(argc, vp); 591 args.rval().setNumber(double(cx->runtime()->gc.gcSliceCount())); 592 return true; 593 } 594 595 static bool GCCompartmentCount(JSContext* cx, unsigned argc, Value* vp) { 596 CallArgs args = CallArgsFromVp(argc, vp); 597 size_t count = 0; 598 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { 599 count += zone->compartments().length(); 600 } 601 602 args.rval().setNumber(double(count)); 603 return true; 604 } 605 606 static bool GCLastStartReason(JSContext* cx, unsigned argc, Value* vp) { 607 CallArgs args = CallArgsFromVp(argc, vp); 608 const char* reason = ExplainGCReason(cx->runtime()->gc.lastStartReason()); 609 RootedString str(cx, JS_NewStringCopyZ(cx, reason)); 610 if (!str) { 611 return false; 612 } 613 614 args.rval().setString(str); 615 return true; 616 } 617 618 static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) { 619 CallArgs args = CallArgsFromVp(argc, vp); 620 args.rval().setNumber(double(cx->zone()->gcHeapSize.bytes())); 621 return true; 622 } 623 624 static bool ZoneGCTriggerBytesGetter(JSContext* cx, unsigned argc, Value* vp) { 625 CallArgs args = CallArgsFromVp(argc, vp); 626 args.rval().setNumber(double(cx->zone()->gcHeapThreshold.startBytes())); 627 return true; 628 } 629 630 static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) { 631 CallArgs args = CallArgsFromVp(argc, vp); 632 bool highFrequency = 633 cx->runtime()->gc.schedulingState.inHighFrequencyGCMode(); 634 args.rval().setNumber( 635 double(cx->zone()->gcHeapThreshold.eagerAllocTrigger(highFrequency))); 636 return true; 637 } 638 639 static bool ZoneMallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) { 640 CallArgs args = CallArgsFromVp(argc, vp); 641 args.rval().setNumber(double(cx->zone()->mallocHeapSize.bytes())); 642 return true; 643 } 644 645 static bool ZoneMallocTriggerBytesGetter(JSContext* cx, unsigned argc, 646 Value* vp) { 647 CallArgs args = CallArgsFromVp(argc, vp); 648 args.rval().setNumber(double(cx->zone()->mallocHeapThreshold.startBytes())); 649 return true; 650 } 651 652 static bool ZoneGCNumberGetter(JSContext* cx, unsigned argc, Value* vp) { 653 CallArgs args = CallArgsFromVp(argc, vp); 654 args.rval().setNumber(double(cx->runtime()->gc.gcNumber())); 655 return true; 656 } 657 658 #ifdef DEBUG 659 static bool DummyGetter(JSContext* cx, unsigned argc, Value* vp) { 660 CallArgs args = CallArgsFromVp(argc, vp); 661 args.rval().setUndefined(); 662 return true; 663 } 664 #endif 665 666 } /* namespace MemInfo */ 667 668 JSObject* NewMemoryInfoObject(JSContext* cx) { 669 RootedObject obj(cx, JS_NewObject(cx, nullptr)); 670 if (!obj) { 671 return nullptr; 672 } 673 674 using namespace MemInfo; 675 struct NamedGetter { 676 const char* name; 677 JSNative getter; 678 } getters[] = {{"gcBytes", GCBytesGetter}, 679 {"gcMaxBytes", GCMaxBytesGetter}, 680 {"mallocBytes", MallocBytesGetter}, 681 {"gcIsHighFrequencyMode", GCHighFreqGetter}, 682 {"gcNumber", GCNumberGetter}, 683 {"majorGCCount", MajorGCCountGetter}, 684 {"minorGCCount", MinorGCCountGetter}, 685 {"sliceCount", GCSliceCountGetter}, 686 {"compartmentCount", GCCompartmentCount}, 687 {"lastStartReason", GCLastStartReason}}; 688 689 for (auto pair : getters) { 690 JSNative getter = pair.getter; 691 692 #ifdef DEBUG 693 if (js::SupportDifferentialTesting()) { 694 getter = DummyGetter; 695 } 696 #endif 697 698 if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr, 699 JSPROP_ENUMERATE)) { 700 return nullptr; 701 } 702 } 703 704 RootedObject zoneObj(cx, JS_NewObject(cx, nullptr)); 705 if (!zoneObj) { 706 return nullptr; 707 } 708 709 if (!JS_DefineProperty(cx, obj, "zone", zoneObj, JSPROP_ENUMERATE)) { 710 return nullptr; 711 } 712 713 struct NamedZoneGetter { 714 const char* name; 715 JSNative getter; 716 } zoneGetters[] = {{"gcBytes", ZoneGCBytesGetter}, 717 {"gcTriggerBytes", ZoneGCTriggerBytesGetter}, 718 {"gcAllocTrigger", ZoneGCAllocTriggerGetter}, 719 {"mallocBytes", ZoneMallocBytesGetter}, 720 {"mallocTriggerBytes", ZoneMallocTriggerBytesGetter}, 721 {"gcNumber", ZoneGCNumberGetter}}; 722 723 for (auto pair : zoneGetters) { 724 JSNative getter = pair.getter; 725 726 #ifdef DEBUG 727 if (js::SupportDifferentialTesting()) { 728 getter = DummyGetter; 729 } 730 #endif 731 732 if (!JS_DefineProperty(cx, zoneObj, pair.name, getter, nullptr, 733 JSPROP_ENUMERATE)) { 734 return nullptr; 735 } 736 } 737 738 return obj; 739 } 740 741 const char* StateName(State state) { 742 switch (state) { 743 #define MAKE_CASE(name) \ 744 case State::name: \ 745 return #name; 746 GCSTATES(MAKE_CASE) 747 #undef MAKE_CASE 748 } 749 MOZ_CRASH("Invalid gc::State enum value"); 750 } 751 752 const char* StateName(JS::Zone::GCState state) { 753 switch (state) { 754 case JS::Zone::NoGC: 755 return "NoGC"; 756 case JS::Zone::Prepare: 757 return "Prepare"; 758 case JS::Zone::MarkBlackOnly: 759 return "MarkBlackOnly"; 760 case JS::Zone::MarkBlackAndGray: 761 return "MarkBlackAndGray"; 762 case JS::Zone::Sweep: 763 return "Sweep"; 764 case JS::Zone::Finished: 765 return "Finished"; 766 case JS::Zone::Compact: 767 return "Compact"; 768 case JS::Zone::VerifyPreBarriers: 769 return "VerifyPreBarriers"; 770 case JS::Zone::Limit: 771 break; 772 } 773 MOZ_CRASH("Invalid Zone::GCState enum value"); 774 } 775 776 const char* CellColorName(CellColor color) { 777 switch (color) { 778 case CellColor::White: 779 return "white"; 780 case CellColor::Black: 781 return "black"; 782 case CellColor::Gray: 783 return "gray"; 784 default: 785 MOZ_CRASH("Unexpected cell color"); 786 } 787 } 788 789 } /* namespace gc */ 790 } /* namespace js */ 791 792 JS_PUBLIC_API JS::GCContext* js::gc::GetGCContext(JSContext* cx) { 793 CHECK_THREAD(cx); 794 return cx->gcContext(); 795 } 796 797 JS_PUBLIC_API void js::gc::SetPerformanceHint(JSContext* cx, 798 PerformanceHint hint) { 799 CHECK_THREAD(cx); 800 MOZ_ASSERT(!JS::RuntimeHeapIsCollecting()); 801 802 cx->runtime()->gc.setPerformanceHint(hint); 803 } 804 805 AutoSelectGCHeap::AutoSelectGCHeap(JSContext* cx, 806 size_t allowedNurseryCollections) 807 : cx_(cx), allowedNurseryCollections_(allowedNurseryCollections) { 808 if (!JS::AddGCNurseryCollectionCallback(cx, &NurseryCollectionCallback, 809 this)) { 810 cx_ = nullptr; 811 } 812 } 813 814 AutoSelectGCHeap::~AutoSelectGCHeap() { 815 if (cx_) { 816 JS::RemoveGCNurseryCollectionCallback(cx_, &NurseryCollectionCallback, 817 this); 818 } 819 } 820 821 /* static */ 822 void AutoSelectGCHeap::NurseryCollectionCallback(JSContext* cx, 823 JS::GCNurseryProgress progress, 824 JS::GCReason reason, 825 void* data) { 826 if (progress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END) { 827 static_cast<AutoSelectGCHeap*>(data)->onNurseryCollectionEnd(); 828 } 829 } 830 831 void AutoSelectGCHeap::onNurseryCollectionEnd() { 832 if (allowedNurseryCollections_ != 0) { 833 allowedNurseryCollections_--; 834 return; 835 } 836 837 heap_ = gc::Heap::Tenured; 838 } 839 840 JS_PUBLIC_API void js::gc::LockStoreBuffer(JSRuntime* runtime) { 841 MOZ_ASSERT(runtime); 842 runtime->gc.lockStoreBuffer(); 843 } 844 845 JS_PUBLIC_API void js::gc::UnlockStoreBuffer(JSRuntime* runtime) { 846 MOZ_ASSERT(runtime); 847 runtime->gc.unlockStoreBuffer(); 848 } 849 850 #ifdef JS_GC_ZEAL 851 JS_PUBLIC_API void JS::GetGCZealBits(JSContext* cx, uint32_t* zealBits, 852 uint32_t* frequency, 853 uint32_t* nextScheduled) { 854 cx->runtime()->gc.getZealBits(zealBits, frequency, nextScheduled); 855 } 856 857 JS_PUBLIC_API void JS::SetGCZeal(JSContext* cx, uint8_t zeal, 858 uint32_t frequency) { 859 cx->runtime()->gc.setZeal(zeal, frequency); 860 } 861 862 JS_PUBLIC_API void JS::UnsetGCZeal(JSContext* cx, uint8_t zeal) { 863 cx->runtime()->gc.unsetZeal(zeal); 864 } 865 866 JS_PUBLIC_API void JS::ScheduleGC(JSContext* cx, uint32_t count) { 867 cx->runtime()->gc.setNextScheduled(count); 868 } 869 #endif