Bug 1503750 - Add preference to allow crashing on repaint failures, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 31 Oct 2018 14:57:01 -1000
changeset 445257 584368614d2ccb8c4b4af9d088f1f647490f97f9
parent 445256 4319d0b6443bc3c6c2bff061ac01a20060841897
child 445258 c4b05c2c7eed6b8a2710258dbf004b10d9c2ba90
push id35014
push userdvarga@mozilla.com
push dateFri, 09 Nov 2018 10:01:40 +0000
treeherdermozilla-central@5e7636ec12c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1503750
milestone65.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 1503750 - Add preference to allow crashing on repaint failures, r=mccr8.
modules/libpref/init/all.js
toolkit/recordreplay/ProcessRedirect.cpp
toolkit/recordreplay/ProcessRewind.cpp
toolkit/recordreplay/ipc/ChildIPC.cpp
toolkit/recordreplay/ipc/ChildInternal.h
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1105,16 +1105,17 @@ pref("toolkit.dump.emit", false);
 // Enable recording/replaying executions.
 #if defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
 pref("devtools.recordreplay.enabled", true);
 pref("devtools.recordreplay.enableRewinding", true);
 #endif
 
 pref("devtools.recordreplay.mvp.enabled", false);
 pref("devtools.recordreplay.timeline.enabled", false);
+pref("devtools.recordreplay.allowRepaintFailures", true);
 
 // view source
 pref("view_source.syntax_highlight", true);
 pref("view_source.wrap_long_lines", false);
 pref("view_source.editor.path", "");
 // allows to add further arguments to the editor; use the %LINE% placeholder
 // for jumping to a specific line (e.g. "/line:%LINE%" or "--goto %LINE%")
 pref("view_source.editor.args", "");
--- a/toolkit/recordreplay/ProcessRedirect.cpp
+++ b/toolkit/recordreplay/ProcessRedirect.cpp
@@ -3,16 +3,17 @@
 /* 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 "ProcessRedirect.h"
 
 #include "InfallibleVector.h"
 #include "MiddlemanCall.h"
+#include "ipc/ChildInternal.h"
 #include "ipc/ParentInternal.h"
 #include "mozilla/Sprintf.h"
 
 #include <dlfcn.h>
 #include <string.h>
 
 namespace {
 
@@ -95,19 +96,21 @@ RecordReplayInterceptCall(int aCallId, C
     // If the redirection has a middleman call hook, try to perform the call in
     // the middleman instead.
     if (redirection.mMiddlemanCall) {
       if (SendCallToMiddleman(aCallId, aArguments, /* aPopulateOutput = */ true)) {
         return 0;
       }
     }
 
-    if (parent::InRepaintStressMode()) {
-      // We're about to crash, so print out the name of the call that failed.
-      Print("Could not perform middleman call: %s\n", redirection.mName);
+    if (child::CurrentRepaintCannotFail()) {
+      // EnsureNotDivergedFromRecording is going to force us to crash, so fail
+      // earlier with a more helpful error message.
+      child::ReportFatalError(Nothing(), "Could not perform middleman call: %s\n",
+                              redirection.mName);
     }
 
     // Calling any redirection which performs the standard steps will cause
     // debugger operations that have diverged from the recording to fail.
     EnsureNotDivergedFromRecording();
     Unreachable();
   }
 
--- a/toolkit/recordreplay/ProcessRewind.cpp
+++ b/toolkit/recordreplay/ProcessRewind.cpp
@@ -232,20 +232,20 @@ void
 EnsureNotDivergedFromRecording()
 {
   // If we have diverged from the recording and encounter an operation we can't
   // handle, rewind to the last checkpoint.
   AssertEventsAreNotPassedThrough();
   if (HasDivergedFromRecording()) {
     MOZ_RELEASE_ASSERT(gUnhandledDivergeAllowed);
 
-    // Crash instead of rewinding in the painting stress mode, for finding
-    // areas where middleman calls do not cover all painting logic.
-    if (parent::InRepaintStressMode()) {
-      MOZ_CRASH("Recording divergence in repaint stress mode");
+    // Crash instead of rewinding if a repaint is about to fail and is not
+    // allowed.
+    if (child::CurrentRepaintCannotFail()) {
+      MOZ_CRASH("Recording divergence while repainting");
     }
 
     PrintSpew("Unhandled recording divergence, restoring checkpoint...\n");
     RestoreCheckpointAndResume(gRewindInfo->mSavedCheckpoints.back().mCheckpoint);
     Unreachable();
   }
 }
 
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -470,16 +470,19 @@ static size_t gPaintWidth, gPaintHeight;
 // update and the compositor processes it before the main thread reaches
 // NotifyPaintStart. Outside of this window, the compositor can only write to
 // gDrawTargetBuffer or update gPaintWidth/gPaintHeight if this is non-zero.
 static Atomic<int32_t, SequentiallyConsistent, Behavior::DontPreserve> gNumPendingPaints;
 
 // ID of the compositor thread.
 static Atomic<size_t, SequentiallyConsistent, Behavior::DontPreserve> gCompositorThreadId;
 
+// Whether repaint failures are allowed, or if the process should crash.
+static bool gAllowRepaintFailures;
+
 already_AddRefed<gfx::DrawTarget>
 DrawTargetForRemoteDrawing(LayoutDeviceIntSize aSize)
 {
   MOZ_RELEASE_ASSERT(!NS_IsMainThread());
 
   // Keep track of the compositor thread ID.
   size_t threadId = Thread::Current()->Id();
   if (gCompositorThreadId) {
@@ -517,16 +520,27 @@ DrawTargetForRemoteDrawing(LayoutDeviceI
   return drawTarget.forget();
 }
 
 void
 NotifyPaintStart()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
+  // Initialize state on the first paint.
+  static bool gPainted;
+  if (!gPainted) {
+    gPainted = true;
+
+    // Repaint failures are not allowed in the repaint stress mode.
+    gAllowRepaintFailures =
+      Preferences::GetBool("devtools.recordreplay.allowRepaintFailures") &&
+      !parent::InRepaintStressMode();
+  }
+
   // A new paint cannot be triggered until the last one finishes and has been
   // sent to the middleman.
   MOZ_RELEASE_ASSERT(HasDivergedFromRecording() || !gHasActivePaint);
 
   gNumPendingPaints++;
   gHasActivePaint = true;
 
   CreateCheckpoint();
@@ -566,16 +580,19 @@ NotifyPaintComplete()
 
   // Notify the middleman about the completed paint from the main thread.
   NS_DispatchToMainThread(NewRunnableFunction("PaintFromMainThread", PaintFromMainThread));
 }
 
 // Whether we have repainted since diverging from the recording.
 static bool gDidRepaint;
 
+// Whether we are currently repainting.
+static bool gRepainting;
+
 void
 Repaint(size_t* aWidth, size_t* aHeight)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(HasDivergedFromRecording());
 
   // Don't try to repaint if the first normal paint hasn't occurred yet.
   if (!gCompositorThreadId) {
@@ -583,16 +600,17 @@ Repaint(size_t* aWidth, size_t* aHeight)
     *aHeight = 0;
     return;
   }
 
   // Ignore the request to repaint if we already triggered a repaint, in which
   // case the last graphics we sent will still be correct.
   if (!gDidRepaint) {
     gDidRepaint = true;
+    gRepainting = true;
 
     // Allow other threads to diverge from the recording so the compositor can
     // perform any paint we are about to trigger, or finish any in flight paint
     // that existed at the point we are paused at.
     for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
       Thread::GetById(i)->SetShouldDivergeFromRecording();
     }
     Thread::ResumeIdleThreads();
@@ -606,28 +624,35 @@ Repaint(size_t* aWidth, size_t* aHeight)
     {
       MonitorAutoLock lock(*gMonitor);
       while (gNumPendingPaints) {
         gMonitor->Wait();
       }
     }
 
     Thread::WaitForIdleThreads();
+    gRepainting = false;
   }
 
   if (gDrawTargetBuffer) {
     memcpy(gGraphicsShmem, gDrawTargetBuffer, gDrawTargetBufferSize);
     *aWidth = gPaintWidth;
     *aHeight = gPaintHeight;
   } else {
     *aWidth = 0;
     *aHeight = 0;
   }
 }
 
+bool
+CurrentRepaintCannotFail()
+{
+  return gRepainting && !gAllowRepaintFailures;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Checkpoint Messages
 ///////////////////////////////////////////////////////////////////////////////
 
 // The time when the last HitCheckpoint message was sent.
 static double gLastCheckpointTime;
 
 // When recording and we are idle, the time when we became idle.
--- a/toolkit/recordreplay/ipc/ChildInternal.h
+++ b/toolkit/recordreplay/ipc/ChildInternal.h
@@ -110,29 +110,30 @@ void ReportFatalError(const Maybe<Minidu
 extern Monitor* gMonitor;
 
 // Notify the middleman that the recording was flushed.
 void NotifyFlushedRecording();
 
 // Notify the middleman about an AlwaysMarkMajorCheckpoints directive.
 void NotifyAlwaysMarkMajorCheckpoints();
 
-// Report a fatal error to the middleman process.
-void ReportFatalError(const char* aFormat, ...);
-
 // Mark a time span when the main thread is idle.
 void BeginIdleTime();
 void EndIdleTime();
 
 // Whether the middleman runs developer tools server code.
 bool DebuggerRunsInMiddleman();
 
 // Send messages operating on middleman calls.
 void SendMiddlemanCallRequest(const char* aInputData, size_t aInputSize,
                               InfallibleVector<char>* aOutputData);
 void SendResetMiddlemanCalls();
 
+// Return whether a repaint is in progress and is not allowed to trigger an
+// unhandled recording divergence per preferences.
+bool CurrentRepaintCannotFail();
+
 } // namespace child
 
 } // namespace recordreplay
 } // namespace mozilla
 
 #endif // mozilla_recordreplay_ChildInternal_h