Home | History | Annotate | Download | only in unittests
      1 // Copyright 2012, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 #include <windows.h>
     31 
     32 #include <string>
     33 
     34 #include "breakpad_googletest_includes.h"
     35 #include "client/windows/handler/exception_handler.h"
     36 #include "client/windows/unittests/exception_handler_test.h"
     37 
     38 namespace {
     39 
     40 const char kFoo[] = "foo";
     41 const char kBar[] = "bar";
     42 
     43 const char kStartOfLine[] = "^";
     44 const char kEndOfLine[] = "$";
     45 
     46 const char kFilterReturnsTrue[] = "filter_returns_true";
     47 const char kFilterReturnsFalse[] = "filter_returns_false";
     48 
     49 const char kCallbackReturnsTrue[] = "callback_returns_true";
     50 const char kCallbackReturnsFalse[] = "callback_returns_false";
     51 
     52 bool DoesPathExist(const wchar_t *path_name) {
     53   DWORD flags = GetFileAttributes(path_name);
     54   if (flags == INVALID_FILE_ATTRIBUTES) {
     55     return false;
     56   }
     57   return true;
     58 }
     59 
     60 // A callback function to run before Breakpad performs any substantial
     61 // processing of an exception.  A FilterCallback is called before writing
     62 // a minidump.  context is the parameter supplied by the user as
     63 // callback_context when the handler was created.  exinfo points to the
     64 // exception record, if any; assertion points to assertion information,
     65 // if any.
     66 //
     67 // If a FilterCallback returns true, Breakpad will continue processing,
     68 // attempting to write a minidump.  If a FilterCallback returns false,
     69 // Breakpad will immediately report the exception as unhandled without
     70 // writing a minidump, allowing another handler the opportunity to handle it.
     71 template <bool filter_return_value>
     72 bool CrashHandlerFilter(void* context,
     73                         EXCEPTION_POINTERS* exinfo,
     74                         MDRawAssertionInfo* assertion) {
     75   if (filter_return_value) {
     76     fprintf(stderr, kFilterReturnsTrue);
     77   } else {
     78     fprintf(stderr, kFilterReturnsFalse);
     79   }
     80   fflush(stderr);
     81 
     82   return filter_return_value;
     83 }
     84 
     85 // A callback function to run after the minidump has been written.
     86 // minidump_id is a unique id for the dump, so the minidump
     87 // file is <dump_path>\<minidump_id>.dmp.  context is the parameter supplied
     88 // by the user as callback_context when the handler was created.  exinfo
     89 // points to the exception record, or NULL if no exception occurred.
     90 // succeeded indicates whether a minidump file was successfully written.
     91 // assertion points to information about an assertion if the handler was
     92 // invoked by an assertion.
     93 //
     94 // If an exception occurred and the callback returns true, Breakpad will treat
     95 // the exception as fully-handled, suppressing any other handlers from being
     96 // notified of the exception.  If the callback returns false, Breakpad will
     97 // treat the exception as unhandled, and allow another handler to handle it.
     98 // If there are no other handlers, Breakpad will report the exception to the
     99 // system as unhandled, allowing a debugger or native crash dialog the
    100 // opportunity to handle the exception.  Most callback implementations
    101 // should normally return the value of |succeeded|, or when they wish to
    102 // not report an exception of handled, false.  Callbacks will rarely want to
    103 // return true directly (unless |succeeded| is true).
    104 //
    105 // For out-of-process dump generation, dump path and minidump ID will always
    106 // be NULL. In case of out-of-process dump generation, the dump path and
    107 // minidump id are controlled by the server process and are not communicated
    108 // back to the crashing process.
    109 template <bool callback_return_value>
    110 bool MinidumpWrittenCallback(const wchar_t* dump_path,
    111                              const wchar_t* minidump_id,
    112                              void* context,
    113                              EXCEPTION_POINTERS* exinfo,
    114                              MDRawAssertionInfo* assertion,
    115                              bool succeeded) {
    116   bool rv = false;
    117   if (callback_return_value &&
    118       succeeded &&
    119       DoesPathExist(dump_path)) {
    120     rv = true;
    121     fprintf(stderr, kCallbackReturnsTrue);
    122   } else {
    123     fprintf(stderr, kCallbackReturnsFalse);
    124   }
    125   fflush(stderr);
    126 
    127   return rv;
    128 }
    129 
    130 
    131 void DoCrash(const char *message) {
    132   if (message) {
    133     fprintf(stderr, "%s", message);
    134     fflush(stderr);
    135   }
    136   int *i = NULL;
    137   (*i)++;
    138 
    139   ASSERT_TRUE(false);
    140 }
    141 
    142 void InstallExceptionHandlerAndCrash(bool install_filter,
    143                                      bool filter_return_value,
    144                                      bool install_callback,
    145                                      bool callback_return_value) {
    146   wchar_t temp_path[MAX_PATH] = { '\0' };
    147   GetTempPath(MAX_PATH, temp_path);
    148 
    149   ASSERT_TRUE(DoesPathExist(temp_path));
    150   google_breakpad::ExceptionHandler exc(
    151       temp_path,
    152       install_filter ?
    153         (filter_return_value ?
    154           &CrashHandlerFilter<true> :
    155           &CrashHandlerFilter<false>) :
    156         NULL,
    157       install_callback ?
    158         (callback_return_value ?
    159           &MinidumpWrittenCallback<true> :
    160           &MinidumpWrittenCallback<false>) :
    161         NULL,
    162       NULL,  // callback_context
    163       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
    164 
    165   // Disable GTest SEH handler
    166   testing::DisableExceptionHandlerInScope disable_exception_handler;
    167 
    168   DoCrash(NULL);
    169 }
    170 
    171 TEST(AssertDeathSanity, Simple) {
    172   ASSERT_DEATH(DoCrash(NULL), "");
    173 }
    174 
    175 TEST(AssertDeathSanity, Regex) {
    176   ASSERT_DEATH(DoCrash(kFoo),
    177     std::string(kStartOfLine) +
    178       std::string(kFoo) +
    179       std::string(kEndOfLine));
    180 
    181   ASSERT_DEATH(DoCrash(kBar),
    182     std::string(kStartOfLine) +
    183       std::string(kBar) +
    184       std::string(kEndOfLine));
    185 }
    186 
    187 TEST(ExceptionHandlerCallbacks, FilterTrue_No_Callback) {
    188   ASSERT_DEATH(
    189     InstallExceptionHandlerAndCrash(true,    // install_filter
    190                                     true,    // filter_return_value
    191                                     false,   // install_callback
    192                                     false),  // callback_return_value
    193     std::string(kStartOfLine) +
    194       std::string(kFilterReturnsTrue) +
    195       std::string(kEndOfLine));
    196 }
    197 
    198 TEST(ExceptionHandlerCallbacks, FilterTrue_Callback) {
    199   ASSERT_DEATH(
    200     InstallExceptionHandlerAndCrash(true,    // install_filter
    201                                     true,    // filter_return_value
    202                                     true,    // install_callback
    203                                     false),  // callback_return_value
    204     std::string(kStartOfLine) +
    205       std::string(kFilterReturnsTrue) +
    206       std::string(kCallbackReturnsFalse) +
    207       std::string(kEndOfLine));
    208 }
    209 
    210 TEST(ExceptionHandlerCallbacks, FilterFalse_No_Callback) {
    211   ASSERT_DEATH(
    212     InstallExceptionHandlerAndCrash(true,    // install_filter
    213                                     false,   // filter_return_value
    214                                     false,   // install_callback
    215                                     false),  // callback_return_value
    216     std::string(kStartOfLine) +
    217       std::string(kFilterReturnsFalse) +
    218       std::string(kEndOfLine));
    219 }
    220 
    221 // Callback shouldn't be executed when filter returns false
    222 TEST(ExceptionHandlerCallbacks, FilterFalse_Callback) {
    223   ASSERT_DEATH(
    224     InstallExceptionHandlerAndCrash(true,    // install_filter
    225                                     false,   // filter_return_value
    226                                     true,    // install_callback
    227                                     false),  // callback_return_value
    228     std::string(kStartOfLine) +
    229       std::string(kFilterReturnsFalse) +
    230       std::string(kEndOfLine));
    231 }
    232 
    233 TEST(ExceptionHandlerCallbacks, No_Filter_No_Callback) {
    234   ASSERT_DEATH(
    235     InstallExceptionHandlerAndCrash(false,   // install_filter
    236                                     true,    // filter_return_value
    237                                     false,   // install_callback
    238                                     false),  // callback_return_value
    239     std::string(kStartOfLine) +
    240       std::string(kEndOfLine));
    241 }
    242 
    243 TEST(ExceptionHandlerCallbacks, No_Filter_Callback) {
    244   ASSERT_DEATH(
    245     InstallExceptionHandlerAndCrash(false,   // install_filter
    246                                     true,    // filter_return_value
    247                                     true,    // install_callback
    248                                     false),  // callback_return_value
    249     std::string(kStartOfLine) +
    250       std::string(kCallbackReturnsFalse) +
    251       std::string(kEndOfLine));
    252 }
    253 
    254 
    255 TEST(ExceptionHandlerNesting, Skip_From_Inner_Filter) {
    256   wchar_t temp_path[MAX_PATH] = { '\0' };
    257   GetTempPath(MAX_PATH, temp_path);
    258 
    259   ASSERT_TRUE(DoesPathExist(temp_path));
    260   google_breakpad::ExceptionHandler exc(
    261       temp_path,
    262       &CrashHandlerFilter<true>,
    263       &MinidumpWrittenCallback<false>,
    264       NULL,  // callback_context
    265       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
    266 
    267   ASSERT_DEATH(
    268     InstallExceptionHandlerAndCrash(true,   // install_filter
    269                                     false,  // filter_return_value
    270                                     true,   // install_callback
    271                                     true),  // callback_return_value
    272     std::string(kStartOfLine) +
    273       std::string(kFilterReturnsFalse) +    // inner filter
    274       std::string(kFilterReturnsTrue) +     // outer filter
    275       std::string(kCallbackReturnsFalse) +  // outer callback
    276       std::string(kEndOfLine));
    277 }
    278 
    279 TEST(ExceptionHandlerNesting, Skip_From_Inner_Callback) {
    280   wchar_t temp_path[MAX_PATH] = { '\0' };
    281   GetTempPath(MAX_PATH, temp_path);
    282 
    283   ASSERT_TRUE(DoesPathExist(temp_path));
    284   google_breakpad::ExceptionHandler exc(
    285       temp_path,
    286       &CrashHandlerFilter<true>,
    287       &MinidumpWrittenCallback<false>,
    288       NULL,  // callback_context
    289       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
    290 
    291   ASSERT_DEATH(
    292     InstallExceptionHandlerAndCrash(true,    // install_filter
    293                                     true,    // filter_return_value
    294                                     true,    // install_callback
    295                                     false),  // callback_return_value
    296     std::string(kStartOfLine) +
    297       std::string(kFilterReturnsTrue) +      // inner filter
    298       std::string(kCallbackReturnsFalse) +   // inner callback
    299       std::string(kFilterReturnsTrue) +      // outer filter
    300       std::string(kCallbackReturnsFalse) +   // outer callback
    301       std::string(kEndOfLine));
    302 }
    303 
    304 TEST(ExceptionHandlerNesting, Handled_By_Inner_Handler) {
    305   wchar_t temp_path[MAX_PATH] = { '\0' };
    306   GetTempPath(MAX_PATH, temp_path);
    307 
    308   ASSERT_TRUE(DoesPathExist(temp_path));
    309   google_breakpad::ExceptionHandler exc(
    310       temp_path,
    311       &CrashHandlerFilter<true>,
    312       &MinidumpWrittenCallback<true>,
    313       NULL,  // callback_context
    314       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
    315 
    316   ASSERT_DEATH(
    317     InstallExceptionHandlerAndCrash(true,   // install_filter
    318                                     true,   // filter_return_value
    319                                     true,   // install_callback
    320                                     true),  // callback_return_value
    321     std::string(kStartOfLine) +
    322       std::string(kFilterReturnsTrue) +    // inner filter
    323       std::string(kCallbackReturnsTrue) +  // inner callback
    324       std::string(kEndOfLine));
    325 }
    326 
    327 }  // namespace
    328