From 1217c1f898457689024c6d0f7448442e7d964f86 Mon Sep 17 00:00:00 2001 From: bryner Date: Wed, 27 Sep 2006 01:00:32 +0000 Subject: Initial version of Windows exception handler and crash report sender classes (#31). r=mmentovai. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@36 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/windows/handler/exception_handler.cc | 148 +++++++++++++++++ src/client/windows/handler/exception_handler.h | 139 ++++++++++++++++ .../windows/handler/exception_handler.vcproj | 175 +++++++++++++++++++++ 3 files changed, 462 insertions(+) create mode 100644 src/client/windows/handler/exception_handler.cc create mode 100644 src/client/windows/handler/exception_handler.h create mode 100644 src/client/windows/handler/exception_handler.vcproj (limited to 'src/client/windows/handler') diff --git a/src/client/windows/handler/exception_handler.cc b/src/client/windows/handler/exception_handler.cc new file mode 100644 index 00000000..ba5c2653 --- /dev/null +++ b/src/client/windows/handler/exception_handler.cc @@ -0,0 +1,148 @@ +// 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 + +#include + +#include "client/windows/handler/exception_handler.h" + +namespace google_airbag { + +ExceptionHandler *ExceptionHandler::current_handler_ = NULL; + +ExceptionHandler::ExceptionHandler(const wstring &dump_path, + MinidumpCallback callback, + void *callback_context, + bool install_handler) + : callback_(callback), callback_context_(callback_context), + dump_path_(dump_path), next_minidump_id_(NULL), + dbghelp_module_(NULL), minidump_write_dump_(NULL), + previous_handler_(current_handler_), previous_filter_(NULL) { + UpdateNextID(); + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); + if (dbghelp_module_) { + minidump_write_dump_ = reinterpret_cast( + GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + } + if (install_handler) { + previous_filter_ = SetUnhandledExceptionFilter(HandleException); + current_handler_ = this; + } +} + +ExceptionHandler::~ExceptionHandler() { + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + if (next_minidump_id_) { + RpcStringFree(&next_minidump_id_); + } + if (current_handler_ == this) { + SetUnhandledExceptionFilter(previous_filter_); + current_handler_ = previous_handler_; + } +} + +// static +LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) { + if (!current_handler_->WriteMinidumpWithException(exinfo)) { + return EXCEPTION_CONTINUE_SEARCH; + } + return EXCEPTION_EXECUTE_HANDLER; +} + +bool ExceptionHandler::WriteMinidump() { + bool success = WriteMinidumpWithException(NULL); + UpdateNextID(); + return success; +} + +// static +bool ExceptionHandler::WriteMinidump(const wstring &dump_path, + MinidumpCallback callback, + void *callback_context) { + ExceptionHandler handler(dump_path, callback, callback_context, false); + return handler.WriteMinidump(); +} + +bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) { + wchar_t dump_file_name[MAX_PATH]; + swprintf_s(dump_file_name, MAX_PATH, L"%s\\%s.dmp", + dump_path_.c_str(), next_minidump_id_); + + bool success = false; + if (minidump_write_dump_) { + HANDLE dump_file = CreateFile(dump_file_name, + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION except_info; + except_info.ThreadId = GetCurrentThreadId(); + except_info.ExceptionPointers = exinfo; + except_info.ClientPointers = FALSE; + + // The explicit comparison to TRUE avoids a warning (C4800). + success = (minidump_write_dump_(GetCurrentProcess(), + GetCurrentProcessId(), + dump_file, + MiniDumpNormal, + &except_info, + NULL, + NULL) == TRUE); + + CloseHandle(dump_file); + } + } + + if (callback_) { + // This looks nasty, but RPC_WSTR is really just a wide string, + // and there are no "supported" ways to convert them other than casting. + callback_(reinterpret_cast(next_minidump_id_), + callback_context_, success); + } + // TODO(bryner): log an error on failure + + return success; +} + +void ExceptionHandler::UpdateNextID() { + if (next_minidump_id_) { + RpcStringFree(&next_minidump_id_); + } + GUID id; + CoCreateGuid(&id); + UuidToString(&id, &next_minidump_id_); +} + +} // namespace google_airbag diff --git a/src/client/windows/handler/exception_handler.h b/src/client/windows/handler/exception_handler.h new file mode 100644 index 00000000..7acb5853 --- /dev/null +++ b/src/client/windows/handler/exception_handler.h @@ -0,0 +1,139 @@ +// 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. + +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the unqiue id of the minidump. The caller can use this +// id to collect and write additional application state, and to launch an +// external crash-reporting application. +// +// It is important that creation and destruction of ExceptionHandler objects +// be nested cleanly, when using install_handler = true. +// Avoid the following pattern: +// ExceptionHandler *e = new ExceptionHandler(...); +// ExceptionHandler *f = new ExceptionHandler(...); +// delete e; +// This will put the exception filter stack into an inconsistent state. + +#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include + +#include + +namespace google_airbag { + +using std::wstring; + +class ExceptionHandler { + public: + // A callback function to run after the minidump has been written. + // minidump_id is a unique id for the dump, so the minidump + // file is \.dmp. succeeded indicates whether + // a minidump file was successfully written. + typedef void (*MinidumpCallback)(const wstring &minidump_id, + void *context, bool succeeded); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Minidump files will be written to dump_path, and the optional callback + // is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + ExceptionHandler(const wstring &dump_path, MinidumpCallback callback, + void *callback_context, bool install_handler); + ~ExceptionHandler(); + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump(); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const wstring &dump_path, + MinidumpCallback callback, void *callback_context); + + private: + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI *MiniDumpWriteDump_type)( + HANDLE hProcess, + DWORD dwPid, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // This function does the actual writing of a minidump. + bool WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo); + + // Called when an unhandled exception occurs. + static LONG WINAPI HandleException(EXCEPTION_POINTERS *exinfo); + + // Generates a new ID and stores it in next_minidump_id_. + void UpdateNextID(); + + MinidumpCallback callback_; + void *callback_context_; + + wstring dump_path_; + RPC_WSTR next_minidump_id_; + + HMODULE dbghelp_module_; + MiniDumpWriteDump_type minidump_write_dump_; + + ExceptionHandler *previous_handler_; // current_handler_ before us + LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_; + + // the currently-installed ExceptionHandler, of which there can be only 1 + static ExceptionHandler *current_handler_; + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler &); + void operator=(const ExceptionHandler &); +}; + +} // namespace google_airbag + +#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/src/client/windows/handler/exception_handler.vcproj b/src/client/windows/handler/exception_handler.vcproj new file mode 100644 index 00000000..5dce6273 --- /dev/null +++ b/src/client/windows/handler/exception_handler.vcproj @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1