Windows exception handler does not survive stack overflows (#34). r=brian,
thanks also to darin - All minidump writing is now done on a dedicated thread. When a stack overflow exception occurs, the only work that needs to be done on the exception thread will easily fit within the guard page. http://groups.google.com/group/airbag-dev/browse_thread/thread/3935e339d8354a75 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@57 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
		@@ -37,6 +37,14 @@
 | 
			
		||||
namespace google_airbag {
 | 
			
		||||
 | 
			
		||||
ExceptionHandler *ExceptionHandler::current_handler_ = NULL;
 | 
			
		||||
HANDLE ExceptionHandler::handler_thread_ = NULL;
 | 
			
		||||
CRITICAL_SECTION ExceptionHandler::handler_critical_section_;
 | 
			
		||||
HANDLE ExceptionHandler::handler_start_semaphore_ = NULL;
 | 
			
		||||
HANDLE ExceptionHandler::handler_finish_semaphore_ = NULL;
 | 
			
		||||
ExceptionHandler *ExceptionHandler::requesting_handler_ = NULL;
 | 
			
		||||
DWORD ExceptionHandler::requesting_thread_id_ = 0;
 | 
			
		||||
EXCEPTION_POINTERS *ExceptionHandler::exception_info_ = NULL;
 | 
			
		||||
bool ExceptionHandler::handler_return_value_ = false;
 | 
			
		||||
 | 
			
		||||
ExceptionHandler::ExceptionHandler(const wstring &dump_path,
 | 
			
		||||
                                   MinidumpCallback callback,
 | 
			
		||||
@@ -46,6 +54,22 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path,
 | 
			
		||||
      dump_path_(dump_path), dbghelp_module_(NULL),
 | 
			
		||||
      minidump_write_dump_(NULL), previous_handler_(current_handler_),
 | 
			
		||||
      previous_filter_(NULL) {
 | 
			
		||||
  if (!handler_thread_) {
 | 
			
		||||
    // The first time an ExceptionHandler is created, set up the handler
 | 
			
		||||
    // thread and the synchronization primitives.
 | 
			
		||||
    InitializeCriticalSection(&handler_critical_section_);
 | 
			
		||||
    handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
 | 
			
		||||
    handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
 | 
			
		||||
 | 
			
		||||
    DWORD thread_id;
 | 
			
		||||
    handler_thread_ = CreateThread(NULL,         // lpThreadAttributes
 | 
			
		||||
                                   64 * 1024,    // dwStackSize
 | 
			
		||||
                                   ExceptionHandlerThreadMain,
 | 
			
		||||
                                   NULL,         // lpParameter
 | 
			
		||||
                                   0,            // dwCreationFlags
 | 
			
		||||
                                   &thread_id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  UpdateNextID();
 | 
			
		||||
  dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
 | 
			
		||||
  if (dbghelp_module_) {
 | 
			
		||||
@@ -66,18 +90,72 @@ ExceptionHandler::~ExceptionHandler() {
 | 
			
		||||
    SetUnhandledExceptionFilter(previous_filter_);
 | 
			
		||||
    current_handler_ = previous_handler_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (previous_handler_ == NULL) {
 | 
			
		||||
    // When destroying the last ExceptionHandler, clean up the handler thread
 | 
			
		||||
    // and synchronization primitives.
 | 
			
		||||
    TerminateThread(handler_thread_, 1);
 | 
			
		||||
    handler_thread_ = NULL;
 | 
			
		||||
    DeleteCriticalSection(&handler_critical_section_);
 | 
			
		||||
    CloseHandle(handler_start_semaphore_);
 | 
			
		||||
    handler_start_semaphore_ = NULL;
 | 
			
		||||
    CloseHandle(handler_finish_semaphore_);
 | 
			
		||||
    handler_finish_semaphore_ = NULL;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
DWORD ExceptionHandler::ExceptionHandlerThreadMain(void *lpParameter) {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (WaitForSingleObject(handler_start_semaphore_, INFINITE) ==
 | 
			
		||||
        WAIT_OBJECT_0) {
 | 
			
		||||
      // Perform the requested action.
 | 
			
		||||
      handler_return_value_ = requesting_handler_->WriteMinidumpWithException(
 | 
			
		||||
          requesting_thread_id_, exception_info_);
 | 
			
		||||
 | 
			
		||||
      // Allow the requesting thread to proceed.
 | 
			
		||||
      ReleaseSemaphore(handler_finish_semaphore_, 1, NULL);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Not reached.  This thread will be terminated by ExceptionHandler's
 | 
			
		||||
  // destructor.
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) {
 | 
			
		||||
  if (!current_handler_->WriteMinidumpWithException(exinfo)) {
 | 
			
		||||
    return EXCEPTION_CONTINUE_SEARCH;
 | 
			
		||||
  }
 | 
			
		||||
  return EXCEPTION_EXECUTE_HANDLER;
 | 
			
		||||
  return current_handler_->WriteMinidumpOnHandlerThread(exinfo) ?
 | 
			
		||||
      EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ExceptionHandler::WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS *exinfo) {
 | 
			
		||||
  EnterCriticalSection(&handler_critical_section_);
 | 
			
		||||
 | 
			
		||||
  // Set up data to be passed in to the handler thread.
 | 
			
		||||
  requesting_handler_ = this;
 | 
			
		||||
  requesting_thread_id_ = GetCurrentThreadId();
 | 
			
		||||
  exception_info_ = exinfo;
 | 
			
		||||
 | 
			
		||||
  // 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_handler_ = NULL;
 | 
			
		||||
  requesting_thread_id_ = 0;
 | 
			
		||||
  exception_info_ = NULL;
 | 
			
		||||
 | 
			
		||||
  LeaveCriticalSection(&handler_critical_section_);
 | 
			
		||||
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ExceptionHandler::WriteMinidump() {
 | 
			
		||||
  bool success = WriteMinidumpWithException(NULL);
 | 
			
		||||
  bool success = WriteMinidumpOnHandlerThread(NULL);
 | 
			
		||||
  UpdateNextID();
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
@@ -90,7 +168,8 @@ bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
 | 
			
		||||
  return handler.WriteMinidump();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) {
 | 
			
		||||
bool ExceptionHandler::WriteMinidumpWithException(DWORD requesting_thread_id,
 | 
			
		||||
                                                  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_.c_str());
 | 
			
		||||
@@ -106,16 +185,18 @@ bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) {
 | 
			
		||||
                                  NULL);
 | 
			
		||||
    if (dump_file != INVALID_HANDLE_VALUE) {
 | 
			
		||||
      MINIDUMP_EXCEPTION_INFORMATION except_info;
 | 
			
		||||
      except_info.ThreadId = GetCurrentThreadId();
 | 
			
		||||
      except_info.ThreadId = requesting_thread_id;
 | 
			
		||||
      except_info.ExceptionPointers = exinfo;
 | 
			
		||||
      except_info.ClientPointers = FALSE;
 | 
			
		||||
 | 
			
		||||
      // TODO(mmentovai): include IDs of handler and requesting threads.
 | 
			
		||||
 | 
			
		||||
      // The explicit comparison to TRUE avoids a warning (C4800).
 | 
			
		||||
      success = (minidump_write_dump_(GetCurrentProcess(),
 | 
			
		||||
                                      GetCurrentProcessId(),
 | 
			
		||||
                                      dump_file,
 | 
			
		||||
                                      MiniDumpNormal,
 | 
			
		||||
                                      &except_info,
 | 
			
		||||
                                      exinfo ? &except_info : NULL,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      NULL) == TRUE);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user