summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc')
-rw-r--r--3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc927
1 files changed, 927 insertions, 0 deletions
diff --git a/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc b/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc
new file mode 100644
index 0000000..6e5b724
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc
@@ -0,0 +1,927 @@
+// Copyright (c) 2006, 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 <ObjBase.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+
+#include "common/windows/string_utils-inl.h"
+
+#include "client/windows/common/ipc_protocol.h"
+#include "client/windows/handler/exception_handler.h"
+#include "common/windows/guid_string.h"
+
+namespace google_breakpad {
+
+static const int kWaitForHandlerThreadMs = 60000;
+static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
+
+// This is passed as the context to the MinidumpWriteDump callback.
+typedef struct {
+ ULONG64 memory_base;
+ ULONG memory_size;
+ bool finished;
+} MinidumpCallbackContext;
+
+vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
+LONG ExceptionHandler::handler_stack_index_ = 0;
+CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
+volatile LONG ExceptionHandler::instance_count_ = 0;
+
+ExceptionHandler::ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ const wchar_t* pipe_name,
+ const CustomClientInfo* custom_info) {
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ dump_type,
+ pipe_name,
+ NULL,
+ custom_info);
+}
+
+ExceptionHandler::ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ HANDLE pipe_handle,
+ const CustomClientInfo* custom_info) {
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ dump_type,
+ NULL,
+ pipe_handle,
+ custom_info);
+}
+
+ExceptionHandler::ExceptionHandler(const wstring &dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types) {
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ MiniDumpNormal,
+ NULL,
+ NULL,
+ NULL);
+}
+
+void ExceptionHandler::Initialize(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ const wchar_t* pipe_name,
+ HANDLE pipe_handle,
+ const CustomClientInfo* custom_info) {
+ LONG instance_count = InterlockedIncrement(&instance_count_);
+ filter_ = filter;
+ callback_ = callback;
+ callback_context_ = callback_context;
+ dump_path_c_ = NULL;
+ next_minidump_id_c_ = NULL;
+ next_minidump_path_c_ = NULL;
+ dbghelp_module_ = NULL;
+ minidump_write_dump_ = NULL;
+ dump_type_ = dump_type;
+ rpcrt4_module_ = NULL;
+ uuid_create_ = NULL;
+ handler_types_ = handler_types;
+ previous_filter_ = NULL;
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ previous_iph_ = NULL;
+#endif // _MSC_VER >= 1400
+ previous_pch_ = NULL;
+ handler_thread_ = NULL;
+ is_shutdown_ = false;
+ handler_start_semaphore_ = NULL;
+ handler_finish_semaphore_ = NULL;
+ requesting_thread_id_ = 0;
+ exception_info_ = NULL;
+ assertion_ = NULL;
+ handler_return_value_ = false;
+ handle_debug_exceptions_ = false;
+
+ // Attempt to use out-of-process if user has specified a pipe.
+ if (pipe_name != NULL || pipe_handle != NULL) {
+ assert(!(pipe_name && pipe_handle));
+
+ scoped_ptr<CrashGenerationClient> client;
+ if (pipe_name) {
+ client.reset(
+ new CrashGenerationClient(pipe_name,
+ dump_type_,
+ custom_info));
+ } else {
+ client.reset(
+ new CrashGenerationClient(pipe_handle,
+ dump_type_,
+ custom_info));
+ }
+
+ // If successful in registering with the monitoring process,
+ // there is no need to setup in-process crash generation.
+ if (client->Register()) {
+ crash_generation_client_.reset(client.release());
+ }
+ }
+
+ if (!IsOutOfProcess()) {
+ // Either client did not ask for out-of-process crash generation
+ // or registration with the server process failed. In either case,
+ // setup to do in-process crash generation.
+
+ // Set synchronization primitives and the handler thread. Each
+ // ExceptionHandler object gets its own handler thread because that's the
+ // only way to reliably guarantee sufficient stack space in an exception,
+ // and it allows an easy way to get a snapshot of the requesting thread's
+ // context outside of an exception.
+ InitializeCriticalSection(&handler_critical_section_);
+ handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
+ assert(handler_start_semaphore_ != NULL);
+
+ handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
+ assert(handler_finish_semaphore_ != NULL);
+
+ // Don't attempt to create the thread if we could not create the semaphores.
+ if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) {
+ DWORD thread_id;
+ handler_thread_ = CreateThread(NULL, // lpThreadAttributes
+ kExceptionHandlerThreadInitialStackSize,
+ ExceptionHandlerThreadMain,
+ this, // lpParameter
+ 0, // dwCreationFlags
+ &thread_id);
+ assert(handler_thread_ != NULL);
+ }
+
+ dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
+ if (dbghelp_module_) {
+ minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(
+ GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
+ }
+
+ // Load this library dynamically to not affect existing projects. Most
+ // projects don't link against this directly, it's usually dynamically
+ // loaded by dependent code.
+ rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll");
+ if (rpcrt4_module_) {
+ uuid_create_ = reinterpret_cast<UuidCreate_type>(
+ GetProcAddress(rpcrt4_module_, "UuidCreate"));
+ }
+
+ // set_dump_path calls UpdateNextID. This sets up all of the path and id
+ // strings, and their equivalent c_str pointers.
+ set_dump_path(dump_path);
+ }
+
+ // There is a race condition here. If the first instance has not yet
+ // initialized the critical section, the second (and later) instances may
+ // try to use uninitialized critical section object. The feature of multiple
+ // instances in one module is not used much, so leave it as is for now.
+ // One way to solve this in the current design (that is, keeping the static
+ // handler stack) is to use spin locks with volatile bools to synchronize
+ // the handler stack. This works only if the compiler guarantees to generate
+ // cache coherent code for volatile.
+ // TODO(munjal): Fix this in a better way by changing the design if possible.
+
+ // Lazy initialization of the handler_stack_critical_section_
+ if (instance_count == 1) {
+ InitializeCriticalSection(&handler_stack_critical_section_);
+ }
+
+ if (handler_types != HANDLER_NONE) {
+ EnterCriticalSection(&handler_stack_critical_section_);
+
+ // The first time an ExceptionHandler that installs a handler is
+ // created, set up the handler stack.
+ if (!handler_stack_) {
+ handler_stack_ = new vector<ExceptionHandler*>();
+ }
+ handler_stack_->push_back(this);
+
+ if (handler_types & HANDLER_EXCEPTION)
+ previous_filter_ = SetUnhandledExceptionFilter(HandleException);
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if (handler_types & HANDLER_INVALID_PARAMETER)
+ previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);
+#endif // _MSC_VER >= 1400
+
+ if (handler_types & HANDLER_PURECALL)
+ previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);
+
+ LeaveCriticalSection(&handler_stack_critical_section_);
+ }
+}
+
+ExceptionHandler::~ExceptionHandler() {
+ if (dbghelp_module_) {
+ FreeLibrary(dbghelp_module_);
+ }
+
+ if (rpcrt4_module_) {
+ FreeLibrary(rpcrt4_module_);
+ }
+
+ if (handler_types_ != HANDLER_NONE) {
+ EnterCriticalSection(&handler_stack_critical_section_);
+
+ if (handler_types_ & HANDLER_EXCEPTION)
+ SetUnhandledExceptionFilter(previous_filter_);
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if (handler_types_ & HANDLER_INVALID_PARAMETER)
+ _set_invalid_parameter_handler(previous_iph_);
+#endif // _MSC_VER >= 1400
+
+ if (handler_types_ & HANDLER_PURECALL)
+ _set_purecall_handler(previous_pch_);
+
+ if (handler_stack_->back() == this) {
+ handler_stack_->pop_back();
+ } else {
+ // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the
+ // system's application event log.
+ fprintf(stderr, "warning: removing Breakpad handler out of order\n");
+ vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin();
+ while (iterator != handler_stack_->end()) {
+ if (*iterator == this) {
+ iterator = handler_stack_->erase(iterator);
+ } else {
+ ++iterator;
+ }
+ }
+ }
+
+ if (handler_stack_->empty()) {
+ // When destroying the last ExceptionHandler that installed a handler,
+ // clean up the handler stack.
+ delete handler_stack_;
+ handler_stack_ = NULL;
+ }
+
+ LeaveCriticalSection(&handler_stack_critical_section_);
+ }
+
+ // Some of the objects were only initialized if out of process
+ // registration was not done.
+ if (!IsOutOfProcess()) {
+#ifdef BREAKPAD_NO_TERMINATE_THREAD
+ // Clean up the handler thread and synchronization primitives. The handler
+ // thread is either waiting on the semaphore to handle a crash or it is
+ // handling a crash. Coming out of the wait is fast but wait more in the
+ // eventuality a crash is handled. This compilation option results in a
+ // deadlock if the exception handler is destroyed while executing code
+ // inside DllMain.
+ is_shutdown_ = true;
+ ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
+ WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs);
+#else
+ TerminateThread(handler_thread_, 1);
+#endif // BREAKPAD_NO_TERMINATE_THREAD
+
+ CloseHandle(handler_thread_);
+ handler_thread_ = NULL;
+ DeleteCriticalSection(&handler_critical_section_);
+ CloseHandle(handler_start_semaphore_);
+ CloseHandle(handler_finish_semaphore_);
+ }
+
+ // There is a race condition in the code below: if this instance is
+ // deleting the static critical section and a new instance of the class
+ // is created, then there is a possibility that the critical section be
+ // initialized while the same critical section is being deleted. Given the
+ // usage pattern for the code, this race condition is unlikely to hit, but it
+ // is a race condition nonetheless.
+ if (InterlockedDecrement(&instance_count_) == 0) {
+ DeleteCriticalSection(&handler_stack_critical_section_);
+ }
+}
+
+bool ExceptionHandler::RequestUpload(DWORD crash_id) {
+ return crash_generation_client_->RequestUpload(crash_id);
+}
+
+// static
+DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) {
+ ExceptionHandler* self = reinterpret_cast<ExceptionHandler *>(lpParameter);
+ assert(self);
+ assert(self->handler_start_semaphore_ != NULL);
+ assert(self->handler_finish_semaphore_ != NULL);
+
+ while (true) {
+ if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) ==
+ WAIT_OBJECT_0) {
+ // Perform the requested action.
+ if (self->is_shutdown_) {
+ // The instance of the exception handler is being destroyed.
+ break;
+ } else {
+ self->handler_return_value_ =
+ self->WriteMinidumpWithException(self->requesting_thread_id_,
+ self->exception_info_,
+ self->assertion_);
+ }
+
+ // Allow the requesting thread to proceed.
+ ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL);
+ }
+ }
+
+ // This statement is not reached when the thread is unconditionally
+ // terminated by the ExceptionHandler destructor.
+ return 0;
+}
+
+// HandleException and HandleInvalidParameter must create an
+// AutoExceptionHandler object to maintain static state and to determine which
+// ExceptionHandler instance to use. The constructor locates the correct
+// instance, and makes it available through get_handler(). The destructor
+// restores the state in effect prior to allocating the AutoExceptionHandler.
+class AutoExceptionHandler {
+ public:
+ AutoExceptionHandler() {
+ // Increment handler_stack_index_ so that if another Breakpad handler is
+ // registered using this same HandleException function, and it needs to be
+ // called while this handler is running (either because this handler
+ // declines to handle the exception, or an exception occurs during
+ // handling), HandleException will find the appropriate ExceptionHandler
+ // object in handler_stack_ to deliver the exception to.
+ //
+ // Because handler_stack_ is addressed in reverse (as |size - index|),
+ // preincrementing handler_stack_index_ avoids needing to subtract 1 from
+ // the argument to |at|.
+ //
+ // The index is maintained instead of popping elements off of the handler
+ // stack and pushing them at the end of this method. This avoids ruining
+ // the order of elements in the stack in the event that some other thread
+ // decides to manipulate the handler stack (such as creating a new
+ // ExceptionHandler object) while an exception is being handled.
+ EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
+ handler_ = ExceptionHandler::handler_stack_->at(
+ ExceptionHandler::handler_stack_->size() -
+ ++ExceptionHandler::handler_stack_index_);
+
+ // In case another exception occurs while this handler is doing its thing,
+ // it should be delivered to the previous filter.
+ SetUnhandledExceptionFilter(handler_->previous_filter_);
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ _set_invalid_parameter_handler(handler_->previous_iph_);
+#endif // _MSC_VER >= 1400
+ _set_purecall_handler(handler_->previous_pch_);
+ }
+
+ ~AutoExceptionHandler() {
+ // Put things back the way they were before entering this handler.
+ SetUnhandledExceptionFilter(ExceptionHandler::HandleException);
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter);
+#endif // _MSC_VER >= 1400
+ _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall);
+
+ --ExceptionHandler::handler_stack_index_;
+ LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
+ }
+
+ ExceptionHandler* get_handler() const { return handler_; }
+
+ private:
+ ExceptionHandler* handler_;
+};
+
+// static
+LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This
+ // logic will short-circuit before calling WriteMinidumpOnHandlerThread,
+ // allowing something else to handle the breakpoint without incurring the
+ // overhead transitioning to and from the handler thread. This behavior
+ // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions.
+ DWORD code = exinfo->ExceptionRecord->ExceptionCode;
+ LONG action;
+ bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
+ (code == EXCEPTION_SINGLE_STEP);
+
+ bool success = false;
+
+ if (!is_debug_exception ||
+ current_handler->get_handle_debug_exceptions()) {
+ // If out-of-proc crash handler client is available, we have to use that
+ // to generate dump and we cannot fall back on in-proc dump generation
+ // because we never prepared for an in-proc dump generation
+
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (current_handler->IsOutOfProcess()) {
+ success = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(),
+ exinfo,
+ NULL);
+ } else {
+ success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL);
+ }
+ }
+
+ // The handler fully handled the exception. Returning
+ // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually
+ // results in the application being terminated.
+ //
+ // Note: If the application was launched from within the Cygwin
+ // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the
+ // application to be restarted.
+ if (success) {
+ action = EXCEPTION_EXECUTE_HANDLER;
+ } else {
+ // There was an exception, it was a breakpoint or something else ignored
+ // above, or it was passed to the handler, which decided not to handle it.
+ // This could be because the filter callback didn't want it, because
+ // minidump writing failed for some reason, or because the post-minidump
+ // callback function indicated failure. Give the previous handler a
+ // chance to do something with the exception. If there is no previous
+ // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger
+ // or native "crashed" dialog to handle the exception.
+ if (current_handler->previous_filter_) {
+ action = current_handler->previous_filter_(exinfo);
+ } else {
+ action = EXCEPTION_CONTINUE_SEARCH;
+ }
+ }
+
+ return action;
+}
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+// static
+void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression,
+ const wchar_t* function,
+ const wchar_t* file,
+ unsigned int line,
+ uintptr_t reserved) {
+ // This is an invalid parameter, not an exception. It's safe to play with
+ // sprintf here.
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ MDRawAssertionInfo assertion;
+ memset(&assertion, 0, sizeof(assertion));
+ _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.expression),
+ sizeof(assertion.expression) / sizeof(assertion.expression[0]),
+ _TRUNCATE, L"%s", expression);
+ _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.function),
+ sizeof(assertion.function) / sizeof(assertion.function[0]),
+ _TRUNCATE, L"%s", function);
+ _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.file),
+ sizeof(assertion.file) / sizeof(assertion.file[0]),
+ _TRUNCATE, L"%s", file);
+ assertion.line = line;
+ assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER;
+
+ // Make up an exception record for the current thread and CPU context
+ // to make it possible for the crash processor to classify these
+ // as do regular crashes, and to make it humane for developers to
+ // analyze them.
+ EXCEPTION_RECORD exception_record = {};
+ CONTEXT exception_context = {};
+ EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+ ::RtlCaptureContext(&exception_context);
+
+ exception_record.ExceptionCode = STATUS_INVALID_PARAMETER;
+
+ // We store pointers to the the expression and function strings,
+ // and the line as exception parameters to make them easy to
+ // access by the developer on the far side.
+ exception_record.NumberParameters = 3;
+ exception_record.ExceptionInformation[0] =
+ reinterpret_cast<ULONG_PTR>(&assertion.expression);
+ exception_record.ExceptionInformation[1] =
+ reinterpret_cast<ULONG_PTR>(&assertion.file);
+ exception_record.ExceptionInformation[2] = assertion.line;
+
+ bool success = false;
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (current_handler->IsOutOfProcess()) {
+ success = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(),
+ &exception_ptrs,
+ &assertion);
+ } else {
+ success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs,
+ &assertion);
+ }
+
+ if (!success) {
+ if (current_handler->previous_iph_) {
+ // The handler didn't fully handle the exception. Give it to the
+ // previous invalid parameter handler.
+ current_handler->previous_iph_(expression,
+ function,
+ file,
+ line,
+ reserved);
+ } else {
+ // If there's no previous handler, pass the exception back in to the
+ // invalid parameter handler's core. That's the routine that called this
+ // function, but now, since this function is no longer registered (and in
+ // fact, no function at all is registered), this will result in the
+ // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson.
+ // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes
+ // more information through. In non-debug builds, it is not available,
+ // so fall back to using _invalid_parameter_noinfo. See invarg.c in the
+ // CRT source.
+#ifdef _DEBUG
+ _invalid_parameter(expression, function, file, line, reserved);
+#else // _DEBUG
+ _invalid_parameter_noinfo();
+#endif // _DEBUG
+ }
+ }
+
+ // The handler either took care of the invalid parameter problem itself,
+ // or passed it on to another handler. "Swallow" it by exiting, paralleling
+ // the behavior of "swallowing" exceptions.
+ exit(0);
+}
+#endif // _MSC_VER >= 1400
+
+// static
+void ExceptionHandler::HandlePureVirtualCall() {
+ // This is an pure virtual function call, not an exception. It's safe to
+ // play with sprintf here.
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ MDRawAssertionInfo assertion;
+ memset(&assertion, 0, sizeof(assertion));
+ assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
+
+ // Make up an exception record for the current thread and CPU context
+ // to make it possible for the crash processor to classify these
+ // as do regular crashes, and to make it humane for developers to
+ // analyze them.
+ EXCEPTION_RECORD exception_record = {};
+ CONTEXT exception_context = {};
+ EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+ ::RtlCaptureContext(&exception_context);
+
+ exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
+
+ // We store pointers to the the expression and function strings,
+ // and the line as exception parameters to make them easy to
+ // access by the developer on the far side.
+ exception_record.NumberParameters = 3;
+ exception_record.ExceptionInformation[0] =
+ reinterpret_cast<ULONG_PTR>(&assertion.expression);
+ exception_record.ExceptionInformation[1] =
+ reinterpret_cast<ULONG_PTR>(&assertion.file);
+ exception_record.ExceptionInformation[2] = assertion.line;
+
+ bool success = false;
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+
+ if (current_handler->IsOutOfProcess()) {
+ success = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(),
+ &exception_ptrs,
+ &assertion);
+ } else {
+ success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs,
+ &assertion);
+ }
+
+ if (!success) {
+ if (current_handler->previous_pch_) {
+ // The handler didn't fully handle the exception. Give it to the
+ // previous purecall handler.
+ current_handler->previous_pch_();
+ } else {
+ // If there's no previous handler, return and let _purecall handle it.
+ // This will just put up an assertion dialog.
+ return;
+ }
+ }
+
+ // The handler either took care of the invalid parameter problem itself,
+ // or passed it on to another handler. "Swallow" it by exiting, paralleling
+ // the behavior of "swallowing" exceptions.
+ exit(0);
+}
+
+bool ExceptionHandler::WriteMinidumpOnHandlerThread(
+ EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) {
+ EnterCriticalSection(&handler_critical_section_);
+
+ // There isn't much we can do if the handler thread
+ // was not successfully created.
+ if (handler_thread_ == NULL) {
+ LeaveCriticalSection(&handler_critical_section_);
+ return false;
+ }
+
+ // The handler thread should only be created when the semaphores are valid.
+ assert(handler_start_semaphore_ != NULL);
+ assert(handler_finish_semaphore_ != NULL);
+
+ // Set up data to be passed in to the handler thread.
+ requesting_thread_id_ = GetCurrentThreadId();
+ exception_info_ = exinfo;
+ assertion_ = assertion;
+
+ // This causes the handler thread to call WriteMinidumpWithException.
+ ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
+
+ // Wait until WriteMinidumpWithException is done and collect its return value.
+ WaitForSingleObject(handler_finish_semaphore_, INFINITE);
+ bool status = handler_return_value_;
+
+ // Clean up.
+ requesting_thread_id_ = 0;
+ exception_info_ = NULL;
+ assertion_ = NULL;
+
+ LeaveCriticalSection(&handler_critical_section_);
+
+ return status;
+}
+
+bool ExceptionHandler::WriteMinidump() {
+ // Make up an exception record for the current thread and CPU context
+ // to make it possible for the crash processor to classify these
+ // as do regular crashes, and to make it humane for developers to
+ // analyze them.
+ EXCEPTION_RECORD exception_record = {};
+ CONTEXT exception_context = {};
+ EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+ ::RtlCaptureContext(&exception_context);
+ exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
+
+ return WriteMinidumpForException(&exception_ptrs);
+}
+
+bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) {
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (IsOutOfProcess()) {
+ return WriteMinidumpWithException(GetCurrentThreadId(),
+ exinfo,
+ NULL);
+ }
+
+ bool success = WriteMinidumpOnHandlerThread(exinfo, NULL);
+ UpdateNextID();
+ return success;
+}
+
+// static
+bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
+ MinidumpCallback callback,
+ void* callback_context) {
+ ExceptionHandler handler(dump_path, NULL, callback, callback_context,
+ HANDLER_NONE);
+ return handler.WriteMinidump();
+}
+
+bool ExceptionHandler::WriteMinidumpWithException(
+ DWORD requesting_thread_id,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion) {
+ // Give user code a chance to approve or prevent writing a minidump. If the
+ // filter returns false, don't handle the exception at all. If this method
+ // was called as a result of an exception, returning false will cause
+ // HandleException to call any previous handler or return
+ // EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear
+ // as though this handler were not present at all.
+ if (filter_ && !filter_(callback_context_, exinfo, assertion)) {
+ return false;
+ }
+
+ bool success = false;
+ if (IsOutOfProcess()) {
+ success = crash_generation_client_->RequestDump(exinfo, assertion);
+ } else {
+ if (minidump_write_dump_) {
+ HANDLE dump_file = CreateFile(next_minidump_path_c_,
+ GENERIC_WRITE,
+ 0, // no sharing
+ NULL,
+ CREATE_NEW, // fail if exists
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (dump_file != INVALID_HANDLE_VALUE) {
+ MINIDUMP_EXCEPTION_INFORMATION except_info;
+ except_info.ThreadId = requesting_thread_id;
+ except_info.ExceptionPointers = exinfo;
+ except_info.ClientPointers = FALSE;
+
+ // Add an MDRawBreakpadInfo stream to the minidump, to provide
+ // additional information about the exception handler to the Breakpad
+ // processor. The information will help the processor determine which
+ // threads are relevant. The Breakpad processor does not require this
+ // information but can function better with Breakpad-generated dumps
+ // when it is present. The native debugger is not harmed by the
+ // presence of this information.
+ MDRawBreakpadInfo breakpad_info;
+ breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
+ breakpad_info.dump_thread_id = GetCurrentThreadId();
+ breakpad_info.requesting_thread_id = requesting_thread_id;
+
+ // Leave room in user_stream_array for a possible assertion info stream.
+ MINIDUMP_USER_STREAM user_stream_array[2];
+ user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
+ user_stream_array[0].BufferSize = sizeof(breakpad_info);
+ user_stream_array[0].Buffer = &breakpad_info;
+
+ MINIDUMP_USER_STREAM_INFORMATION user_streams;
+ user_streams.UserStreamCount = 1;
+ user_streams.UserStreamArray = user_stream_array;
+
+ if (assertion) {
+ user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
+ user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
+ user_stream_array[1].Buffer = assertion;
+ ++user_streams.UserStreamCount;
+ }
+
+ MINIDUMP_CALLBACK_INFORMATION callback;
+ MinidumpCallbackContext context;
+ MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
+ // Older versions of DbgHelp.dll don't correctly put the memory around
+ // the faulting instruction pointer into the minidump. This
+ // callback will ensure that it gets included.
+ if (exinfo) {
+ // Find a memory region of 256 bytes centered on the
+ // faulting instruction pointer.
+ const ULONG64 instruction_pointer =
+#if defined(_M_IX86)
+ exinfo->ContextRecord->Eip;
+#elif defined(_M_AMD64)
+ exinfo->ContextRecord->Rip;
+#else
+#error Unsupported platform
+#endif
+
+ MEMORY_BASIC_INFORMATION info;
+ if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer),
+ &info,
+ sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
+ info.State == MEM_COMMIT) {
+ // Attempt to get 128 bytes before and after the instruction
+ // pointer, but settle for whatever's available up to the
+ // boundaries of the memory region.
+ const ULONG64 kIPMemorySize = 256;
+ context.memory_base =
+ (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
+ instruction_pointer - (kIPMemorySize / 2));
+ ULONG64 end_of_range =
+ (std::min)(instruction_pointer + (kIPMemorySize / 2),
+ reinterpret_cast<ULONG64>(info.BaseAddress)
+ + info.RegionSize);
+ context.memory_size =
+ static_cast<ULONG>(end_of_range - context.memory_base);
+
+ context.finished = false;
+ callback.CallbackRoutine = MinidumpWriteDumpCallback;
+ callback.CallbackParam = reinterpret_cast<void*>(&context);
+ callback_pointer = &callback;
+ }
+ }
+
+ // The explicit comparison to TRUE avoids a warning (C4800).
+ success = (minidump_write_dump_(GetCurrentProcess(),
+ GetCurrentProcessId(),
+ dump_file,
+ dump_type_,
+ exinfo ? &except_info : NULL,
+ &user_streams,
+ callback_pointer) == TRUE);
+
+ CloseHandle(dump_file);
+ }
+ }
+ }
+
+ if (callback_) {
+ // TODO(munjal): In case of out-of-process dump generation, both
+ // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process
+ // scenario, the server process ends up creating the dump path and dump
+ // id so they are not known to the client.
+ success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
+ exinfo, assertion, success);
+ }
+
+ return success;
+}
+
+// static
+BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
+ PVOID context,
+ const PMINIDUMP_CALLBACK_INPUT callback_input,
+ PMINIDUMP_CALLBACK_OUTPUT callback_output) {
+ switch (callback_input->CallbackType) {
+ case MemoryCallback: {
+ MinidumpCallbackContext* callback_context =
+ reinterpret_cast<MinidumpCallbackContext*>(context);
+ if (callback_context->finished)
+ return FALSE;
+
+ // Include the specified memory region.
+ callback_output->MemoryBase = callback_context->memory_base;
+ callback_output->MemorySize = callback_context->memory_size;
+ callback_context->finished = true;
+ return TRUE;
+ }
+
+ // Include all modules.
+ case IncludeModuleCallback:
+ case ModuleCallback:
+ return TRUE;
+
+ // Include all threads.
+ case IncludeThreadCallback:
+ case ThreadCallback:
+ return TRUE;
+
+ // Stop receiving cancel callbacks.
+ case CancelCallback:
+ callback_output->CheckCancel = FALSE;
+ callback_output->Cancel = FALSE;
+ return TRUE;
+ }
+ // Ignore other callback types.
+ return FALSE;
+}
+
+void ExceptionHandler::UpdateNextID() {
+ assert(uuid_create_);
+ UUID id = {0};
+ if (uuid_create_) {
+ uuid_create_(&id);
+ }
+ next_minidump_id_ = GUIDString::GUIDToWString(&id);
+ next_minidump_id_c_ = next_minidump_id_.c_str();
+
+ wchar_t minidump_path[MAX_PATH];
+ swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp",
+ dump_path_c_, next_minidump_id_c_);
+
+ // remove when VC++7.1 is no longer supported
+ minidump_path[MAX_PATH - 1] = L'\0';
+
+ next_minidump_path_ = minidump_path;
+ next_minidump_path_c_ = next_minidump_path_.c_str();
+}
+
+} // namespace google_breakpad