diff options
Diffstat (limited to 'externals/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc')
-rw-r--r-- | externals/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/externals/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc b/externals/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc new file mode 100644 index 0000000000..7fa6ac2260 --- /dev/null +++ b/externals/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc @@ -0,0 +1,330 @@ +// Copyright 2012 Google LLC +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include <config.h> // Must come first +#endif + +#include <windows.h> + +#include <string> + +#include "breakpad_googletest_includes.h" +#include "client/windows/handler/exception_handler.h" +#include "client/windows/unittests/exception_handler_test.h" + +namespace { + +const char kFoo[] = "foo"; +const char kBar[] = "bar"; + +const char kStartOfLine[] = "^"; +const char kEndOfLine[] = "$"; + +const char kFilterReturnsTrue[] = "filter_returns_true"; +const char kFilterReturnsFalse[] = "filter_returns_false"; + +const char kCallbackReturnsTrue[] = "callback_returns_true"; +const char kCallbackReturnsFalse[] = "callback_returns_false"; + +bool DoesPathExist(const wchar_t* path_name) { + DWORD flags = GetFileAttributes(path_name); + if (flags == INVALID_FILE_ATTRIBUTES) { + return false; + } + return true; +} + +// A callback function to run before Breakpad performs any substantial +// processing of an exception. A FilterCallback is called before writing +// a minidump. context is the parameter supplied by the user as +// callback_context when the handler was created. exinfo points to the +// exception record, if any; assertion points to assertion information, +// if any. +// +// If a FilterCallback returns true, Breakpad will continue processing, +// attempting to write a minidump. If a FilterCallback returns false, +// Breakpad will immediately report the exception as unhandled without +// writing a minidump, allowing another handler the opportunity to handle it. +template <bool filter_return_value> +bool CrashHandlerFilter(void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion) { + if (filter_return_value) { + fprintf(stderr, kFilterReturnsTrue); + } else { + fprintf(stderr, kFilterReturnsFalse); + } + fflush(stderr); + + return filter_return_value; +} + +// A callback function to run after the minidump has been written. +// minidump_id is a unique id for the dump, so the minidump +// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied +// by the user as callback_context when the handler was created. exinfo +// points to the exception record, or NULL if no exception occurred. +// succeeded indicates whether a minidump file was successfully written. +// assertion points to information about an assertion if the handler was +// invoked by an assertion. +// +// If an exception occurred and the callback returns true, Breakpad will treat +// the exception as fully-handled, suppressing any other handlers from being +// notified of the exception. If the callback returns false, Breakpad will +// treat the exception as unhandled, and allow another handler to handle it. +// If there are no other handlers, Breakpad will report the exception to the +// system as unhandled, allowing a debugger or native crash dialog the +// opportunity to handle the exception. Most callback implementations +// should normally return the value of |succeeded|, or when they wish to +// not report an exception of handled, false. Callbacks will rarely want to +// return true directly (unless |succeeded| is true). +// +// For out-of-process dump generation, dump path and minidump ID will always +// be NULL. In case of out-of-process dump generation, the dump path and +// minidump id are controlled by the server process and are not communicated +// back to the crashing process. +template <bool callback_return_value> +bool MinidumpWrittenCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + bool rv = false; + if (callback_return_value && + succeeded && + DoesPathExist(dump_path)) { + rv = true; + fprintf(stderr, kCallbackReturnsTrue); + } else { + fprintf(stderr, kCallbackReturnsFalse); + } + fflush(stderr); + + return rv; +} + + +void DoCrash(const char* message) { + if (message) { + fprintf(stderr, "%s", message); + fflush(stderr); + } + int* i = NULL; + (*i)++; + + ASSERT_TRUE(false); +} + +void InstallExceptionHandlerAndCrash(bool install_filter, + bool filter_return_value, + bool install_callback, + bool callback_return_value) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + install_filter ? + (filter_return_value ? + &CrashHandlerFilter<true> : + &CrashHandlerFilter<false>) : + NULL, + install_callback ? + (callback_return_value ? + &MinidumpWrittenCallback<true> : + &MinidumpWrittenCallback<false>) : + NULL, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + DoCrash(NULL); +} + +TEST(AssertDeathSanity, Simple) { + ASSERT_DEATH(DoCrash(NULL), ""); +} + +TEST(AssertDeathSanity, Regex) { + ASSERT_DEATH(DoCrash(kFoo), + std::string(kStartOfLine) + + std::string(kFoo) + + std::string(kEndOfLine)); + + ASSERT_DEATH(DoCrash(kBar), + std::string(kStartOfLine) + + std::string(kBar) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, FilterTrue_No_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + false, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, FilterTrue_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + + std::string(kCallbackReturnsFalse) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, FilterFalse_No_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + false, // filter_return_value + false, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsFalse) + + std::string(kEndOfLine)); +} + +// Callback shouldn't be executed when filter returns false +TEST(ExceptionHandlerCallbacks, FilterFalse_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + false, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsFalse) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, No_Filter_No_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(false, // install_filter + true, // filter_return_value + false, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, No_Filter_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(false, // install_filter + true, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kCallbackReturnsFalse) + + std::string(kEndOfLine)); +} + + +TEST(ExceptionHandlerNesting, Skip_From_Inner_Filter) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + &CrashHandlerFilter<true>, + &MinidumpWrittenCallback<false>, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + false, // filter_return_value + true, // install_callback + true), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsFalse) + // inner filter + std::string(kFilterReturnsTrue) + // outer filter + std::string(kCallbackReturnsFalse) + // outer callback + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerNesting, Skip_From_Inner_Callback) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + &CrashHandlerFilter<true>, + &MinidumpWrittenCallback<false>, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + // inner filter + std::string(kCallbackReturnsFalse) + // inner callback + std::string(kFilterReturnsTrue) + // outer filter + std::string(kCallbackReturnsFalse) + // outer callback + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerNesting, Handled_By_Inner_Handler) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + &CrashHandlerFilter<true>, + &MinidumpWrittenCallback<true>, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + true, // install_callback + true), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + // inner filter + std::string(kCallbackReturnsTrue) + // inner callback + std::string(kEndOfLine)); +} + +} // namespace |