Skip to content

Commit

Permalink
Bug 763070 - Give MOZ_CRASH() an optional string argument. r=waldo
Browse files Browse the repository at this point in the history
  • Loading branch information
jlebar committed Jun 29, 2013
1 parent e25a7df commit d97ce09
Showing 1 changed file with 82 additions and 33 deletions.
115 changes: 82 additions & 33 deletions mfbt/Assertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,55 +133,93 @@ extern "C" {
#endif

/*
* MOZ_CRASH crashes the program, plain and simple, in a Breakpad-compatible
* way, in both debug and release builds.
* Prints |s| as an assertion failure (using file and ln as the location of the
* assertion) to the standard debug-output channel.
*
* 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.
* 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.
* 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_CRASH() \
# define MOZ_REALLY_CRASH() \
do { \
__debugbreak(); \
*((volatile int*) NULL) = 123; \
::TerminateProcess(::GetCurrentProcess(), 3); \
::MOZ_NoReturn(); \
} while (0)
# else
# define MOZ_CRASH() \
# define MOZ_REALLY_CRASH() \
do { \
__debugbreak(); \
*((volatile int*) NULL) = 123; \
TerminateProcess(GetCurrentProcess(), 3); \
MOZ_NoReturn(); \
} while (0)
# endif
#else
# ifdef __cplusplus
# define MOZ_CRASH() \
# define MOZ_REALLY_CRASH() \
do { \
*((volatile int*) NULL) = 123; \
::abort(); \
} while (0)
# else
# define MOZ_CRASH() \
# define MOZ_REALLY_CRASH() \
do { \
*((volatile int*) NULL) = 123; \
abort(); \
Expand All @@ -190,24 +228,35 @@ extern "C" {
#endif

/*
* Prints |s| as an assertion failure (using file and ln as the location of the
* assertion) to the standard debug-output channel.
* 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.
*
* 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.
* 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.
*/
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);
#ifndef DEBUG
# define MOZ_CRASH(...) MOZ_REALLY_CRASH()
#else
fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
fflush(stderr);
# define MOZ_CRASH(...) \
do { \
MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
MOZ_REALLY_CRASH(); \
} while(0)
#endif
}

#ifdef __cplusplus
} /* extern "C" */
Expand Down Expand Up @@ -251,15 +300,15 @@ MOZ_ReportAssertionFailure(const char* s, const char* file, int ln)
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. */
Expand Down

0 comments on commit d97ce09

Please sign in to comment.