Bug 763070 - Give MOZ_CRASH() an optional string argument. r=waldo
authorJustin Lebar <justin.lebar@gmail.com>
Fri, 28 Jun 2013 18:38:30 -0700
changeset 136863 02d4ae55e1c3429bb95230496b21f1dabbda51e7
parent 136862 6d731a339374fec6d97787d32e18fd0fef7b42f5
child 136864 55c1f447549d5ad6245f8f78fbea89ebd5350b04
push id24898
push userphilringnalda@gmail.com
push dateSat, 29 Jun 2013 13:54:45 +0000
treeherdermozilla-central@cbb24a4a96af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs763070
milestone25.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 763070 - Give MOZ_CRASH() an optional string argument. r=waldo
mfbt/Assertions.h
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -128,92 +128,141 @@
 
 #define MOZ_STATIC_ASSERT_IF(cond, expr, reason)  MOZ_STATIC_ASSERT(!(cond) || (expr), reason)
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /*
- * MOZ_CRASH crashes the program, plain and simple, in a Breakpad-compatible
- * way, in both debug and release builds.
- *
- * MOZ_CRASH is a good solution for "handling" failure cases when you're
- * unwilling or unable to handle them more cleanly -- for OOM, for likely memory
- * corruption, and so on.  It's also a good solution if you need safe behavior
- * in release builds as well as debug builds.  But if the failure is one that
- * should be debugged and fixed, MOZ_ASSERT is generally preferable.
- */
-#if defined(_MSC_VER)
-   /*
-    * On MSVC use the __debugbreak compiler intrinsic, which produces an inline
-    * (not nested in a system function) breakpoint.  This distinctively invokes
-    * Breakpad without requiring system library symbols on all stack-processing
-    * machines, as a nested breakpoint would require.  We use TerminateProcess
-    * with the exit code aborting would generate because we don't want to invoke
-    * atexit handlers, destructors, library unload handlers, and so on when our
-    * process might be in a compromised state.  We don't use abort() because
-    * it'd cause Windows to annoyingly pop up the process error dialog multiple
-    * times.  See bug 345118 and bug 426163.
-    *
-    * (Technically these are Windows requirements, not MSVC requirements.  But
-    * practically you need MSVC for debugging, and we only ship builds created
-    * by MSVC, so doing it this way reduces complexity.)
-    */
-#  ifdef __cplusplus
-#    define MOZ_CRASH() \
-       do { \
-         __debugbreak(); \
-         *((volatile int*) NULL) = 123; \
-         ::TerminateProcess(::GetCurrentProcess(), 3); \
-       } while (0)
-#  else
-#    define MOZ_CRASH() \
-       do { \
-         __debugbreak(); \
-         *((volatile int*) NULL) = 123; \
-         TerminateProcess(GetCurrentProcess(), 3); \
-       } while (0)
-#  endif
-#else
-#  ifdef __cplusplus
-#    define MOZ_CRASH() \
-       do { \
-         *((volatile int*) NULL) = 123; \
-         ::abort(); \
-       } while (0)
-#  else
-#    define MOZ_CRASH() \
-       do { \
-         *((volatile int*) NULL) = 123; \
-         abort(); \
-       } while (0)
-#  endif
-#endif
-
-/*
  * Prints |s| as an assertion failure (using file and ln as the location of the
  * assertion) to the standard debug-output channel.
  *
- * Usually you should use MOZ_ASSERT instead of this method.  This method is
- * primarily for internal use in this header, and only secondarily for use in
- * implementing release-build assertions.
+ * Usually you should use MOZ_ASSERT or MOZ_CRASH instead of this method.  This
+ * method is primarily for internal use in this header, and only secondarily
+ * for use in implementing release-build assertions.
  */
 static MOZ_ALWAYS_INLINE void
 MOZ_ReportAssertionFailure(const char* s, const char* file, int ln)
 {
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_FATAL, "MOZ_Assert",
                       "Assertion failure: %s, at %s:%d\n", s, file, ln);
 #else
   fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
   fflush(stderr);
 #endif
 }
 
+static MOZ_ALWAYS_INLINE void
+MOZ_ReportCrash(const char* s, const char* file, int ln)
+{
+#ifdef ANDROID
+    __android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH",
+                        "Hit MOZ_CRASH(%s) at %s:%d\n", s, file, ln);
+#else
+  fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", s, file, ln);
+  fflush(stderr);
+#endif
+}
+
+/**
+ * MOZ_REALLY_CRASH is used in the implementation of MOZ_CRASH().  You should
+ * call MOZ_CRASH instead.
+ */
+#if defined(_MSC_VER)
+   /*
+    * On MSVC use the __debugbreak compiler intrinsic, which produces an inline
+    * (not nested in a system function) breakpoint.  This distinctively invokes
+    * Breakpad without requiring system library symbols on all stack-processing
+    * machines, as a nested breakpoint would require.
+    *
+    * We use TerminateProcess with the exit code aborting would generate
+    * because we don't want to invoke atexit handlers, destructors, library
+    * unload handlers, and so on when our process might be in a compromised
+    * state.
+    *
+    * We don't use abort() because it'd cause Windows to annoyingly pop up the
+    * process error dialog multiple times.  See bug 345118 and bug 426163.
+    *
+    * We follow TerminateProcess() with a call to MOZ_NoReturn() so that the
+    * compiler doesn't hassle us to provide a return statement after a
+    * MOZ_REALLY_CRASH() call.
+    *
+    * (Technically these are Windows requirements, not MSVC requirements.  But
+    * practically you need MSVC for debugging, and we only ship builds created
+    * by MSVC, so doing it this way reduces complexity.)
+    */
+
+__declspec(noreturn) __inline void MOZ_NoReturn() {}
+
+#  ifdef __cplusplus
+#    define MOZ_REALLY_CRASH() \
+       do { \
+         __debugbreak(); \
+         *((volatile int*) NULL) = 123; \
+         ::TerminateProcess(::GetCurrentProcess(), 3); \
+         ::MOZ_NoReturn(); \
+       } while (0)
+#  else
+#    define MOZ_REALLY_CRASH() \
+       do { \
+         __debugbreak(); \
+         *((volatile int*) NULL) = 123; \
+         TerminateProcess(GetCurrentProcess(), 3); \
+         MOZ_NoReturn(); \
+       } while (0)
+#  endif
+#else
+#  ifdef __cplusplus
+#    define MOZ_REALLY_CRASH() \
+       do { \
+         *((volatile int*) NULL) = 123; \
+         ::abort(); \
+       } while (0)
+#  else
+#    define MOZ_REALLY_CRASH() \
+       do { \
+         *((volatile int*) NULL) = 123; \
+         abort(); \
+       } while (0)
+#  endif
+#endif
+
+/*
+ * MOZ_CRASH([explanation-string]) crashes the program, plain and simple, in a
+ * Breakpad-compatible way, in both debug and release builds.
+ *
+ * MOZ_CRASH is a good solution for "handling" failure cases when you're
+ * unwilling or unable to handle them more cleanly -- for OOM, for likely memory
+ * corruption, and so on.  It's also a good solution if you need safe behavior
+ * in release builds as well as debug builds.  But if the failure is one that
+ * should be debugged and fixed, MOZ_ASSERT is generally preferable.
+ *
+ * The optional explanation-string, if provided, must be a string literal
+ * explaining why we're crashing.  This argument is intended for use with
+ * MOZ_CRASH() calls whose rationale is non-obvious; don't use it if it's
+ * obvious why we're crashing.
+ *
+ * If we're a DEBUG build and we crash at a MOZ_CRASH which provides an
+ * explanation-string, we print the string to stderr.  Otherwise, we don't
+ * print anything; this is because we want MOZ_CRASH to be 100% safe in release
+ * builds, and it's hard to print to stderr safely when memory might have been
+ * corrupted.
+ */
+#ifndef DEBUG
+#  define MOZ_CRASH(...) MOZ_REALLY_CRASH()
+#else
+#  define MOZ_CRASH(...) \
+     do { \
+       MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
+       MOZ_REALLY_CRASH(); \
+     } while(0)
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
 /*
  * MOZ_ASSERT(expr [, explanation-string]) asserts that |expr| must be truthy in
  * debug builds.  If it is, execution continues.  Otherwise, an error message
  * including the expression and the explanation-string (if provided) is printed,
@@ -246,25 +295,25 @@ MOZ_ReportAssertionFailure(const char* s
  * *only* during debugging, not "in the field".
  */
 #ifdef DEBUG
    /* First the single-argument form. */
 #  define MOZ_ASSERT_HELPER1(expr) \
      do { \
        if (MOZ_UNLIKELY(!(expr))) { \
          MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
-         MOZ_CRASH(); \
+         MOZ_REALLY_CRASH(); \
        } \
      } while (0)
    /* Now the two-argument form. */
 #  define MOZ_ASSERT_HELPER2(expr, explain) \
      do { \
        if (MOZ_UNLIKELY(!(expr))) { \
          MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
-         MOZ_CRASH(); \
+         MOZ_REALLY_CRASH(); \
        } \
      } while (0)
    /* And now, helper macrology up the wazoo. */
    /*
     * Count the number of arguments passed to MOZ_ASSERT, very carefully
     * tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a
     * single token in argument lists.  See these URLs for details:
     *