| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006, 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 <ObjBase.h> | |
| 31 | |
| 32 #include <algorithm> | |
| 33 #include <cassert> | |
| 34 #include <cstdio> | |
| 35 | |
| 36 #include "common/windows/string_utils-inl.h" | |
| 37 | |
| 38 #include "client/windows/common/ipc_protocol.h" | |
| 39 #include "client/windows/handler/exception_handler.h" | |
| 40 #include "common/windows/guid_string.h" | |
| 41 | |
| 42 typedef VOID (WINAPI *RtlCaptureContextPtr) (PCONTEXT pContextRecord); | |
| 43 | |
| 44 namespace google_breakpad { | |
| 45 | |
| 46 static const int kWaitForHandlerThreadMs = 60000; | |
| 47 static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; | |
| 48 | |
| 49 // This is passed as the context to the MinidumpWriteDump callback. | |
| 50 typedef struct { | |
| 51 ULONG64 memory_base; | |
| 52 ULONG memory_size; | |
| 53 bool finished; | |
| 54 } MinidumpCallbackContext; | |
| 55 | |
| 56 vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; | |
| 57 LONG ExceptionHandler::handler_stack_index_ = 0; | |
| 58 CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_; | |
| 59 volatile LONG ExceptionHandler::instance_count_ = 0; | |
| 60 | |
| 61 ExceptionHandler::ExceptionHandler(const wstring& dump_path, | |
| 62 FilterCallback filter, | |
| 63 MinidumpCallback callback, | |
| 64 void* callback_context, | |
| 65 int handler_types, | |
| 66 MINIDUMP_TYPE dump_type, | |
| 67 const wchar_t* pipe_name, | |
| 68 const CustomClientInfo* custom_info) { | |
| 69 Initialize(dump_path, | |
| 70 filter, | |
| 71 callback, | |
| 72 callback_context, | |
| 73 handler_types, | |
| 74 dump_type, | |
| 75 pipe_name, | |
| 76 custom_info); | |
| 77 } | |
| 78 | |
| 79 ExceptionHandler::ExceptionHandler(const wstring &dump_path, | |
| 80 FilterCallback filter, | |
| 81 MinidumpCallback callback, | |
| 82 void* callback_context, | |
| 83 int handler_types) { | |
| 84 Initialize(dump_path, | |
| 85 filter, | |
| 86 callback, | |
| 87 callback_context, | |
| 88 handler_types, | |
| 89 MiniDumpNormal, | |
| 90 NULL, | |
| 91 NULL); | |
| 92 } | |
| 93 | |
| 94 void ExceptionHandler::Initialize(const wstring& dump_path, | |
| 95 FilterCallback filter, | |
| 96 MinidumpCallback callback, | |
| 97 void* callback_context, | |
| 98 int handler_types, | |
| 99 MINIDUMP_TYPE dump_type, | |
| 100 const wchar_t* pipe_name, | |
| 101 const CustomClientInfo* custom_info) { | |
| 102 LONG instance_count = InterlockedIncrement(&instance_count_); | |
| 103 filter_ = filter; | |
| 104 callback_ = callback; | |
| 105 callback_context_ = callback_context; | |
| 106 dump_path_c_ = NULL; | |
| 107 next_minidump_id_c_ = NULL; | |
| 108 next_minidump_path_c_ = NULL; | |
| 109 dbghelp_module_ = NULL; | |
| 110 minidump_write_dump_ = NULL; | |
| 111 dump_type_ = dump_type; | |
| 112 rpcrt4_module_ = NULL; | |
| 113 uuid_create_ = NULL; | |
| 114 handler_types_ = handler_types; | |
| 115 previous_filter_ = NULL; | |
| 116 #if _MSC_VER >= 1400 // MSVC 2005/8 | |
| 117 previous_iph_ = NULL; | |
| 118 #endif // _MSC_VER >= 1400 | |
| 119 previous_pch_ = NULL; | |
| 120 handler_thread_ = NULL; | |
| 121 is_shutdown_ = false; | |
| 122 handler_start_semaphore_ = NULL; | |
| 123 handler_finish_semaphore_ = NULL; | |
| 124 requesting_thread_id_ = 0; | |
| 125 exception_info_ = NULL; | |
| 126 assertion_ = NULL; | |
| 127 handler_return_value_ = false; | |
| 128 handle_debug_exceptions_ = false; | |
| 129 | |
| 130 // Attempt to use out-of-process if user has specified pipe name. | |
| 131 if (pipe_name != NULL) { | |
| 132 scoped_ptr<CrashGenerationClient> client( | |
| 133 new CrashGenerationClient(pipe_name, | |
| 134 dump_type_, | |
| 135 custom_info)); | |
| 136 | |
| 137 // If successful in registering with the monitoring process, | |
| 138 // there is no need to setup in-process crash generation. | |
| 139 if (client->Register()) { | |
| 140 crash_generation_client_.reset(client.release()); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 if (!IsOutOfProcess()) { | |
| 145 // Either client did not ask for out-of-process crash generation | |
| 146 // or registration with the server process failed. In either case, | |
| 147 // setup to do in-process crash generation. | |
| 148 | |
| 149 // Set synchronization primitives and the handler thread. Each | |
| 150 // ExceptionHandler object gets its own handler thread because that's the | |
| 151 // only way to reliably guarantee sufficient stack space in an exception, | |
| 152 // and it allows an easy way to get a snapshot of the requesting thread's | |
| 153 // context outside of an exception. | |
| 154 InitializeCriticalSection(&handler_critical_section_); | |
| 155 handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); | |
| 156 assert(handler_start_semaphore_ != NULL); | |
| 157 | |
| 158 handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); | |
| 159 assert(handler_finish_semaphore_ != NULL); | |
| 160 | |
| 161 // Don't attempt to create the thread if we could not create the semaphores. | |
| 162 if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) { | |
| 163 DWORD thread_id; | |
| 164 handler_thread_ = CreateThread(NULL, // lpThreadAttributes | |
| 165 kExceptionHandlerThreadInitialStackSize, | |
| 166 ExceptionHandlerThreadMain, | |
| 167 this, // lpParameter | |
| 168 0, // dwCreationFlags | |
| 169 &thread_id); | |
| 170 assert(handler_thread_ != NULL); | |
| 171 } | |
| 172 | |
| 173 dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); | |
| 174 if (dbghelp_module_) { | |
| 175 minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>( | |
| 176 GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); | |
| 177 } | |
| 178 | |
| 179 // Load this library dynamically to not affect existing projects. Most | |
| 180 // projects don't link against this directly, it's usually dynamically | |
| 181 // loaded by dependent code. | |
| 182 rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll"); | |
| 183 if (rpcrt4_module_) { | |
| 184 uuid_create_ = reinterpret_cast<UuidCreate_type>( | |
| 185 GetProcAddress(rpcrt4_module_, "UuidCreate")); | |
| 186 } | |
| 187 | |
| 188 // set_dump_path calls UpdateNextID. This sets up all of the path and id | |
| 189 // strings, and their equivalent c_str pointers. | |
| 190 set_dump_path(dump_path); | |
| 191 } | |
| 192 | |
| 193 // There is a race condition here. If the first instance has not yet | |
| 194 // initialized the critical section, the second (and later) instances may | |
| 195 // try to use uninitialized critical section object. The feature of multiple | |
| 196 // instances in one module is not used much, so leave it as is for now. | |
| 197 // One way to solve this in the current design (that is, keeping the static | |
| 198 // handler stack) is to use spin locks with volatile bools to synchronize | |
| 199 // the handler stack. This works only if the compiler guarantees to generate | |
| 200 // cache coherent code for volatile. | |
| 201 // TODO(munjal): Fix this in a better way by changing the design if possible. | |
| 202 | |
| 203 // Lazy initialization of the handler_stack_critical_section_ | |
| 204 if (instance_count == 1) { | |
| 205 InitializeCriticalSection(&handler_stack_critical_section_); | |
| 206 } | |
| 207 | |
| 208 if (handler_types != HANDLER_NONE) { | |
| 209 EnterCriticalSection(&handler_stack_critical_section_); | |
| 210 | |
| 211 // The first time an ExceptionHandler that installs a handler is | |
| 212 // created, set up the handler stack. | |
| 213 if (!handler_stack_) { | |
| 214 handler_stack_ = new vector<ExceptionHandler*>(); | |
| 215 } | |
| 216 handler_stack_->push_back(this); | |
| 217 | |
| 218 if (handler_types & HANDLER_EXCEPTION) | |
| 219 previous_filter_ = SetUnhandledExceptionFilter(HandleException); | |
| 220 | |
| 221 #if _MSC_VER >= 1400 // MSVC 2005/8 | |
| 222 if (handler_types & HANDLER_INVALID_PARAMETER) | |
| 223 previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter); | |
| 224 #endif // _MSC_VER >= 1400 | |
| 225 | |
| 226 if (handler_types & HANDLER_PURECALL) | |
| 227 previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); | |
| 228 | |
| 229 LeaveCriticalSection(&handler_stack_critical_section_); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 ExceptionHandler::~ExceptionHandler() { | |
| 234 if (dbghelp_module_) { | |
| 235 FreeLibrary(dbghelp_module_); | |
| 236 } | |
| 237 | |
| 238 if (rpcrt4_module_) { | |
| 239 FreeLibrary(rpcrt4_module_); | |
| 240 } | |
| 241 | |
| 242 if (handler_types_ != HANDLER_NONE) { | |
| 243 EnterCriticalSection(&handler_stack_critical_section_); | |
| 244 | |
| 245 if (handler_types_ & HANDLER_EXCEPTION) | |
| 246 SetUnhandledExceptionFilter(previous_filter_); | |
| 247 | |
| 248 #if _MSC_VER >= 1400 // MSVC 2005/8 | |
| 249 if (handler_types_ & HANDLER_INVALID_PARAMETER) | |
| 250 _set_invalid_parameter_handler(previous_iph_); | |
| 251 #endif // _MSC_VER >= 1400 | |
| 252 | |
| 253 if (handler_types_ & HANDLER_PURECALL) | |
| 254 _set_purecall_handler(previous_pch_); | |
| 255 | |
| 256 if (handler_stack_->back() == this) { | |
| 257 handler_stack_->pop_back(); | |
| 258 } else { | |
| 259 // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the | |
| 260 // system's application event log. | |
| 261 fprintf(stderr, "warning: removing Breakpad handler out of order\n"); | |
| 262 vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin(); | |
| 263 while (iterator != handler_stack_->end()) { | |
| 264 if (*iterator == this) { | |
| 265 iterator = handler_stack_->erase(iterator); | |
| 266 } else { | |
| 267 ++iterator; | |
| 268 } | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 if (handler_stack_->empty()) { | |
| 273 // When destroying the last ExceptionHandler that installed a handler, | |
| 274 // clean up the handler stack. | |
| 275 delete handler_stack_; | |
| 276 handler_stack_ = NULL; | |
| 277 } | |
| 278 | |
| 279 LeaveCriticalSection(&handler_stack_critical_section_); | |
| 280 } | |
| 281 | |
| 282 // Some of the objects were only initialized if out of process | |
| 283 // registration was not done. | |
| 284 if (!IsOutOfProcess()) { | |
| 285 #ifdef BREAKPAD_NO_TERMINATE_THREAD | |
| 286 // Clean up the handler thread and synchronization primitives. The handler | |
| 287 // thread is either waiting on the semaphore to handle a crash or it is | |
| 288 // handling a crash. Coming out of the wait is fast but wait more in the | |
| 289 // eventuality a crash is handled. This compilation option results in a | |
| 290 // deadlock if the exception handler is destroyed while executing code | |
| 291 // inside DllMain. | |
| 292 is_shutdown_ = true; | |
| 293 ReleaseSemaphore(handler_start_semaphore_, 1, NULL); | |
| 294 WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs); | |
| 295 #else | |
| 296 TerminateThread(handler_thread_, 1); | |
| 297 #endif // BREAKPAD_NO_TERMINATE_THREAD | |
| 298 | |
| 299 CloseHandle(handler_thread_); | |
| 300 handler_thread_ = NULL; | |
| 301 DeleteCriticalSection(&handler_critical_section_); | |
| 302 CloseHandle(handler_start_semaphore_); | |
| 303 CloseHandle(handler_finish_semaphore_); | |
| 304 } | |
| 305 | |
| 306 // There is a race condition in the code below: if this instance is | |
| 307 // deleting the static critical section and a new instance of the class | |
| 308 // is created, then there is a possibility that the critical section be | |
| 309 // initialized while the same critical section is being deleted. Given the | |
| 310 // usage pattern for the code, this race condition is unlikely to hit, but it | |
| 311 // is a race condition nonetheless. | |
| 312 if (InterlockedDecrement(&instance_count_) == 0) { | |
| 313 DeleteCriticalSection(&handler_stack_critical_section_); | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 // static | |
| 318 DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) { | |
| 319 ExceptionHandler* self = reinterpret_cast<ExceptionHandler *>(lpParameter); | |
| 320 assert(self); | |
| 321 assert(self->handler_start_semaphore_ != NULL); | |
| 322 assert(self->handler_finish_semaphore_ != NULL); | |
| 323 | |
| 324 while (true) { | |
| 325 if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) == | |
| 326 WAIT_OBJECT_0) { | |
| 327 // Perform the requested action. | |
| 328 if (self->is_shutdown_) { | |
| 329 // The instance of the exception handler is being destroyed. | |
| 330 break; | |
| 331 } else { | |
| 332 self->handler_return_value_ = | |
| 333 self->WriteMinidumpWithException(self->requesting_thread_id_, | |
| 334 self->exception_info_, | |
| 335 self->assertion_); | |
| 336 } | |
| 337 | |
| 338 // Allow the requesting thread to proceed. | |
| 339 ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL); | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 // This statement is not reached when the thread is unconditionally | |
| 344 // terminated by the ExceptionHandler destructor. | |
| 345 return 0; | |
| 346 } | |
| 347 | |
| 348 // HandleException and HandleInvalidParameter must create an | |
| 349 // AutoExceptionHandler object to maintain static state and to determine which | |
| 350 // ExceptionHandler instance to use. The constructor locates the correct | |
| 351 // instance, and makes it available through get_handler(). The destructor | |
| 352 // restores the state in effect prior to allocating the AutoExceptionHandler. | |
| 353 class AutoExceptionHandler { | |
| 354 public: | |
| 355 AutoExceptionHandler() { | |
| 356 // Increment handler_stack_index_ so that if another Breakpad handler is | |
| 357 // registered using this same HandleException function, and it needs to be | |
| 358 // called while this handler is running (either because this handler | |
| 359 // declines to handle the exception, or an exception occurs during | |
| 360 // handling), HandleException will find the appropriate ExceptionHandler | |
| 361 // object in handler_stack_ to deliver the exception to. | |
| 362 // | |
| 363 // Because handler_stack_ is addressed in reverse (as |size - index|), | |
| 364 // preincrementing handler_stack_index_ avoids needing to subtract 1 from | |
| 365 // the argument to |at|. | |
| 366 // | |
| 367 // The index is maintained instead of popping elements off of the handler | |
| 368 // stack and pushing them at the end of this method. This avoids ruining | |
| 369 // the order of elements in the stack in the event that some other thread | |
| 370 // decides to manipulate the handler stack (such as creating a new | |
| 371 // ExceptionHandler object) while an exception is being handled. | |
| 372 EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_); | |
| 373 handler_ = ExceptionHandler::handler_stack_->at( | |
| 374 ExceptionHandler::handler_stack_->size() - | |
| 375 ++ExceptionHandler::handler_stack_index_); | |
| 376 | |
| 377 // In case another exception occurs while this handler is doing its thing, | |
| 378 // it should be delivered to the previous filter. | |
| 379 SetUnhandledExceptionFilter(handler_->previous_filter_); | |
| 380 #if _MSC_VER >= 1400 // MSVC 2005/8 | |
| 381 _set_invalid_parameter_handler(handler_->previous_iph_); | |
| 382 #endif // _MSC_VER >= 1400 | |
| 383 _set_purecall_handler(handler_->previous_pch_); | |
| 384 } | |
| 385 | |
| 386 ~AutoExceptionHandler() { | |
| 387 // Put things back the way they were before entering this handler. | |
| 388 SetUnhandledExceptionFilter(ExceptionHandler::HandleException); | |
| 389 #if _MSC_VER >= 1400 // MSVC 2005/8 | |
| 390 _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter); | |
| 391 #endif // _MSC_VER >= 1400 | |
| 392 _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall); | |
| 393 | |
| 394 --ExceptionHandler::handler_stack_index_; | |
| 395 LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_); | |
| 396 } | |
| 397 | |
| 398 ExceptionHandler* get_handler() const { return handler_; } | |
| 399 | |
| 400 private: | |
| 401 ExceptionHandler* handler_; | |
| 402 }; | |
| 403 | |
| 404 // static | |
| 405 LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) { | |
| 406 AutoExceptionHandler auto_exception_handler; | |
| 407 ExceptionHandler* current_handler = auto_exception_handler.get_handler(); | |
| 408 | |
| 409 // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This | |
| 410 // logic will short-circuit before calling WriteMinidumpOnHandlerThread, | |
| 411 // allowing something else to handle the breakpoint without incurring the | |
| 412 // overhead transitioning to and from the handler thread. This behavior | |
| 413 // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions. | |
| 414 DWORD code = exinfo->ExceptionRecord->ExceptionCode; | |
| 415 LONG action; | |
| 416 bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) || | |
| 417 (code == EXCEPTION_SINGLE_STEP); | |
| 418 | |
| 419 bool success = false; | |
| 420 | |
| 421 if (!is_debug_exception || | |
| 422 current_handler->get_handle_debug_exceptions()) { | |
| 423 // If out-of-proc crash handler client is available, we have to use that | |
| 424 // to generate dump and we cannot fall back on in-proc dump generation | |
| 425 // because we never prepared for an in-proc dump generation | |
| 426 | |
| 427 // In case of out-of-process dump generation, directly call | |
| 428 // WriteMinidumpWithException since there is no separate thread running. | |
| 429 if (current_handler->IsOutOfProcess()) { | |
| 430 success = current_handler->WriteMinidumpWithException( | |
| 431 GetCurrentThreadId(), | |
| 432 exinfo, | |
| 433 NULL); | |
| 434 } else { | |
| 435 success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL); | |
| 436 } | |
| 437 } | |
| 438 | |
| 439 // The handler fully handled the exception. Returning | |
| 440 // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually | |
| 441 // results in the application being terminated. | |
| 442 // | |
| 443 // Note: If the application was launched from within the Cygwin | |
| 444 // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the | |
| 445 // application to be restarted. | |
| 446 if (success) { | |
| 447 action = EXCEPTION_EXECUTE_HANDLER; | |
| 448 } else { | |
| 449 // There was an exception, it was a breakpoint or something else ignored | |
| 450 // above, or it was passed to the handler, which decided not to handle it. | |
| 451 // This could be because the filter callback didn't want it, because | |
| 452 // minidump writing failed for some reason, or because the post-minidump | |
| 453 // callback function indicated failure. Give the previous handler a | |
| 454 // chance to do something with the exception. If there is no previous | |
| 455 // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger | |
| 456 // or native "crashed" dialog to handle the exception. | |
| 457 if (current_handler->previous_filter_) { | |
| 458 action = current_handler->previous_filter_(exinfo); | |
| 459 } else { | |
| 460 action = EXCEPTION_CONTINUE_SEARCH; | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 return action; | |
| 465 } | |
| 466 | |
| 467 #if _MSC_VER >= 1400 // MSVC 2005/8 | |
| 468 // static | |
| 469 void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, | |
| 470 const wchar_t* function, | |
| 471 const wchar_t* file, | |
| 472 unsigned int line, | |
| 473 uintptr_t reserved) { | |
| 474 // This is an invalid parameter, not an exception. It's safe to play with | |
| 475 // sprintf here. | |
| 476 AutoExceptionHandler auto_exception_handler; | |
| 477 ExceptionHandler* current_handler = auto_exception_handler.get_handler(); | |
| 478 | |
| 479 MDRawAssertionInfo assertion; | |
| 480 memset(&assertion, 0, sizeof(assertion)); | |
| 481 _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.expression), | |
| 482 sizeof(assertion.expression) / sizeof(assertion.expression[0]), | |
| 483 _TRUNCATE, L"%s", expression); | |
| 484 _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.function), | |
| 485 sizeof(assertion.function) / sizeof(assertion.function[0]), | |
| 486 _TRUNCATE, L"%s", function); | |
| 487 _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.file), | |
| 488 sizeof(assertion.file) / sizeof(assertion.file[0]), | |
| 489 _TRUNCATE, L"%s", file); | |
| 490 assertion.line = line; | |
| 491 assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER; | |
| 492 | |
| 493 // Make up an exception record for the current thread and CPU context | |
| 494 // to make it possible for the crash processor to classify these | |
| 495 // as do regular crashes, and to make it humane for developers to | |
| 496 // analyze them. | |
| 497 EXCEPTION_RECORD exception_record = {}; | |
| 498 CONTEXT exception_context = {}; | |
| 499 EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; | |
| 500 | |
| 501 EXCEPTION_POINTERS* exinfo = NULL; | |
| 502 | |
| 503 RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr) | |
| 504 GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext"); | |
| 505 if (fnRtlCaptureContext) { | |
| 506 fnRtlCaptureContext(&exception_context); | |
| 507 | |
| 508 exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; | |
| 509 | |
| 510 // We store pointers to the the expression and function strings, | |
| 511 // and the line as exception parameters to make them easy to | |
| 512 // access by the developer on the far side. | |
| 513 exception_record.NumberParameters = 3; | |
| 514 exception_record.ExceptionInformation[0] = | |
| 515 reinterpret_cast<ULONG_PTR>(&assertion.expression); | |
| 516 exception_record.ExceptionInformation[1] = | |
| 517 reinterpret_cast<ULONG_PTR>(&assertion.file); | |
| 518 exception_record.ExceptionInformation[2] = assertion.line; | |
| 519 | |
| 520 exinfo = &exception_ptrs; | |
| 521 } | |
| 522 | |
| 523 bool success = false; | |
| 524 // In case of out-of-process dump generation, directly call | |
| 525 // WriteMinidumpWithException since there is no separate thread running. | |
| 526 if (current_handler->IsOutOfProcess()) { | |
| 527 success = current_handler->WriteMinidumpWithException( | |
| 528 GetCurrentThreadId(), | |
| 529 exinfo, | |
| 530 &assertion); | |
| 531 } else { | |
| 532 success = current_handler->WriteMinidumpOnHandlerThread(exinfo, | |
| 533 &assertion); | |
| 534 } | |
| 535 | |
| 536 if (!success) { | |
| 537 if (current_handler->previous_iph_) { | |
| 538 // The handler didn't fully handle the exception. Give it to the | |
| 539 // previous invalid parameter handler. | |
| 540 current_handler->previous_iph_(expression, | |
| 541 function, | |
| 542 file, | |
| 543 line, | |
| 544 reserved); | |
| 545 } else { | |
| 546 // If there's no previous handler, pass the exception back in to the | |
| 547 // invalid parameter handler's core. That's the routine that called this | |
| 548 // function, but now, since this function is no longer registered (and in | |
| 549 // fact, no function at all is registered), this will result in the | |
| 550 // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson. | |
| 551 // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes | |
| 552 // more information through. In non-debug builds, it is not available, | |
| 553 // so fall back to using _invalid_parameter_noinfo. See invarg.c in the | |
| 554 // CRT source. | |
| 555 #ifdef _DEBUG | |
| 556 _invalid_parameter(expression, function, file, line, reserved); | |
| 557 #else // _DEBUG | |
| 558 _invalid_parameter_noinfo(); | |
| 559 #endif // _DEBUG | |
| 560 } | |
| 561 } | |
| 562 | |
| 563 // The handler either took care of the invalid parameter problem itself, | |
| 564 // or passed it on to another handler. "Swallow" it by exiting, paralleling | |
| 565 // the behavior of "swallowing" exceptions. | |
| 566 exit(0); | |
| 567 } | |
| 568 #endif // _MSC_VER >= 1400 | |
| 569 | |
| 570 // static | |
| 571 void ExceptionHandler::HandlePureVirtualCall() { | |
| 572 // This is an pure virtual function call, not an exception. It's safe to | |
| 573 // play with sprintf here. | |
| 574 AutoExceptionHandler auto_exception_handler; | |
| 575 ExceptionHandler* current_handler = auto_exception_handler.get_handler(); | |
| 576 | |
| 577 MDRawAssertionInfo assertion; | |
| 578 memset(&assertion, 0, sizeof(assertion)); | |
| 579 assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL; | |
| 580 | |
| 581 // Make up an exception record for the current thread and CPU context | |
| 582 // to make it possible for the crash processor to classify these | |
| 583 // as do regular crashes, and to make it humane for developers to | |
| 584 // analyze them. | |
| 585 EXCEPTION_RECORD exception_record = {}; | |
| 586 CONTEXT exception_context = {}; | |
| 587 EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; | |
| 588 | |
| 589 EXCEPTION_POINTERS* exinfo = NULL; | |
| 590 | |
| 591 RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr) | |
| 592 GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext"); | |
| 593 if (fnRtlCaptureContext) { | |
| 594 fnRtlCaptureContext(&exception_context); | |
| 595 | |
| 596 exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; | |
| 597 | |
| 598 // We store pointers to the the expression and function strings, | |
| 599 // and the line as exception parameters to make them easy to | |
| 600 // access by the developer on the far side. | |
| 601 exception_record.NumberParameters = 3; | |
| 602 exception_record.ExceptionInformation[0] = | |
| 603 reinterpret_cast<ULONG_PTR>(&assertion.expression); | |
| 604 exception_record.ExceptionInformation[1] = | |
| 605 reinterpret_cast<ULONG_PTR>(&assertion.file); | |
| 606 exception_record.ExceptionInformation[2] = assertion.line; | |
| 607 | |
| 608 exinfo = &exception_ptrs; | |
| 609 } | |
| 610 | |
| 611 bool success = false; | |
| 612 // In case of out-of-process dump generation, directly call | |
| 613 // WriteMinidumpWithException since there is no separate thread running. | |
| 614 | |
| 615 if (current_handler->IsOutOfProcess()) { | |
| 616 success = current_handler->WriteMinidumpWithException( | |
| 617 GetCurrentThreadId(), | |
| 618 exinfo, | |
| 619 &assertion); | |
| 620 } else { | |
| 621 success = current_handler->WriteMinidumpOnHandlerThread(exinfo, | |
| 622 &assertion); | |
| 623 } | |
| 624 | |
| 625 if (!success) { | |
| 626 if (current_handler->previous_pch_) { | |
| 627 // The handler didn't fully handle the exception. Give it to the | |
| 628 // previous purecall handler. | |
| 629 current_handler->previous_pch_(); | |
| 630 } else { | |
| 631 // If there's no previous handler, return and let _purecall handle it. | |
| 632 // This will just put up an assertion dialog. | |
| 633 return; | |
| 634 } | |
| 635 } | |
| 636 | |
| 637 // The handler either took care of the invalid parameter problem itself, | |
| 638 // or passed it on to another handler. "Swallow" it by exiting, paralleling | |
| 639 // the behavior of "swallowing" exceptions. | |
| 640 exit(0); | |
| 641 } | |
| 642 | |
| 643 bool ExceptionHandler::WriteMinidumpOnHandlerThread( | |
| 644 EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) { | |
| 645 EnterCriticalSection(&handler_critical_section_); | |
| 646 | |
| 647 // There isn't much we can do if the handler thread | |
| 648 // was not successfully created. | |
| 649 if (handler_thread_ == NULL) { | |
| 650 LeaveCriticalSection(&handler_critical_section_); | |
| 651 return false; | |
| 652 } | |
| 653 | |
| 654 // The handler thread should only be created when the semaphores are valid. | |
| 655 assert(handler_start_semaphore_ != NULL); | |
| 656 assert(handler_finish_semaphore_ != NULL); | |
| 657 | |
| 658 // Set up data to be passed in to the handler thread. | |
| 659 requesting_thread_id_ = GetCurrentThreadId(); | |
| 660 exception_info_ = exinfo; | |
| 661 assertion_ = assertion; | |
| 662 | |
| 663 // This causes the handler thread to call WriteMinidumpWithException. | |
| 664 ReleaseSemaphore(handler_start_semaphore_, 1, NULL); | |
| 665 | |
| 666 // Wait until WriteMinidumpWithException is done and collect its return value. | |
| 667 WaitForSingleObject(handler_finish_semaphore_, INFINITE); | |
| 668 bool status = handler_return_value_; | |
| 669 | |
| 670 // Clean up. | |
| 671 requesting_thread_id_ = 0; | |
| 672 exception_info_ = NULL; | |
| 673 assertion_ = NULL; | |
| 674 | |
| 675 LeaveCriticalSection(&handler_critical_section_); | |
| 676 | |
| 677 return status; | |
| 678 } | |
| 679 | |
| 680 bool ExceptionHandler::WriteMinidump() { | |
| 681 return WriteMinidumpForException(NULL); | |
| 682 } | |
| 683 | |
| 684 bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) { | |
| 685 // In case of out-of-process dump generation, directly call | |
| 686 // WriteMinidumpWithException since there is no separate thread running. | |
| 687 if (IsOutOfProcess()) { | |
| 688 return WriteMinidumpWithException(GetCurrentThreadId(), | |
| 689 exinfo, | |
| 690 NULL); | |
| 691 } | |
| 692 | |
| 693 bool success = WriteMinidumpOnHandlerThread(exinfo, NULL); | |
| 694 UpdateNextID(); | |
| 695 return success; | |
| 696 } | |
| 697 | |
| 698 // static | |
| 699 bool ExceptionHandler::WriteMinidump(const wstring &dump_path, | |
| 700 MinidumpCallback callback, | |
| 701 void* callback_context) { | |
| 702 ExceptionHandler handler(dump_path, NULL, callback, callback_context, | |
| 703 HANDLER_NONE); | |
| 704 return handler.WriteMinidump(); | |
| 705 } | |
| 706 | |
| 707 bool ExceptionHandler::WriteMinidumpWithException( | |
| 708 DWORD requesting_thread_id, | |
| 709 EXCEPTION_POINTERS* exinfo, | |
| 710 MDRawAssertionInfo* assertion) { | |
| 711 // Give user code a chance to approve or prevent writing a minidump. If the | |
| 712 // filter returns false, don't handle the exception at all. If this method | |
| 713 // was called as a result of an exception, returning false will cause | |
| 714 // HandleException to call any previous handler or return | |
| 715 // EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear | |
| 716 // as though this handler were not present at all. | |
| 717 if (filter_ && !filter_(callback_context_, exinfo, assertion)) { | |
| 718 return false; | |
| 719 } | |
| 720 | |
| 721 bool success = false; | |
| 722 if (IsOutOfProcess()) { | |
| 723 success = crash_generation_client_->RequestDump(exinfo, assertion); | |
| 724 } else { | |
| 725 if (minidump_write_dump_) { | |
| 726 HANDLE dump_file = CreateFile(next_minidump_path_c_, | |
| 727 GENERIC_WRITE, | |
| 728 0, // no sharing | |
| 729 NULL, | |
| 730 CREATE_NEW, // fail if exists | |
| 731 FILE_ATTRIBUTE_NORMAL, | |
| 732 NULL); | |
| 733 if (dump_file != INVALID_HANDLE_VALUE) { | |
| 734 MINIDUMP_EXCEPTION_INFORMATION except_info; | |
| 735 except_info.ThreadId = requesting_thread_id; | |
| 736 except_info.ExceptionPointers = exinfo; | |
| 737 except_info.ClientPointers = FALSE; | |
| 738 | |
| 739 // Add an MDRawBreakpadInfo stream to the minidump, to provide | |
| 740 // additional information about the exception handler to the Breakpad | |
| 741 // processor. The information will help the processor determine which | |
| 742 // threads are relevant. The Breakpad processor does not require this | |
| 743 // information but can function better with Breakpad-generated dumps | |
| 744 // when it is present. The native debugger is not harmed by the | |
| 745 // presence of this information. | |
| 746 MDRawBreakpadInfo breakpad_info; | |
| 747 breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | | |
| 748 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; | |
| 749 breakpad_info.dump_thread_id = GetCurrentThreadId(); | |
| 750 breakpad_info.requesting_thread_id = requesting_thread_id; | |
| 751 | |
| 752 // Leave room in user_stream_array for a possible assertion info stream. | |
| 753 MINIDUMP_USER_STREAM user_stream_array[2]; | |
| 754 user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; | |
| 755 user_stream_array[0].BufferSize = sizeof(breakpad_info); | |
| 756 user_stream_array[0].Buffer = &breakpad_info; | |
| 757 | |
| 758 MINIDUMP_USER_STREAM_INFORMATION user_streams; | |
| 759 user_streams.UserStreamCount = 1; | |
| 760 user_streams.UserStreamArray = user_stream_array; | |
| 761 | |
| 762 if (assertion) { | |
| 763 user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; | |
| 764 user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); | |
| 765 user_stream_array[1].Buffer = assertion; | |
| 766 ++user_streams.UserStreamCount; | |
| 767 } | |
| 768 | |
| 769 MINIDUMP_CALLBACK_INFORMATION callback; | |
| 770 MinidumpCallbackContext context; | |
| 771 MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL; | |
| 772 // Older versions of DbgHelp.dll don't correctly put the memory around | |
| 773 // the faulting instruction pointer into the minidump. This | |
| 774 // callback will ensure that it gets included. | |
| 775 if (exinfo) { | |
| 776 // Find a memory region of 256 bytes centered on the | |
| 777 // faulting instruction pointer. | |
| 778 const ULONG64 instruction_pointer = | |
| 779 #if defined(_M_IX86) | |
| 780 exinfo->ContextRecord->Eip; | |
| 781 #elif defined(_M_AMD64) | |
| 782 exinfo->ContextRecord->Rip; | |
| 783 #else | |
| 784 #error Unsupported platform | |
| 785 #endif | |
| 786 | |
| 787 MEMORY_BASIC_INFORMATION info; | |
| 788 if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer), | |
| 789 &info, | |
| 790 sizeof(MEMORY_BASIC_INFORMATION)) != 0 && | |
| 791 info.State == MEM_COMMIT) { | |
| 792 // Attempt to get 128 bytes before and after the instruction | |
| 793 // pointer, but settle for whatever's available up to the | |
| 794 // boundaries of the memory region. | |
| 795 const ULONG64 kIPMemorySize = 256; | |
| 796 context.memory_base = | |
| 797 (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress), | |
| 798 instruction_pointer - (kIPMemorySize / 2)); | |
| 799 ULONG64 end_of_range = | |
| 800 (std::min)(instruction_pointer + (kIPMemorySize / 2), | |
| 801 reinterpret_cast<ULONG64>(info.BaseAddress) | |
| 802 + info.RegionSize); | |
| 803 context.memory_size = | |
| 804 static_cast<ULONG>(end_of_range - context.memory_base); | |
| 805 | |
| 806 context.finished = false; | |
| 807 callback.CallbackRoutine = MinidumpWriteDumpCallback; | |
| 808 callback.CallbackParam = reinterpret_cast<void*>(&context); | |
| 809 callback_pointer = &callback; | |
| 810 } | |
| 811 } | |
| 812 | |
| 813 // The explicit comparison to TRUE avoids a warning (C4800). | |
| 814 success = (minidump_write_dump_(GetCurrentProcess(), | |
| 815 GetCurrentProcessId(), | |
| 816 dump_file, | |
| 817 dump_type_, | |
| 818 exinfo ? &except_info : NULL, | |
| 819 &user_streams, | |
| 820 callback_pointer) == TRUE); | |
| 821 | |
| 822 CloseHandle(dump_file); | |
| 823 } | |
| 824 } | |
| 825 } | |
| 826 | |
| 827 if (callback_) { | |
| 828 // TODO(munjal): In case of out-of-process dump generation, both | |
| 829 // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process | |
| 830 // scenario, the server process ends up creating the dump path and dump | |
| 831 // id so they are not known to the client. | |
| 832 success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_, | |
| 833 exinfo, assertion, success); | |
| 834 } | |
| 835 | |
| 836 return success; | |
| 837 } | |
| 838 | |
| 839 // static | |
| 840 BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback( | |
| 841 PVOID context, | |
| 842 const PMINIDUMP_CALLBACK_INPUT callback_input, | |
| 843 PMINIDUMP_CALLBACK_OUTPUT callback_output) { | |
| 844 switch (callback_input->CallbackType) { | |
| 845 case MemoryCallback: { | |
| 846 MinidumpCallbackContext* callback_context = | |
| 847 reinterpret_cast<MinidumpCallbackContext*>(context); | |
| 848 if (callback_context->finished) | |
| 849 return FALSE; | |
| 850 | |
| 851 // Include the specified memory region. | |
| 852 callback_output->MemoryBase = callback_context->memory_base; | |
| 853 callback_output->MemorySize = callback_context->memory_size; | |
| 854 callback_context->finished = true; | |
| 855 return TRUE; | |
| 856 } | |
| 857 | |
| 858 // Include all modules. | |
| 859 case IncludeModuleCallback: | |
| 860 case ModuleCallback: | |
| 861 return TRUE; | |
| 862 | |
| 863 // Include all threads. | |
| 864 case IncludeThreadCallback: | |
| 865 case ThreadCallback: | |
| 866 return TRUE; | |
| 867 | |
| 868 // Stop receiving cancel callbacks. | |
| 869 case CancelCallback: | |
| 870 callback_output->CheckCancel = FALSE; | |
| 871 callback_output->Cancel = FALSE; | |
| 872 return TRUE; | |
| 873 } | |
| 874 // Ignore other callback types. | |
| 875 return FALSE; | |
| 876 } | |
| 877 | |
| 878 void ExceptionHandler::UpdateNextID() { | |
| 879 assert(uuid_create_); | |
| 880 UUID id = {0}; | |
| 881 if (uuid_create_) { | |
| 882 uuid_create_(&id); | |
| 883 } | |
| 884 next_minidump_id_ = GUIDString::GUIDToWString(&id); | |
| 885 next_minidump_id_c_ = next_minidump_id_.c_str(); | |
| 886 | |
| 887 wchar_t minidump_path[MAX_PATH]; | |
| 888 swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp", | |
| 889 dump_path_c_, next_minidump_id_c_); | |
| 890 | |
| 891 // remove when VC++7.1 is no longer supported | |
| 892 minidump_path[MAX_PATH - 1] = L'\0'; | |
| 893 | |
| 894 next_minidump_path_ = minidump_path; | |
| 895 next_minidump_path_c_ = next_minidump_path_.c_str(); | |
| 896 } | |
| 897 | |
| 898 } // namespace google_breakpad | |
| OLD | NEW |