Bug 1470795 Part 2 - Record/replay API changes for replay debugger, r=froydnj.
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 22 Jul 2018 11:56:47 +0000
changeset 427739 2500fc654564002eea1ba8f974e6f560a55e7835
parent 427738 dc52c82903fcb7978a887893fd83d92363cd6d9d
child 427740 669eb891fa02815229756050399c754d0c2a611e
push id34314
push usercsabou@mozilla.com
push dateMon, 23 Jul 2018 09:31:12 +0000
treeherdermozilla-central@143984185dce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1470795
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1470795 Part 2 - Record/replay API changes for replay debugger, r=froydnj.
mfbt/RecordReplay.cpp
mfbt/RecordReplay.h
toolkit/recordreplay/ProcessRecordReplay.h
toolkit/recordreplay/ProcessRewind.h
toolkit/recordreplay/ipc/ParentIPC.h
toolkit/recordreplay/ipc/ParentInternal.h
--- a/mfbt/RecordReplay.cpp
+++ b/mfbt/RecordReplay.cpp
@@ -28,67 +28,64 @@ namespace recordreplay {
   Macro(InternalAreThreadEventsPassedThrough, bool, (), ())     \
   Macro(InternalAreThreadEventsDisallowed, bool, (), ())        \
   Macro(InternalRecordReplayValue, size_t, (size_t aValue), (aValue)) \
   Macro(InternalHasDivergedFromRecording, bool, (), ())         \
   Macro(InternalGeneratePLDHashTableCallbacks, const PLDHashTableOps*, \
         (const PLDHashTableOps* aOps), (aOps))                  \
   Macro(InternalUnwrapPLDHashTableCallbacks, const PLDHashTableOps*, \
         (const PLDHashTableOps* aOps), (aOps))                  \
-  Macro(AllocateMemory, void*, (size_t aSize, AllocatedMemoryKind aKind), (aSize, aKind)) \
   Macro(InternalThingIndex, size_t, (void* aThing), (aThing))   \
   Macro(InternalVirtualThingName, const char*, (void* aThing), (aThing)) \
-  Macro(NewCheckpoint, bool, (bool aTemporary), (aTemporary))   \
-  Macro(SpewEnabled, bool, (), ())
+  Macro(ExecutionProgressCounter, ProgressCounter*, (), ())     \
+  Macro(IsInternalScript, bool, (const char* aURL), (aURL))     \
+  Macro(DefineRecordReplayControlObject, bool, (JSContext* aCx, JSObject* aObj), (aCx, aObj))
 
 #define FOR_EACH_INTERFACE_VOID(Macro)                          \
   Macro(InternalBeginOrderedAtomicAccess, (), ())               \
   Macro(InternalEndOrderedAtomicAccess, (), ())                 \
   Macro(InternalBeginPassThroughThreadEvents, (), ())           \
   Macro(InternalEndPassThroughThreadEvents, (), ())             \
   Macro(InternalBeginDisallowThreadEvents, (), ())              \
   Macro(InternalEndDisallowThreadEvents, (), ())                \
   Macro(InternalBeginCaptureEventStacks, (), ())                \
   Macro(InternalEndCaptureEventStacks, (), ())                  \
   Macro(InternalRecordReplayBytes,                              \
         (void* aData, size_t aSize), (aData, aSize))            \
-  Macro(DisallowUnhandledDivergeFromRecording, (), ())          \
   Macro(NotifyUnrecordedWait,                                   \
         (const std::function<void()>& aCallback), (aCallback))  \
   Macro(MaybeWaitForCheckpointSave, (), ())                     \
   Macro(InternalInvalidateRecording, (const char* aWhy), (aWhy)) \
   Macro(InternalDestroyPLDHashTableCallbacks,                   \
         (const PLDHashTableOps* aOps), (aOps))                  \
   Macro(InternalMovePLDHashTableContents,                       \
         (const PLDHashTableOps* aFirstOps, const PLDHashTableOps* aSecondOps), \
         (aFirstOps, aSecondOps))                                \
-  Macro(SetCheckpointHooks,                                     \
-        (BeforeCheckpointHook aBefore, AfterCheckpointHook aAfter), \
-        (aBefore, aAfter))                                      \
-  Macro(ResumeExecution, (), ())                                \
-  Macro(RestoreCheckpointAndResume, (const CheckpointId& aId), (aId)) \
-  Macro(DivergeFromRecording, (), ())                           \
-  Macro(DeallocateMemory,                                       \
-        (void* aAddress, size_t aSize, AllocatedMemoryKind aKind), (aAddress, aSize, aKind)) \
   Macro(SetWeakPointerJSRoot,                                   \
-        (const void* aPtr, void* aJSObj), (aPtr, aJSObj))       \
+        (const void* aPtr, JSObject* aJSObj), (aPtr, aJSObj))   \
   Macro(RegisterTrigger,                                        \
         (void* aObj, const std::function<void()>& aCallback),   \
         (aObj, aCallback))                                      \
   Macro(UnregisterTrigger,                                      \
         (void* aObj), (aObj))                                   \
   Macro(ActivateTrigger, (void* aObj), (aObj))                  \
   Macro(ExecuteTriggers, (), ())                                \
   Macro(InternalRecordReplayAssert, (const char* aFormat, va_list aArgs), (aFormat, aArgs)) \
   Macro(InternalRecordReplayAssertBytes,                        \
         (const void* aData, size_t aSize), (aData, aSize))      \
   Macro(InternalRegisterThing, (void* aThing), (aThing))        \
   Macro(InternalUnregisterThing, (void* aThing), (aThing))      \
   Macro(InternalRecordReplayDirective, (long aDirective), (aDirective)) \
-  Macro(InternalPrint, (const char* aFormat, va_list aArgs), (aFormat, aArgs))
+  Macro(BeginContentParse,                                      \
+        (const void* aToken, const char* aURL, const char* aContentType), \
+        (aToken, aURL, aContentType))                           \
+  Macro(AddContentParseData,                                    \
+        (const void* aToken, const char16_t* aBuffer, size_t aLength), \
+        (aToken, aBuffer, aLength))                             \
+  Macro(EndContentParse, (const void* aToken), (aToken))
 
 #define DECLARE_SYMBOL(aName, aReturnType, aFormals, _) \
   static aReturnType (*gPtr ##aName) aFormals;
 #define DECLARE_SYMBOL_VOID(aName, aFormals, _)  DECLARE_SYMBOL(aName, void, aFormals, _)
 
 FOR_EACH_INTERFACE(DECLARE_SYMBOL)
 FOR_EACH_INTERFACE_VOID(DECLARE_SYMBOL_VOID)
 
--- a/mfbt/RecordReplay.h
+++ b/mfbt/RecordReplay.h
@@ -13,16 +13,18 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TemplateLib.h"
 #include "mozilla/Types.h"
 
 #include <functional>
 #include <stdarg.h>
 
 struct PLDHashTableOps;
+struct JSContext;
+class JSObject;
 
 namespace mozilla {
 namespace recordreplay {
 
 // Record/Replay Overview.
 //
 // Firefox content processes can be specified to record or replay their
 // behavior. Whether a process is recording or replaying is initialized at the
@@ -196,17 +198,17 @@ static inline void InvalidateRecording(c
 static inline const PLDHashTableOps* GeneratePLDHashTableCallbacks(const PLDHashTableOps* aOps);
 static inline const PLDHashTableOps* UnwrapPLDHashTableCallbacks(const PLDHashTableOps* aOps);
 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*/void* aJSObj);
+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
@@ -318,170 +320,69 @@ enum class ProcessKind {
 
 // Command line option for specifying the record/replay kind of a process.
 static const char gProcessKindOption[] = "-recordReplayKind";
 
 // Command line option for specifying the recording file to use.
 static const char gRecordingFileOption[] = "-recordReplayFile";
 
 ///////////////////////////////////////////////////////////////////////////////
-// Devtools API
+// JS interface
 ///////////////////////////////////////////////////////////////////////////////
 
-// This interface is used by devtools C++ code (e.g. the JS Debugger) running
-// in a child or middleman process.
-
-// The ID of a checkpoint in a child process. Checkpoints are either normal or
-// temporary. Normal checkpoints occur at the same point in the recording and
-// all replays, while temporary checkpoints are not used while recording and
-// may be at different points in different replays.
-struct CheckpointId
-{
-  // ID of the most recent normal checkpoint, which are numbered in sequence
-  // starting at FirstCheckpointId.
-  size_t mNormal;
-
-  // Special IDs for normal checkpoints.
-  static const size_t Invalid = 0;
-  static const size_t First = 1;
-
-  // How many temporary checkpoints have been generated since the most recent
-  // normal checkpoint, zero if this represents the normal checkpoint itself.
-  size_t mTemporary;
-
-  explicit CheckpointId(size_t aNormal = Invalid, size_t aTemporary = 0)
-    : mNormal(aNormal), mTemporary(aTemporary)
-  {}
-
-  inline bool operator==(const CheckpointId& o) const {
-    return mNormal == o.mNormal && mTemporary == o.mTemporary;
-  }
-
-  inline bool operator!=(const CheckpointId& o) const {
-    return mNormal != o.mNormal || mTemporary != o.mTemporary;
-  }
-};
+// Get the counter used to keep track of how much progress JS execution has
+// made while running on the main thread. Progress must advance whenever a JS
+// function is entered or loop entry point is reached, so that no script
+// location may be hit twice while the progress counter is the same. See
+// JSControl.h for more.
+typedef uint64_t ProgressCounter;
+MFBT_API ProgressCounter* ExecutionProgressCounter();
 
-// Signature for the hook called when running forward, immediately before
-// hitting a normal or temporary checkpoint.
-typedef void (*BeforeCheckpointHook)();
-
-// Signature for the hook called immediately after hitting a normal or
-// temporary checkpoint, either when running forward or after rewinding.
-typedef void (*AfterCheckpointHook)(const CheckpointId& aCheckpoint);
-
-// Set hooks to call when encountering checkpoints.
-MFBT_API void SetCheckpointHooks(BeforeCheckpointHook aBeforeCheckpoint,
-                                 AfterCheckpointHook aAfterCheckpoint);
-
-// When paused at a breakpoint or at a checkpoint, unpause and proceed with
-// execution.
-MFBT_API void ResumeExecution();
-
-// When paused at a breakpoint or at a checkpoint, restore a checkpoint that
-// was saved earlier and resume execution.
-MFBT_API void RestoreCheckpointAndResume(const CheckpointId& aCheckpoint);
+static inline void
+AdvanceExecutionProgressCounter()
+{
+  ++*ExecutionProgressCounter();
+}
 
-// Allow execution after this point to diverge from the recording. Execution
-// will remain diverged until an earlier checkpoint is restored.
-//
-// If an unhandled divergence occurs (see the 'Recording Divergence' comment
-// in ProcessRewind.h) then the process rewinds to the most recent saved
-// checkpoint.
-MFBT_API void DivergeFromRecording();
+// Return whether a script is internal to the record/replay infrastructure,
+// may run non-deterministically between recording and replaying, and whose
+// execution must not update the progress counter.
+MFBT_API bool IsInternalScript(const char* aURL);
 
-// After a call to DivergeFromRecording(), this may be called to prevent future
-// unhandled divergence from causing earlier checkpoints to be restored
-// (the process will immediately crash instead). This state lasts until a new
-// call to DivergeFromRecording, or to an explicit restore of an earlier
-// checkpoint.
-MFBT_API void DisallowUnhandledDivergeFromRecording();
-
-// Note a checkpoint at the current execution position. This checkpoint will be
-// saved if either (a) it is temporary, or (b) the middleman has instructed
-// this process to save this normal checkpoint. This method returns true if the
-// checkpoint was just saved, and false if it was just restored.
-MFBT_API bool NewCheckpoint(bool aTemporary);
+// Define a RecordReplayControl object on the specified global object, with
+// methods specialized to the current recording/replaying or middleman process
+// kind.
+MFBT_API bool DefineRecordReplayControlObject(JSContext* aCx, JSObject* aObj);
 
-// Print information about record/replay state. Printing is independent from
-// the recording and will be printed by any recording, replaying, or middleman
-// process. Spew is only printed when enabled via the RECORD_REPLAY_SPEW
-// environment variable.
-static inline void Print(const char* aFormat, ...);
-static inline void PrintSpew(const char* aFormat, ...);
-MFBT_API bool SpewEnabled();
-
-///////////////////////////////////////////////////////////////////////////////
-// Allocation policies
-///////////////////////////////////////////////////////////////////////////////
+// Notify the infrastructure that some URL which contains JavaScript is
+// being parsed. This is used to provide the complete contents of the URL to
+// devtools code when it is inspecting the state of this process; that devtools
+// code can't simply fetch the URL itself since it may have been changed since
+// the recording was made or may no longer exist. The token for a parse may not
+// be used in other parses until after EndContentParse() is called.
+MFBT_API void BeginContentParse(const void* aToken,
+                                const char* aURL, const char* aContentType);
 
-// Type describing what kind of memory to allocate/deallocate by APIs below.
-// TrackedMemoryKind is reserved for memory that is saved and restored when
-// saving or restoring checkpoints. All other values refer to memory that is
-// untracked, and whose contents are preserved when restoring checkpoints.
-// Different values are used to distinguish different classes of memory for
-// diagnosing leaks and reporting memory usage.
-typedef size_t AllocatedMemoryKind;
-static const AllocatedMemoryKind TrackedMemoryKind = 0;
+// Add some parse data to an existing content parse.
+MFBT_API void AddContentParseData(const void* aToken,
+                                  const char16_t* aBuffer, size_t aLength);
 
-// Memory kind to use for untracked debugger memory.
-static const AllocatedMemoryKind DebuggerAllocatedMemoryKind = 1;
+// Mark a content parse as having completed.
+MFBT_API void EndContentParse(const void* aToken);
 
-// Allocate or deallocate a block of memory of a particular kind. Allocated
-// memory is initially zeroed.
-MFBT_API void* AllocateMemory(size_t aSize, AllocatedMemoryKind aKind);
-MFBT_API void DeallocateMemory(void* aAddress, size_t aSize, AllocatedMemoryKind aKind);
-
-// Allocation policy for managing memory of a particular kind.
-template <AllocatedMemoryKind Kind>
-class AllocPolicy
+// Perform an entire content parse, when the entire URL is available at once.
+static inline void
+NoteContentParse(const void* aToken,
+                 const char* aURL, const char* aContentType,
+                 const char16_t* aBuffer, size_t aLength)
 {
-public:
-  template <typename T>
-  T* maybe_pod_calloc(size_t aNumElems) {
-    if (aNumElems & tl::MulOverflowMask<sizeof(T)>::value) {
-      MOZ_CRASH();
-    }
-    // Note: AllocateMemory always returns zeroed memory.
-    return static_cast<T*>(AllocateMemory(aNumElems * sizeof(T), Kind));
-  }
-
-  template <typename T>
-  void free_(T* aPtr, size_t aSize) {
-    DeallocateMemory(aPtr, aSize * sizeof(T), Kind);
-  }
-
-  template <typename T>
-  T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
-    T* res = maybe_pod_calloc<T>(aNewSize);
-    memcpy(res, aPtr, aOldSize * sizeof(T));
-    free_<T>(aPtr, aOldSize);
-    return res;
-  }
-
-  template <typename T>
-  T* maybe_pod_malloc(size_t aNumElems) { return maybe_pod_calloc<T>(aNumElems); }
-
-  template <typename T>
-  T* pod_malloc(size_t aNumElems) { return maybe_pod_malloc<T>(aNumElems); }
-
-  template <typename T>
-  T* pod_calloc(size_t aNumElems) { return maybe_pod_calloc<T>(aNumElems); }
-
-  template <typename T>
-  T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
-    return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
-  }
-
-  void reportAllocOverflow() const {}
-
-  MOZ_MUST_USE bool checkSimulatedOOM() const {
-    return true;
-  }
-};
+  BeginContentParse(aToken, aURL, aContentType);
+  AddContentParseData(aToken, aBuffer, aLength);
+  EndContentParse(aToken);
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 // API inline function implementation
 ///////////////////////////////////////////////////////////////////////////////
 
 // Define inline wrappers on builds where recording/replaying is enabled.
 #if defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
 
@@ -566,31 +467,12 @@ RecordReplayAssert(const char* aFormat, 
   if (IsRecordingOrReplaying()) {
     va_list ap;
     va_start(ap, aFormat);
     InternalRecordReplayAssert(aFormat, ap);
     va_end(ap);
   }
 }
 
-MFBT_API void InternalPrint(const char* aFormat, va_list aArgs);
-
-#define MOZ_MakeRecordReplayPrinter(aName, aSpewing)            \
-  static inline void                                            \
-  aName(const char* aFormat, ...)                               \
-  {                                                             \
-    if ((IsRecordingOrReplaying() || IsMiddleman()) && (!aSpewing || SpewEnabled())) { \
-      va_list ap;                                               \
-      va_start(ap, aFormat);                                    \
-      InternalPrint(aFormat, ap);                               \
-      va_end(ap);                                               \
-    }                                                           \
-  }
-
-MOZ_MakeRecordReplayPrinter(Print, false)
-MOZ_MakeRecordReplayPrinter(PrintSpew, true)
-
-#undef MOZ_MakeRecordReplayPrinter
-
 } // recordreplay
 } // mozilla
 
 #endif /* mozilla_RecordReplay_h */
--- a/toolkit/recordreplay/ProcessRecordReplay.h
+++ b/toolkit/recordreplay/ProcessRecordReplay.h
@@ -189,16 +189,40 @@ VectorAddOrRemoveEntry(Vector& aVector, 
       aVector.erase(&existing);
       return;
     }
   }
   MOZ_RELEASE_ASSERT(aAdding);
   aVector.append(aEntry);
 }
 
+bool SpewEnabled();
+void InternalPrint(const char* aFormat, va_list aArgs);
+
+#define MOZ_MakeRecordReplayPrinter(aName, aSpewing)            \
+  static inline void                                            \
+  aName(const char* aFormat, ...)                               \
+  {                                                             \
+    if ((IsRecordingOrReplaying() || IsMiddleman()) && (!aSpewing || SpewEnabled())) { \
+      va_list ap;                                               \
+      va_start(ap, aFormat);                                    \
+      InternalPrint(aFormat, ap);                               \
+      va_end(ap);                                               \
+    }                                                           \
+  }
+
+// Print information about record/replay state. Printing is independent from
+// the recording and will be printed by any recording, replaying, or middleman
+// process. Spew is only printed when enabled via the RECORD_REPLAY_SPEW
+// environment variable.
+MOZ_MakeRecordReplayPrinter(Print, false)
+MOZ_MakeRecordReplayPrinter(PrintSpew, true)
+
+#undef MOZ_MakeRecordReplayPrinter
+
 ///////////////////////////////////////////////////////////////////////////////
 // Profiling
 ///////////////////////////////////////////////////////////////////////////////
 
 void InitializeCurrentTime();
 
 // Get a current timestamp, in microseconds.
 double CurrentTime();
@@ -220,50 +244,119 @@ struct AutoTimer
 
 private:
   TimerKind mKind;
   double mStart;
 };
 
 void DumpTimers();
 
-// Different kinds of untracked memory used in the system.
-namespace UntrackedMemoryKind {
-  // Note: 0 is TrackedMemoryKind, 1 is DebuggerAllocatedMemoryKind.
-  static const AllocatedMemoryKind Generic = 2;
+///////////////////////////////////////////////////////////////////////////////
+// Memory Management
+///////////////////////////////////////////////////////////////////////////////
 
-  // Memory used by untracked files.
-  static const AllocatedMemoryKind File = 3;
+// In cases where memory is tracked and should be saved/restored with
+// checkoints, malloc and other standard library functions suffice to allocate
+// memory in the record/replay system. The routines below are used for handling
+// redirections for the raw system calls underlying the standard libraries, and
+// for cases where allocated memory should be untracked: the contents are
+// ignored when saving/restoring checkpoints.
+
+// Different kinds of memory used in the system.
+enum class MemoryKind {
+  // Memory whose contents are saved/restored with checkpoints.
+  Tracked,
+
+  // All remaining memory kinds refer to untracked memory.
+
+  // Memory not fitting into one of the categories below.
+  Generic,
 
   // Memory used for thread snapshots.
-  static const AllocatedMemoryKind ThreadSnapshot = 4;
+  ThreadSnapshot,
 
   // Memory used by various parts of the memory snapshot system.
-  static const AllocatedMemoryKind TrackedRegions = 5;
-  static const AllocatedMemoryKind FreeRegions = 6;
-  static const AllocatedMemoryKind DirtyPageSet = 7;
-  static const AllocatedMemoryKind SortedDirtyPageSet = 8;
-  static const AllocatedMemoryKind PageCopy = 9;
+  TrackedRegions,
+  FreeRegions,
+  DirtyPageSet,
+  SortedDirtyPageSet,
+  PageCopy,
+
+  // Memory used for navigation state.
+  Navigation,
+
+  Count
+};
+
+// Allocate or deallocate a block of memory of a particular kind. Allocated
+// memory is initially zeroed.
+void* AllocateMemory(size_t aSize, MemoryKind aKind);
+void DeallocateMemory(void* aAddress, size_t aSize, MemoryKind aKind);
+
+// Allocation policy for managing memory of a particular kind.
+template <MemoryKind Kind>
+class AllocPolicy
+{
+public:
+  template <typename T>
+  T* maybe_pod_calloc(size_t aNumElems) {
+    if (aNumElems & tl::MulOverflowMask<sizeof(T)>::value) {
+      MOZ_CRASH();
+    }
+    // Note: AllocateMemory always returns zeroed memory.
+    return static_cast<T*>(AllocateMemory(aNumElems * sizeof(T), Kind));
+  }
 
-  static const size_t Count = 10;
-}
+  template <typename T>
+  void free_(T* aPtr, size_t aSize) {
+    DeallocateMemory(aPtr, aSize * sizeof(T), Kind);
+  }
+
+  template <typename T>
+  T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
+    T* res = maybe_pod_calloc<T>(aNewSize);
+    memcpy(res, aPtr, aOldSize * sizeof(T));
+    free_<T>(aPtr, aOldSize);
+    return res;
+  }
+
+  template <typename T>
+  T* maybe_pod_malloc(size_t aNumElems) { return maybe_pod_calloc<T>(aNumElems); }
+
+  template <typename T>
+  T* pod_malloc(size_t aNumElems) { return maybe_pod_malloc<T>(aNumElems); }
+
+  template <typename T>
+  T* pod_calloc(size_t aNumElems) { return maybe_pod_calloc<T>(aNumElems); }
+
+  template <typename T>
+  T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
+    return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
+  }
+
+  void reportAllocOverflow() const {}
+
+  MOZ_MUST_USE bool checkSimulatedOOM() const {
+    return true;
+  }
+};
 
 ///////////////////////////////////////////////////////////////////////////////
 // Redirection Bypassing
 ///////////////////////////////////////////////////////////////////////////////
 
 // The functions below bypass any redirections and give access to the system
 // even if events are not passed through in the current thread. These are
 // implemented in the various platform ProcessRedirect*.cpp files, and will
 // crash on errors which can't be handled internally.
 
 // Generic typedef for a system file handle.
 typedef size_t FileHandle;
 
-// Allocate/deallocate a block of memory.
+// Allocate/deallocate a block of memory directly from the system.
 void* DirectAllocateMemory(void* aAddress, size_t aSize);
 void DirectDeallocateMemory(void* aAddress, size_t aSize);
 
 // Give a block of memory R or RX access.
 void DirectWriteProtectMemory(void* aAddress, size_t aSize, bool aExecutable,
                               bool aIgnoreFailures = false);
 
 // Give a block of memory RW or RWX access.
--- a/toolkit/recordreplay/ProcessRewind.h
+++ b/toolkit/recordreplay/ProcessRewind.h
@@ -74,16 +74,52 @@ namespace recordreplay {
 // will trigger an unhandled divergence from the recording via
 // EnsureNotDivergedFromRecording, causing the process to rewind to the most
 // recent saved checkpoint. The debugger will recognize this rewind and play
 // back in a way that restores the state when DivergeFromRecording() was
 // called, but without performing the later operation that triggered the
 // rewind.
 ///////////////////////////////////////////////////////////////////////////////
 
+// The ID of a checkpoint in a child process. Checkpoints are either normal or
+// temporary. Normal checkpoints occur at the same point in the recording and
+// all replays, while temporary checkpoints are not used while recording and
+// may be at different points in different replays.
+struct CheckpointId
+{
+  // ID of the most recent normal checkpoint, which are numbered in sequence
+  // starting at FirstCheckpointId.
+  size_t mNormal;
+
+  // Special IDs for normal checkpoints.
+  static const size_t Invalid = 0;
+  static const size_t First = 1;
+
+  // How many temporary checkpoints have been generated since the most recent
+  // normal checkpoint, zero if this represents the normal checkpoint itself.
+  size_t mTemporary;
+
+  explicit CheckpointId(size_t aNormal = Invalid, size_t aTemporary = 0)
+    : mNormal(aNormal), mTemporary(aTemporary)
+  {}
+
+  inline bool operator==(const CheckpointId& o) const {
+    return mNormal == o.mNormal && mTemporary == o.mTemporary;
+  }
+
+  inline bool operator!=(const CheckpointId& o) const {
+    return mNormal != o.mNormal || mTemporary != o.mTemporary;
+  }
+
+  CheckpointId NextCheckpoint(bool aTemporary) const {
+    return CheckpointId(aTemporary ? mNormal : mNormal + 1,
+                        aTemporary ? mTemporary + 1 : 0);
+  }
+};
+
 // Initialize state needed for rewinding.
 void InitializeRewindState();
 
 // Set whether this process should save a particular checkpoint.
 void SetSaveCheckpoint(size_t aCheckpoint, bool aSave);
 
 // Invoke a callback on the main thread, and pause it until ResumeExecution or
 // RestoreCheckpointAndResume are called. When the main thread is not paused,
@@ -100,20 +136,49 @@ bool MainThreadShouldPause();
 void PauseMainThreadAndServiceCallbacks();
 
 // Return whether any checkpoints have been saved.
 bool HasSavedCheckpoint();
 
 // Get the ID of the most recent saved checkpoint.
 CheckpointId GetLastSavedCheckpoint();
 
+// When paused at a breakpoint or at a checkpoint, restore a checkpoint that
+// was saved earlier and resume execution.
+void RestoreCheckpointAndResume(const CheckpointId& aCheckpoint);
+
+// When paused at a breakpoint or at a checkpoint, unpause and proceed with
+// execution.
+void ResumeExecution();
+
+// Allow execution after this point to diverge from the recording. Execution
+// will remain diverged until an earlier checkpoint is restored.
+//
+// If an unhandled divergence occurs (see the 'Recording Divergence' comment
+// in ProcessRewind.h) then the process rewinds to the most recent saved
+// checkpoint.
+void DivergeFromRecording();
+
+// After a call to DivergeFromRecording(), this may be called to prevent future
+// unhandled divergence from causing earlier checkpoints to be restored
+// (the process will immediately crash instead). This state lasts until a new
+// call to DivergeFromRecording, or to an explicit restore of an earlier
+// checkpoint.
+void DisallowUnhandledDivergeFromRecording();
+
 // Make sure that execution has not diverged from the recording after a call to
 // DivergeFromRecording, by rewinding to the last saved checkpoint if so.
 void EnsureNotDivergedFromRecording();
 
 // Access the flag for whether this is the active child process.
 void SetIsActiveChild(bool aActive);
 bool IsActiveChild();
 
+// Note a checkpoint at the current execution position. This checkpoint will be
+// saved if either (a) it is temporary, or (b) the middleman has instructed
+// this process to save this normal checkpoint. This method returns true if the
+// checkpoint was just saved, and false if it was just restored.
+bool NewCheckpoint(bool aTemporary);
+
 } // namespace recordreplay
 } // namespace mozilla
 
 #endif // mozilla_recordreplay_ProcessRewind_h
--- a/toolkit/recordreplay/ipc/ParentIPC.h
+++ b/toolkit/recordreplay/ipc/ParentIPC.h
@@ -8,17 +8,16 @@
 #define mozilla_recordreplay_ParentIPC_h
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/ScopedXREEmbed.h"
 #include "mozilla/ipc/Shmem.h"
-#include "js/ReplayHooks.h"
 
 namespace mozilla {
 namespace recordreplay {
 namespace parent {
 
 // The middleman process is a content process that manages communication with
 // one or more child recording or replaying processes. It performs IPC with the
 // UI process in the normal fashion for a content process, using the normal
--- a/toolkit/recordreplay/ipc/ParentInternal.h
+++ b/toolkit/recordreplay/ipc/ParentInternal.h
@@ -46,16 +46,29 @@ bool MainThreadIsWaitingForIPDLReply();
 void InitializeForwarding();
 
 // Terminate all children and kill this process.
 void Shutdown();
 
 // Monitor used for synchronizing between the main and channel or message loop threads.
 static Monitor* gMonitor;
 
+// Allow the child process to resume execution.
+void Resume(bool aForward);
+
+// Pause the child process at the next opportunity.
+void Pause();
+
+// Send a JSON request to the child process, and synchronously wait for a
+// response.
+void SendRequest(const js::CharBuffer& aBuffer, js::CharBuffer* aResponse);
+
+// Set or clear a breakpoint in the child process.
+void SetBreakpoint(size_t aId, const js::BreakpointPosition& aPosition);
+
 ///////////////////////////////////////////////////////////////////////////////
 // Graphics
 ///////////////////////////////////////////////////////////////////////////////
 
 extern void* gGraphicsMemory;
 
 void InitializeGraphicsMemory();
 void SendGraphicsMemoryToChild();
@@ -253,17 +266,17 @@ public:
   const InfallibleVector<size_t>& MajorCheckpoints() { return mMajorCheckpoints; }
 
   bool IsPaused() { return mPaused; }
   bool IsPausedAtCheckpoint();
   bool IsPausedAtRecordingEndpoint();
 
   // Return whether this process is paused at a breakpoint whose kind matches
   // the supplied filter.
-  typedef std::function<bool(JS::replay::ExecutionPosition::Kind)> BreakpointFilter;
+  typedef std::function<bool(js::BreakpointPosition::Kind)> BreakpointFilter;
   bool IsPausedAtMatchingBreakpoint(const BreakpointFilter& aFilter);
 
   // Get the checkpoint at or earlier to the process' position. This is either
   // the last reached checkpoint or the previous one.
   size_t MostRecentCheckpoint() {
     return (GetDisposition() == BeforeLastCheckpoint) ? mLastCheckpoint - 1 : mLastCheckpoint;
   }