Allow exception handler callbacks more flexibility (#81). r=bryner
- Provide an optional filter callback that gets triggered before attempting
to write a dump, to give client code a chance to refuse handling early
in the process.
- Allow exceptions that are unhandled by Airbag (due to filter callback or
dump callback return value, or failure to write a dump) to be passed to the
previous handler or to the system.
- In order to pass exceptions unhandled by the topmost Airbag handler to
lower handlers, fix up the stacking of ExceptionHandler objects, and give
each ExceptionHandler object its own thread (like the Mac implementation)
to avoid deadlock.
- Provide a dump_path argument to callbacks, as requested by developers and
already implemented in the Mac handler.
- Avoid calling c_str in exception handler code (#90).
http://groups.google.com/group/airbag-dev/browse_thread/thread/4771825ced38a84c
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@79 4c0a9323-5329-0410-9bdc-e9ce6186880e
diff --git a/src/client/windows/handler/exception_handler.h b/src/client/windows/handler/exception_handler.h
index b24a37b..f5ca31b 100644
--- a/src/client/windows/handler/exception_handler.h
+++ b/src/client/windows/handler/exception_handler.h
@@ -67,27 +67,57 @@
#pragma warning( disable : 4530 )
#include <string>
+#include <vector>
namespace google_airbag {
+using std::vector;
using std::wstring;
class ExceptionHandler {
public:
+ // A callback function to run before Airbag performs any substantial
+ // processing of an exception. A FilterCallback is called before writing
+ // a minidump. context is the parameter supplied by the user as
+ // callback_context when the handler was created.
+ //
+ // If a FilterCallback returns true, Airbag will continue processing,
+ // attempting to write a minidump. If a FilterCallback returns false, Airbag
+ // will immediately report the exception as unhandled without writing a
+ // minidump, allowing another handler the opportunity to handle it.
+ typedef bool (*FilterCallback)(void *context);
+
// 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 <dump_path>\<minidump_id>.dmp. succeeded indicates whether
- // a minidump file was successfully written.
- typedef void (*MinidumpCallback)(const wstring &minidump_id,
- void *context, bool succeeded);
+ // file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
+ // by the user as callback_context when the handler was created. succeeded
+ // indicates whether a minidump file was successfully written.
+ //
+ // If an exception occurred and the callback returns true, Airbag will treat
+ // the exception as fully-handled, suppressing any other handlers from being
+ // notified of the exception. If the callback returns false, Airbag will
+ // treat the exception as unhandled, and allow another handler to handle it.
+ // If there are no other handlers, Airbag will report the exception to the
+ // system as unhandled, allowing a debugger or native crash dialog the
+ // opportunity to handle the exception. Most callback implementations
+ // should normally return the value of |succeeded|, or when they wish to
+ // not report an exception of handled, false. Callbacks will rarely want to
+ // return true directly (unless |succeeded| is true).
+ typedef bool (*MinidumpCallback)(const wchar_t *dump_path,
+ const wchar_t *minidump_id,
+ void *context,
+ bool succeeded);
// Creates a new ExceptionHandler instance to handle writing minidumps.
+ // Before writing a minidump, the optional filter callback will be called.
+ // Its return value determines whether or not Airbag should write a minidump.
// 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,
+ ExceptionHandler(const wstring &dump_path,
+ FilterCallback filter, MinidumpCallback callback,
void *callback_context, bool install_handler);
~ExceptionHandler();
@@ -95,6 +125,7 @@
wstring dump_path() const { return dump_path_; }
void set_dump_path(const wstring &dump_path) {
dump_path_ = dump_path;
+ dump_path_c_ = dump_path_.c_str();
UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_.
}
@@ -147,56 +178,93 @@
// path of the next minidump to be written in next_minidump_path_.
void UpdateNextID();
+ FilterCallback filter_;
MinidumpCallback callback_;
void *callback_context_;
+ // The directory in which a minidump will be written, set by the dump_path
+ // argument to the constructor, or set_dump_path.
wstring dump_path_;
+
+ // The basename of the next minidump to be written, without the extension.
wstring next_minidump_id_;
+
+ // The full pathname of the next minidump to be written, including the file
+ // extension.
wstring next_minidump_path_;
+ // Pointers to C-string representations of the above. These are set when
+ // the above wstring versions are set in order to avoid calling c_str during
+ // an exception, as c_str may attempt to allocate heap memory. These
+ // pointers are not owned by the ExceptionHandler object, but their lifetimes
+ // should be equivalent to the lifetimes of the associated wstring, provided
+ // that the wstrings are not altered.
+ const wchar_t *dump_path_c_;
+ const wchar_t *next_minidump_id_c_;
+ const wchar_t *next_minidump_path_c_;
+
HMODULE dbghelp_module_;
MiniDumpWriteDump_type minidump_write_dump_;
- ExceptionHandler *previous_handler_; // current_handler_ before us
+ // True if the ExceptionHandler installed an unhandled exception filter
+ // when created (with an install_handler parameter set to true).
+ bool installed_handler_;
+
+ // When installed_handler_ is true, previous_filter_ is the unhandled
+ // exception filter that was set prior to installing ExceptionHandler as
+ // the unhandled exception filter and pointing it to |this|. NULL indicates
+ // that there is no previous unhandled exception filter.
LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_;
-
- // the currently-installed ExceptionHandler, of which there can be only 1
- static ExceptionHandler *current_handler_;
-
- // The exception handler thread, if one has been created.
- static HANDLE handler_thread_;
+
+ // The exception handler thread.
+ HANDLE handler_thread_;
// The critical section enforcing the requirement that only one exception be
- // handled at a time.
- static CRITICAL_SECTION handler_critical_section_;
+ // handled by a handler at a time.
+ CRITICAL_SECTION handler_critical_section_;
// Semaphores used to move exception handling between the exception thread
// and the handler thread. handler_start_semaphore_ is signalled by the
// exception thread to wake up the handler thread when an exception occurs.
// handler_finish_semaphore_ is signalled by the handler thread to wake up
// the exception thread when handling is complete.
- static HANDLE handler_start_semaphore_;
- static HANDLE handler_finish_semaphore_;
+ HANDLE handler_start_semaphore_;
+ HANDLE handler_finish_semaphore_;
- // The next 3 fields are static data passed from the requesting thread to
+ // The next 2 fields contain data passed from the requesting thread to
// the handler thread.
- // The ExceptionHandler through which a request to write a dump was routed.
- // This will be the same as current_handler_ for exceptions, but
- // user-requested dumps may be routed through any live ExceptionHandler.
- static ExceptionHandler *requesting_handler_;
-
// The thread ID of the thread requesting the dump (either the exception
// thread or any other thread that called WriteMinidump directly).
- static DWORD requesting_thread_id_;
+ DWORD requesting_thread_id_;
// The exception info passed to the exception handler on the exception
// thread, if an exception occurred. NULL for user-requested dumps.
- static EXCEPTION_POINTERS *exception_info_;
+ EXCEPTION_POINTERS *exception_info_;
// The return value of the handler, passed from the handler thread back to
// the requesting thread.
- static bool handler_return_value_;
+ bool handler_return_value_;
+
+ // A stack of ExceptionHandler objects that have installed unhandled
+ // exception filters. This vector is used by HandleException to determine
+ // which ExceptionHandler object to route an exception to. When an
+ // ExceptionHandler is created with install_handler true, it will append
+ // itself to this list.
+ static vector<ExceptionHandler *> *handler_stack_;
+
+ // The index of the ExceptionHandler in handler_stack_ that will handle the
+ // next exception. Note that 0 means the last entry in handler_stack_, 1
+ // means the next-to-last entry, and so on. This is used by HandleException
+ // to support multiple stacked Airbag handlers.
+ static LONG handler_stack_index_;
+
+ // handler_stack_critical_section_ guards operations on handler_stack_ and
+ // handler_stack_index_.
+ static CRITICAL_SECTION handler_stack_critical_section_;
+
+ // True when handler_stack_critical_section_ has been initialized.
+ static bool handler_stack_critical_section_initialized_;
// disallow copy ctor and operator=
explicit ExceptionHandler(const ExceptionHandler &);