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 38namespace { 39 40const char kFoo[] = "foo"; 41const char kBar[] = "bar"; 42 43const char kStartOfLine[] = "^"; 44const char kEndOfLine[] = "$"; 45 46const char kFilterReturnsTrue[] = "filter_returns_true"; 47const char kFilterReturnsFalse[] = "filter_returns_false"; 48 49const char kCallbackReturnsTrue[] = "callback_returns_true"; 50const char kCallbackReturnsFalse[] = "callback_returns_false"; 51 52bool 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. 71template <bool filter_return_value> 72bool 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. 109template <bool callback_return_value> 110bool 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 131void 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 142void 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 171TEST(AssertDeathSanity, Simple) { 172 ASSERT_DEATH(DoCrash(NULL), ""); 173} 174 175TEST(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 187TEST(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 198TEST(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 210TEST(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 222TEST(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 233TEST(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 243TEST(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 255TEST(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 279TEST(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 304TEST(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