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