| // Copyright 2012, Google Inc. | 
 | // All rights reserved. | 
 | // | 
 | // 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 Inc. 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. | 
 |  | 
 | #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 |