diff --git a/dom/base/nsWrapperCache.cpp b/dom/base/nsWrapperCache.cpp index 8117d225cbd0..8952ac699b47 100644 --- a/dom/base/nsWrapperCache.cpp +++ b/dom/base/nsWrapperCache.cpp @@ -39,9 +39,10 @@ void nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper) { CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this); } - if (mozilla::recordreplay::IsReplaying()) { - mozilla::recordreplay::SetWeakPointerJSRoot(this, aWrapper); - } + // Never collect the wrapper object while recording or replaying, to avoid + // non-deterministic behaviors if the cache is emptied and then refilled at + // a different point when replaying. + recordreplay::HoldJSObject(aWrapper); } void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) { @@ -98,12 +99,6 @@ static void DebugWrapperTraceCallback(JS::GCCellPtr aPtr, const char* aName, void nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer) { - // Skip checking if we are recording or replaying, as calling - // GetWrapperPreserveColor() can cause the cache's wrapper to be cleared. - if (recordreplay::IsRecordingOrReplaying()) { - return; - } - JSObject* wrapper = GetWrapperPreserveColor(); if (!wrapper) { return; diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index 4e85a97f4f4e..39539cf65251 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -82,10 +82,6 @@ static_assert(sizeof(void*) == 4, "Only support 32-bit and 64-bit"); * A number of the methods are implemented in nsWrapperCacheInlines.h because we * have to include some JS headers that don't play nicely with the rest of the * codebase. Include nsWrapperCacheInlines.h if you need to call those methods. - * - * When recording or replaying an execution, wrapper caches are instrumented so - * that they behave consistently even if the GC executes at different points - * and collects different objects. */ class nsWrapperCache { @@ -102,10 +98,6 @@ class nsWrapperCache { { } ~nsWrapperCache() { - // Clear any JS root associated with this cache while replaying. - if (mozilla::recordreplay::IsReplaying()) { - mozilla::recordreplay::SetWeakPointerJSRoot(this, nullptr); - } // Preserved wrappers should never end up getting cleared, but this can // happen during shutdown when a leaked wrapper object is finalized, causing // its wrapper to be cleared. @@ -145,23 +137,6 @@ class nsWrapperCache { * escape. */ JSObject* GetWrapperMaybeDead() const { - // Keep track of accesses on the cache when recording or replaying an - // execution. Accesses during a GC (when thread events are disallowed) - // fetch the underlying object without making sure the returned value - // is consistent between recording and replay. - if (mozilla::recordreplay::IsRecordingOrReplaying() && - !mozilla::recordreplay::AreThreadEventsDisallowed() && - !mozilla::recordreplay::HasDivergedFromRecording()) { - bool success = mozilla::recordreplay::RecordReplayValue(!!mWrapper); - if (mozilla::recordreplay::IsReplaying()) { - if (success) { - MOZ_RELEASE_ASSERT(mWrapper); - } else { - const_cast(this)->ClearWrapper(); - } - } - } - return mWrapper; } diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index c6c4b3980524..a34ded157ac2 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1368,18 +1368,9 @@ inline mozilla::dom::ReflectionScope GetReflectionScope( template inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) { - // Skip clearing the wrapper when replaying. This method is called during - // finalization of |obj|, and when replaying a strong reference is kept on - // the contents of the cache: since |obj| is being finalized, the cache - // cannot point to |obj|, and clearing here won't do anything. - // Additionally, the reference held on the cache may have already been - // released, if we are finalizing later than we did while recording, and the - // cache may have already been deleted. - if (!recordreplay::IsReplaying()) { - MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj || - (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead())); - cache->ClearWrapper(obj); - } + MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj || + (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead())); + cache->ClearWrapper(obj); } template @@ -1387,13 +1378,9 @@ inline void ClearWrapper(T* p, void*, JSObject* obj) { // QueryInterface to nsWrapperCache can't GC, we hope. JS::AutoSuppressGCAnalysis nogc; - // Skip clearing the wrapper when replaying, for the same reason as in the - // overload above: |p| may have been deleted and we cannot QI it. - if (!recordreplay::IsReplaying()) { - nsWrapperCache* cache; - CallQueryInterface(p, &cache); - ClearWrapper(p, cache, obj); - } + nsWrapperCache* cache; + CallQueryInterface(p, &cache); + ClearWrapper(p, cache, obj); } template @@ -2562,11 +2549,6 @@ bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache) { // object types. inline size_t BindingJSObjectMallocBytes(void* aNativePtr) { return 0; } -// Register a thing which DeferredFinalize might be called on during GC -// finalization. See DeferredFinalize.h -template -static void RecordReplayRegisterDeferredFinalize(T* aObject); - // The BindingJSObjectCreator class is supposed to be used by a caller that // wants to create and initialise a binding JSObject. After initialisation has // been successfully completed it should call ForgetObject(). @@ -2632,7 +2614,10 @@ class MOZ_STACK_CLASS BindingJSObjectCreator { void InitializationSucceeded() { T* pointer; mNative.forget(&pointer); - RecordReplayRegisterDeferredFinalize(pointer); + + // Never collect binding objects while recording or replaying, to avoid + // non-deterministically releasing references during finalization. + recordreplay::HoldJSObject(mReflector); mReflector = nullptr; } @@ -2727,12 +2712,6 @@ struct DeferredFinalizer { DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize, aObject); } - - static void RecordReplayRegisterDeferredFinalize(T* aObject) { - typedef DeferredFinalizerImpl Impl; - RecordReplayRegisterDeferredFinalizeThing( - Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize, aObject); - } }; template @@ -2740,10 +2719,6 @@ struct DeferredFinalizer { static void AddForDeferredFinalization(T* aObject) { DeferredFinalize(reinterpret_cast(aObject)); } - - static void RecordReplayRegisterDeferredFinalize(T* aObject) { - RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, aObject); - } }; template @@ -2751,11 +2726,6 @@ static void AddForDeferredFinalization(T* aObject) { DeferredFinalizer::AddForDeferredFinalization(aObject); } -template -static void RecordReplayRegisterDeferredFinalize(T* aObject) { - DeferredFinalizer::RecordReplayRegisterDeferredFinalize(aObject); -} - // This returns T's CC participant if it participates in CC and does not inherit // from nsISupports. Otherwise, it returns null. QI should be used to get the // participant if T inherits from nsISupports. @@ -2865,7 +2835,6 @@ bool CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, NS_ADDREF(aNative); aCache->SetWrapper(aGlobal); - RecordReplayRegisterDeferredFinalize(aNative); dom::AllocateProtoAndIfaceCache( aGlobal, CreateGlobalOptions::ProtoAndIfaceCacheKind); diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index 6b638343837c..a6dcebe29282 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -863,9 +863,12 @@ nsresult nsXBLBinding::DoInitJSClass(JSContext* cx, JS::Handle obj, nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo(); ::JS_SetPrivate(proto, docInfo); NS_ADDREF(docInfo); - RecordReplayRegisterDeferredFinalize(docInfo); JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding)); + // Don't collect the proto while recording/replaying, to avoid + // non-deterministically releasing the docInfo reference. + recordreplay::HoldJSObject(proto); + // Next, enter the realm of the property holder, wrap the proto, and // stick it on. JSAutoRealm ar3(cx, holder); diff --git a/js/xpconnect/src/SandboxPrivate.h b/js/xpconnect/src/SandboxPrivate.h index a1d7d5558424..418878155ef1 100644 --- a/js/xpconnect/src/SandboxPrivate.h +++ b/js/xpconnect/src/SandboxPrivate.h @@ -33,8 +33,11 @@ class SandboxPrivate : public nsIGlobalObject, // The type used to cast to void needs to match the one in GetPrivate. nsIScriptObjectPrincipal* sop = static_cast(sbp.forget().take()); - mozilla::RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, sop); JS_SetPrivate(global, sop); + + // Never collect the global while recording or replaying, so that the + // principal reference is not released at a non-deterministic point. + mozilla::recordreplay::HoldJSObject(global); } static SandboxPrivate* GetPrivate(JSObject* obj) { diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index bc00959e6b0f..30f4fbcec3b3 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -486,8 +486,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed&& aIdentity, MOZ_ASSERT(NS_IsMainThread()); mIdentity = aIdentity; - RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity); - mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); MOZ_ASSERT(mMaybeProto, "bad ctor param"); @@ -503,8 +501,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed&& aIdentity, MOZ_ASSERT(NS_IsMainThread()); mIdentity = aIdentity; - RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity); - mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); MOZ_ASSERT(aScope, "bad ctor param"); @@ -526,13 +522,8 @@ void XPCWrappedNative::Destroy() { #endif if (mIdentity) { - // Either release mIdentity immediately or defer the release. When - // recording or replaying the release must always be deferred, so that - // DeferredFinalize matches the earlier call to - // RecordReplayRegisterDeferredFinalizeThing. XPCJSRuntime* rt = GetRuntime(); - if ((rt && rt->GetDoingFinalization()) || - recordreplay::IsRecordingOrReplaying()) { + if (rt && rt->GetDoingFinalization()) { DeferredFinalize(mIdentity.forget().take()); } else { mIdentity = nullptr; @@ -555,6 +546,10 @@ inline void XPCWrappedNative::SetFlatJSObject(JSObject* object) { mFlatJSObject = object; mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); + + // Never collect the wrapper object while recording or replaying, to avoid + // non-deterministically releasing references during finalization. + recordreplay::HoldJSObject(object); } inline void XPCWrappedNative::UnsetFlatJSObject() { @@ -762,10 +757,8 @@ void XPCWrappedNative::FlatJSObjectFinalized() { } // We also need to release any native pointers held... - // As for XPCWrappedNative::Destroy, when recording or replaying the - // release must always be deferred. RefPtr native = to->TakeNative(); - if (native && (GetRuntime() || recordreplay::IsRecordingOrReplaying())) { + if (native && GetRuntime()) { DeferredFinalize(native.forget().take()); } @@ -1033,7 +1026,6 @@ nsresult XPCWrappedNative::InitTearOff(JSContext* cx, aTearOff->SetInterface(aInterface); aTearOff->SetNative(qiResult); - RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, qiResult); if (needJSObject && !InitTearOffJSObject(cx, aTearOff)) { return NS_ERROR_OUT_OF_MEMORY; diff --git a/js/xpconnect/src/XPCWrappedNativeProto.cpp b/js/xpconnect/src/XPCWrappedNativeProto.cpp index 4e283ffadbcb..66c21b4f7f40 100644 --- a/js/xpconnect/src/XPCWrappedNativeProto.cpp +++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp @@ -28,8 +28,6 @@ XPCWrappedNativeProto::XPCWrappedNativeProto( #ifdef DEBUG gDEBUG_LiveProtoCount++; #endif - - RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mClassInfo); } XPCWrappedNativeProto::~XPCWrappedNativeProto() { @@ -57,6 +55,10 @@ bool XPCWrappedNativeProto::Init(JSContext* cx, nsIXPCScriptable* scriptable) { bool success = !!mJSProtoObject; if (success) { JS_SetPrivate(mJSProtoObject, this); + + // Never collect the proto object while recording or replaying, to avoid + // non-deterministically releasing references during finalization. + recordreplay::HoldJSObject(mJSProtoObject); } return success; diff --git a/mfbt/RecordReplay.cpp b/mfbt/RecordReplay.cpp index 3e272d5cf241..c082feed25ad 100644 --- a/mfbt/RecordReplay.cpp +++ b/mfbt/RecordReplay.cpp @@ -62,16 +62,8 @@ namespace recordreplay { (const PLDHashTableOps* aFirstOps, \ const PLDHashTableOps* aSecondOps), \ (aFirstOps, aSecondOps)) \ - Macro(SetWeakPointerJSRoot, \ - (const void* aPtr, JSObject* aJSObj), (aPtr, aJSObj)) \ - Macro(RegisterTrigger, \ - (void* aObj, const std::function& aCallback), \ - (aObj, \ - aCallback)) Macro(UnregisterTrigger, (void* aObj), \ - (aObj)) Macro(ActivateTrigger, \ - (void* aObj), (aObj)) \ - Macro(ExecuteTriggers, (), ()) Macro( \ - InternalRecordReplayAssert, \ + Macro(InternalHoldJSObject, (JSObject* aJSObj), (aJSObj)) \ + Macro(InternalRecordReplayAssert, \ (const char* aFormat, va_list aArgs), \ (aFormat, \ aArgs)) Macro(InternalRecordReplayAssertBytes, \ diff --git a/mfbt/RecordReplay.h b/mfbt/RecordReplay.h index d1f64ffdedc1..e7a469c4ee87 100644 --- a/mfbt/RecordReplay.h +++ b/mfbt/RecordReplay.h @@ -187,54 +187,11 @@ static inline void DestroyPLDHashTableCallbacks(const PLDHashTableOps* aOps); static inline void MovePLDHashTableContents(const PLDHashTableOps* aFirstOps, const PLDHashTableOps* aSecondOps); -// Associate an arbitrary pointer with a JS object root while replaying. This -// is useful for replaying the behavior of weak pointers. -MFBT_API void SetWeakPointerJSRoot(const void* aPtr, JSObject* aJSObj); - -// API for ensuring that a function executes at a consistent point when -// recording or replaying. This is primarily needed for finalizers and other -// activity during a GC that can perform recorded events (because GCs can -// occur at different times and behave differently between recording and -// replay, thread events are disallowed during a GC). Triggers can be -// registered at a point where thread events are allowed, then activated at -// a point where thread events are not allowed. When recording, the trigger's -// callback will execute at the next point when ExecuteTriggers is called on -// the thread which originally registered the trigger (typically at the top of -// the thread's event loop), and when replaying the callback will execute at -// the same point, even if it was never activated. -// -// Below is an example of how this API can be used. -// -// // This structure's lifetime is managed by the GC. -// struct GarbageCollectedHolder { -// GarbageCollectedHolder() { -// RegisterTrigger(this, [=]() { this->DestroyContents(); }); -// } -// ~GarbageCollectedHolder() { -// UnregisterTrigger(this); -// } -// -// void Finalize() { -// // During finalization, thread events are disallowed. -// if (IsRecordingOrReplaying()) { -// ActivateTrigger(this); -// } else { -// DestroyContents(); -// } -// } -// -// // This is free to release resources held by the system, communicate with -// // other threads or processes, and so forth. When replaying, this may -// // be called before the GC has actually collected this object, but since -// // the GC will have already collected this object at this point in the -// // recording, this object will never be accessed again. -// void DestroyContents(); -// }; -MFBT_API void RegisterTrigger(void* aObj, - const std::function& aCallback); -MFBT_API void UnregisterTrigger(void* aObj); -MFBT_API void ActivateTrigger(void* aObj); -MFBT_API void ExecuteTriggers(); +// Prevent a JS object from ever being collected while recording or replaying. +// GC behavior is non-deterministic when recording/replaying, and preventing +// an object from being collected ensures that finalizers which might interact +// with the recording will not execute. +static inline void HoldJSObject(JSObject* aJSObj); // Some devtools operations which execute in a replaying process can cause code // to run which did not run while recording. For example, the JS debugger can @@ -419,15 +376,8 @@ MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(MovePLDHashTableContents, (aFirstOps, aSecondOps)) MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(InvalidateRecording, (const char* aWhy), (aWhy)) -MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID( - RegisterWeakPointer, - (const void* aPtr, const std::function& aCallback), - (aPtr, aCallback)) -MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(UnregisterWeakPointer, (const void* aPtr), - (aPtr)) -MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(WeakPointerAccess, - (const void* aPtr, bool aSuccess), - (aPtr, aSuccess)) +MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(HoldJSObject, (JSObject* aObject), + (aObject)) MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RecordReplayAssertBytes, (const void* aData, size_t aSize), (aData, aSize)) diff --git a/toolkit/recordreplay/ProcessRecordReplay.cpp b/toolkit/recordreplay/ProcessRecordReplay.cpp index 480a28df35cd..004c723f3d2b 100644 --- a/toolkit/recordreplay/ProcessRecordReplay.cpp +++ b/toolkit/recordreplay/ProcessRecordReplay.cpp @@ -7,6 +7,7 @@ #include "ProcessRecordReplay.h" #include "ipc/ChildInternal.h" +#include "mozilla/dom/ScriptSettings.h" #include "mozilla/Compression.h" #include "mozilla/Maybe.h" #include "mozilla/Sprintf.h" @@ -16,9 +17,7 @@ #include "MemorySnapshot.h" #include "ProcessRedirect.h" #include "ProcessRewind.h" -#include "Trigger.h" #include "ValueIndex.h" -#include "WeakPointer.h" #include "pratom.h" #include @@ -149,8 +148,6 @@ MOZ_EXPORT void RecordReplayInterface_Initialize(int aArgc, char* aArgv[]) { thread->BindToCurrent(); thread->SetPassThrough(true); - InitializeTriggers(); - InitializeWeakPointers(); InitializeMemorySnapshots(); Thread::SpawnAllThreads(); InitializeCountdownThread(); @@ -392,6 +389,14 @@ MOZ_EXPORT const char* RecordReplayInterface_InternalVirtualThingName( return name ? name : "(unknown)"; } +MOZ_EXPORT void RecordReplayInterface_InternalHoldJSObject(JSObject* aJSObj) { + if (aJSObj) { + JSContext* cx = dom::danger::GetJSContext(); + JS::PersistentRootedObject* root = new JS::PersistentRootedObject(cx); + *root = aJSObj; + } +} + } // extern "C" } // namespace recordreplay diff --git a/toolkit/recordreplay/ProcessRecordReplay.h b/toolkit/recordreplay/ProcessRecordReplay.h index 7e97d205845c..1d475148a010 100644 --- a/toolkit/recordreplay/ProcessRecordReplay.h +++ b/toolkit/recordreplay/ProcessRecordReplay.h @@ -55,16 +55,7 @@ namespace recordreplay { _Macro(CallbacksFinished) \ \ /* Restoring a data pointer used in a callback (see Callback.h). */ \ - _Macro(RestoreCallbackData) \ - \ - /* Called RegisterTrigger. */ \ - _Macro(RegisterTrigger) \ - \ - /* Executed a trigger within a call to ExecuteTriggers. */ \ - _Macro(ExecuteTrigger) \ - \ - /* Finished executing triggers within a call to ExecuteTriggers. */ \ - _Macro(ExecuteTriggersFinished) + _Macro(RestoreCallbackData) // ID of an event in a thread's event stream. Each ID in the stream is followed // by data associated with the event. diff --git a/toolkit/recordreplay/Trigger.cpp b/toolkit/recordreplay/Trigger.cpp deleted file mode 100644 index e2e873f65cd5..000000000000 --- a/toolkit/recordreplay/Trigger.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "Trigger.h" - -#include "ipc/ChildIPC.h" -#include "mozilla/Maybe.h" -#include "mozilla/StaticMutex.h" -#include "mozilla/RecordReplay.h" -#include "InfallibleVector.h" -#include "ProcessRewind.h" -#include "Thread.h" -#include "ValueIndex.h" - -namespace mozilla { -namespace recordreplay { - -// Information about each trigger. -struct TriggerInfo { - // ID of the thread which registered this trigger. - size_t mThreadId; - - // Callback to execute when the trigger is activated. - std::function mCallback; - - // Number of times this trigger has been activated. - size_t mRegisterCount; - - TriggerInfo(size_t aThreadId, const std::function& aCallback) - : mThreadId(aThreadId), mCallback(aCallback), mRegisterCount(1) {} -}; - -// All registered triggers. -static ValueIndex* gTriggers; - -typedef std::unordered_map TriggerInfoMap; -static TriggerInfoMap* gTriggerInfoMap; - -// Triggers which have been activated. This is protected by the global lock. -static StaticInfallibleVector gActivatedTriggers; - -static StaticMutexNotRecorded gTriggersMutex; - -void InitializeTriggers() { - gTriggers = new ValueIndex(); - gTriggerInfoMap = new TriggerInfoMap(); -} - -extern "C" { - -MOZ_EXPORT void RecordReplayInterface_RegisterTrigger( - void* aObj, const std::function& aCallback) { - MOZ_RELEASE_ASSERT(aObj); - MOZ_RELEASE_ASSERT(!AreThreadEventsPassedThrough()); - - Thread* thread = Thread::Current(); - if (thread->HasDivergedFromRecording()) { - return; - } - MOZ_RELEASE_ASSERT(thread->CanAccessRecording()); - - size_t id; - { - AutoOrderedAtomicAccess order(gTriggers); - StaticMutexAutoLock lock(gTriggersMutex); - - TriggerInfoMap::iterator iter = gTriggerInfoMap->find(aObj); - if (iter != gTriggerInfoMap->end()) { - id = gTriggers->GetIndex(aObj); - MOZ_RELEASE_ASSERT(iter->second.mThreadId == thread->Id()); - iter->second.mCallback = aCallback; - iter->second.mRegisterCount++; - } else { - id = gTriggers->Insert(aObj); - TriggerInfo info(thread->Id(), aCallback); - gTriggerInfoMap->insert(TriggerInfoMap::value_type(aObj, info)); - } - } - - RecordingEventSection res(thread); - MOZ_RELEASE_ASSERT(res.CanAccessEvents()); - - thread->Events().RecordOrReplayThreadEvent(ThreadEvent::RegisterTrigger); - thread->Events().CheckInput(id); -} - -MOZ_EXPORT void RecordReplayInterface_UnregisterTrigger(void* aObj) { - MOZ_ASSERT(IsRecordingOrReplaying()); - MOZ_RELEASE_ASSERT(!AreThreadEventsPassedThrough()); - - StaticMutexAutoLock lock(gTriggersMutex); - - TriggerInfoMap::iterator iter = gTriggerInfoMap->find(aObj); - MOZ_RELEASE_ASSERT(iter != gTriggerInfoMap->end()); - if (--iter->second.mRegisterCount == 0) { - gTriggerInfoMap->erase(iter); - gTriggers->Remove(aObj); - } -} - -MOZ_EXPORT void RecordReplayInterface_ActivateTrigger(void* aObj) { - if (!IsRecording()) { - return; - } - - StaticMutexAutoLock lock(gTriggersMutex); - - size_t id = gTriggers->GetIndex(aObj); - gActivatedTriggers.emplaceBack(id); -} - -static void InvokeTriggerCallback(size_t aId) { - void* obj; - std::function callback; - { - StaticMutexAutoLock lock(gTriggersMutex); - obj = const_cast(gTriggers->GetValue(aId)); - TriggerInfoMap::iterator iter = gTriggerInfoMap->find(obj); - MOZ_RELEASE_ASSERT(iter != gTriggerInfoMap->end()); - MOZ_RELEASE_ASSERT(iter->second.mThreadId == Thread::Current()->Id()); - MOZ_RELEASE_ASSERT(iter->second.mRegisterCount); - MOZ_RELEASE_ASSERT(iter->second.mCallback); - callback = iter->second.mCallback; - } - - callback(); -} - -static Maybe RemoveTriggerCallbackForThreadId(size_t aThreadId) { - StaticMutexAutoLock lock(gTriggersMutex); - for (size_t i = 0; i < gActivatedTriggers.length(); i++) { - size_t id = gActivatedTriggers[i]; - void* obj = const_cast(gTriggers->GetValue(id)); - TriggerInfoMap::iterator iter = gTriggerInfoMap->find(obj); - MOZ_RELEASE_ASSERT(iter != gTriggerInfoMap->end()); - if (iter->second.mThreadId == aThreadId) { - gActivatedTriggers.erase(&gActivatedTriggers[i]); - return Some(id); - } - } - return Nothing(); -} - -MOZ_EXPORT void RecordReplayInterface_ExecuteTriggers() { - Thread* thread = Thread::Current(); - RecordingEventSection res(thread); - if (!res.CanAccessEvents()) { - return; - } - - if (IsRecording()) { - // Invoke the callbacks for any triggers waiting for execution, including - // any whose callbacks are triggered by earlier callback invocations. - while (true) { - Maybe id = RemoveTriggerCallbackForThreadId(thread->Id()); - if (id.isNothing()) { - break; - } - - thread->Events().WriteScalar((size_t)ThreadEvent::ExecuteTrigger); - thread->Events().WriteScalar(id.ref()); - InvokeTriggerCallback(id.ref()); - } - thread->Events().WriteScalar((size_t)ThreadEvent::ExecuteTriggersFinished); - } else { - // Execute the same callbacks which were executed at this point while - // recording. - while (true) { - ThreadEvent ev = (ThreadEvent)thread->Events().ReadScalar(); - if (ev != ThreadEvent::ExecuteTrigger) { - if (ev != ThreadEvent::ExecuteTriggersFinished) { - child::ReportFatalError(Nothing(), "ExecuteTrigger Mismatch"); - Unreachable(); - } - break; - } - size_t id = thread->Events().ReadScalar(); - InvokeTriggerCallback(id); - } - } -} - -} // extern "C" - -} // namespace recordreplay -} // namespace mozilla diff --git a/toolkit/recordreplay/Trigger.h b/toolkit/recordreplay/Trigger.h deleted file mode 100644 index 68015221adaf..000000000000 --- a/toolkit/recordreplay/Trigger.h +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_recordreplay_Trigger_h -#define mozilla_recordreplay_Trigger_h - -namespace mozilla { -namespace recordreplay { - -// See RecordReplay.h for a description of the record/replay trigger API. - -// Initialize trigger state at the beginning of recording or replaying. -void InitializeTriggers(); - -} // namespace recordreplay -} // namespace mozilla - -#endif // mozilla_recordreplay_Trigger_h diff --git a/toolkit/recordreplay/WeakPointer.cpp b/toolkit/recordreplay/WeakPointer.cpp deleted file mode 100644 index 7eeb4c49b929..000000000000 --- a/toolkit/recordreplay/WeakPointer.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "WeakPointer.h" - -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/StaticMutex.h" -#include "jsapi.h" - -#include - -namespace mozilla { -namespace recordreplay { - -typedef std::unordered_map> - WeakPointerRootMap; -static WeakPointerRootMap* gWeakPointerRootMap; - -static StaticMutexNotRecorded gWeakPointerMutex; - -static UniquePtr NewRoot(JSObject* aJSObj) { - MOZ_RELEASE_ASSERT(aJSObj); - JSContext* cx = dom::danger::GetJSContext(); - UniquePtr root = - MakeUnique(cx); - *root = aJSObj; - return root; -} - -extern "C" { - -MOZ_EXPORT void RecordReplayInterface_SetWeakPointerJSRoot(const void* aPtr, - JSObject* aJSObj) { - MOZ_RELEASE_ASSERT(IsReplaying()); - - StaticMutexAutoLock lock(gWeakPointerMutex); - - auto iter = gWeakPointerRootMap->find(aPtr); - if (iter != gWeakPointerRootMap->end()) { - if (aJSObj) { - *iter->second = aJSObj; - } else { - gWeakPointerRootMap->erase(aPtr); - } - } else if (aJSObj) { - gWeakPointerRootMap->insert( - WeakPointerRootMap::value_type(aPtr, NewRoot(aJSObj))); - } -} - -} // extern "C" - -void InitializeWeakPointers() { - gWeakPointerRootMap = new WeakPointerRootMap(); -} - -} // namespace recordreplay -} // namespace mozilla diff --git a/toolkit/recordreplay/WeakPointer.h b/toolkit/recordreplay/WeakPointer.h deleted file mode 100644 index d28ea523e965..000000000000 --- a/toolkit/recordreplay/WeakPointer.h +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_recordreplay_WeakPointer_h -#define mozilla_recordreplay_WeakPointer_h - -namespace mozilla { -namespace recordreplay { - -// See RecordReplay.h for a description of the record/replay weak pointer API. - -// Initialize weak pointer state. -void InitializeWeakPointers(); - -} // namespace recordreplay -} // namespace mozilla - -#endif // mozilla_recordreplay_WeakPointer_h diff --git a/toolkit/recordreplay/ipc/JSControl.cpp b/toolkit/recordreplay/ipc/JSControl.cpp index 0f04bf479fd0..58d68b2ae95d 100644 --- a/toolkit/recordreplay/ipc/JSControl.cpp +++ b/toolkit/recordreplay/ipc/JSControl.cpp @@ -6,6 +6,7 @@ #include "JSControl.h" +#include "mozilla/Base64.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" #include "js/CharacterEncoding.h" @@ -13,6 +14,7 @@ #include "js/JSON.h" #include "js/PropertySpec.h" #include "ChildInternal.h" +#include "MemorySnapshot.h" #include "ParentInternal.h" #include "nsImportModule.h" #include "rrIControl.h" diff --git a/toolkit/recordreplay/moz.build b/toolkit/recordreplay/moz.build index 249db3929130..023f7fd102bf 100644 --- a/toolkit/recordreplay/moz.build +++ b/toolkit/recordreplay/moz.build @@ -31,9 +31,7 @@ if CONFIG['OS_ARCH'] == 'Darwin' and CONFIG['NIGHTLY_BUILD']: 'ProcessRewind.cpp', 'Thread.cpp', 'ThreadSnapshot.cpp', - 'Trigger.cpp', 'ValueIndex.cpp', - 'WeakPointer.cpp', ] SOURCES += [ # ProcessRedirect includes udis86 directly and will not compile if the diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 367392f3046c..d850836d0851 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1358,13 +1358,6 @@ void CycleCollectedJSRuntime::FinalizeDeferredThings( } } - // When recording or replaying, execute triggers that were activated recently - // by mozilla::DeferredFinalize. This will populate the deferred finalizer - // table with a consistent set of entries between the recording and replay. - if (recordreplay::IsRecordingOrReplaying()) { - recordreplay::ExecuteTriggers(); - } - if (mDeferredFinalizerTable.Count() == 0) { return; } diff --git a/xpcom/base/DeferredFinalize.cpp b/xpcom/base/DeferredFinalize.cpp index c3c19fadb94a..aea034dbf34f 100644 --- a/xpcom/base/DeferredFinalize.cpp +++ b/xpcom/base/DeferredFinalize.cpp @@ -12,48 +12,12 @@ void mozilla::DeferredFinalize(nsISupports* aSupports) { CycleCollectedJSRuntime* rt = CycleCollectedJSRuntime::Get(); MOZ_ASSERT(rt, "Should have a CycleCollectedJSRuntime by now"); - if (mozilla::recordreplay::IsRecordingOrReplaying()) { - // RecordReplayRegisterDeferredFinalizeThing should have been called when - // the reference on this object was added earlier. Cause the reference to - // be released soon, at a consistent point in the recording and replay. - mozilla::recordreplay::ActivateTrigger(aSupports); - } else { - rt->DeferredFinalize(aSupports); - } + rt->DeferredFinalize(aSupports); } void mozilla::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc, void* aThing) { CycleCollectedJSRuntime* rt = CycleCollectedJSRuntime::Get(); MOZ_ASSERT(rt, "Should have a CycleCollectedJSRuntime by now"); - if (mozilla::recordreplay::IsRecordingOrReplaying()) { - // As above, cause the finalization action to occur soon, at a consistent - // point in the recording and replay. - mozilla::recordreplay::ActivateTrigger(aThing); - } else { - rt->DeferredFinalize(aAppendFunc, aFunc, aThing); - } -} - -static void RecordReplayDeferredFinalize( - DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc, - void* aThing) { - mozilla::recordreplay::UnregisterTrigger(aThing); - - CycleCollectedJSRuntime* rt = CycleCollectedJSRuntime::Get(); - if (aAppendFunc) { - rt->DeferredFinalize(aAppendFunc, aFunc, aThing); - } else { - rt->DeferredFinalize(reinterpret_cast(aThing)); - } -} - -void mozilla::RecordReplayRegisterDeferredFinalizeThing( - DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc, - void* aThing) { - if (mozilla::recordreplay::IsRecordingOrReplaying()) { - mozilla::recordreplay::RegisterTrigger(aThing, [=]() { - RecordReplayDeferredFinalize(aAppendFunc, aFunc, aThing); - }); - } + rt->DeferredFinalize(aAppendFunc, aFunc, aThing); } diff --git a/xpcom/base/DeferredFinalize.h b/xpcom/base/DeferredFinalize.h index cb3a28de3191..08e5e30ce9c3 100644 --- a/xpcom/base/DeferredFinalize.h +++ b/xpcom/base/DeferredFinalize.h @@ -27,20 +27,6 @@ void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc, void DeferredFinalize(nsISupports* aSupports); -// When recording or replaying, deferred finalizers are forced to run at the -// same point during replay that they ran at while recording, even if there is -// a JSObject associated with the reference which has not been collected yet -// (since at this point the JSObject has been collected during the recording, -// that JSObject will never be used again and its reference can be released). -// -// This requires that RecordReplayRegisterDeferredFinalizeThing() be called for -// every thing which DeferredFinalize() will be called for at a later time. -// Calls to these functions must be 1:1. When not recording or replaying, this -// function is a no-op. -void RecordReplayRegisterDeferredFinalizeThing( - DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc, - void* aThing); - } // namespace mozilla #endif // mozilla_DeferredFinalize_h