| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | // This module contains the necessary code to register the Breakpad exception | 
 | // handler. This implementation is based on Chrome crash reporting code. See: | 
 | //   - src/components/crash/content/app/breakpad_win.cc | 
 | //   - src/chrome/installer/setup/setup_main.cc | 
 |  | 
 | #include "remoting/base/breakpad.h" | 
 |  | 
 | #include <crtdbg.h> | 
 | #include <windows.h> | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 |  | 
 | #include "base/atomicops.h" | 
 | #include "base/file_version_info.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/win/current_module.h" | 
 | #include "base/win/wrapped_window_proc.h" | 
 | #include "third_party/breakpad/breakpad/src/client/windows/handler/exception_handler.h" | 
 |  | 
 | namespace remoting { | 
 | void InitializeCrashReportingForTest(const wchar_t* pipe_name); | 
 | }  // namespace remoting | 
 |  | 
 | namespace { | 
 |  | 
 | const wchar_t kBreakpadProductName[] = L"Chromoting"; | 
 | const wchar_t kBreakpadVersionEntry[] = L"ver"; | 
 | const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0"; | 
 | const wchar_t kBreakpadProdEntry[] = L"prod"; | 
 | const wchar_t kBreakpadPlatformEntry[] = L"plat"; | 
 | const wchar_t kBreakpadPlatformWin32[] = L"Win32"; | 
 |  | 
 | // The protocol for connecting to the out-of-process Breakpad crash | 
 | // reporter is different for x86-32 and x86-64: the message sizes | 
 | // are different because the message struct contains a pointer.  As | 
 | // a result, there are two different named pipes to connect to.  The | 
 | // 64-bit one is distinguished with an "-x64" suffix. | 
 | #if defined(_WIN64) | 
 | const wchar_t kGoogleUpdatePipeName[] = | 
 |     L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64"; | 
 | #else | 
 | const wchar_t kGoogleUpdatePipeName[] = | 
 |     L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18"; | 
 | #endif | 
 |  | 
 | using base::subtle::AtomicWord; | 
 | using base::subtle::NoBarrier_CompareAndSwap; | 
 |  | 
 | class BreakpadWin { | 
 |  public: | 
 |   BreakpadWin(); | 
 |   ~BreakpadWin(); | 
 |  | 
 |   static BreakpadWin* GetInstance(); | 
 |  | 
 |  private: | 
 |   // Returns the Custom information to be used for crash reporting. | 
 |   google_breakpad::CustomClientInfo* GetCustomInfo(); | 
 |  | 
 |   // This callback is executed when the process has crashed and *before* | 
 |   // the crash dump is created. To prevent duplicate crash reports we | 
 |   // make every thread calling this method, except the very first one, | 
 |   // go to sleep. | 
 |   static bool OnExceptionCallback(void* context, | 
 |                                   EXCEPTION_POINTERS* exinfo, | 
 |                                   MDRawAssertionInfo* assertion); | 
 |  | 
 |   // Crashes the process after generating a dump for the provided exception. | 
 |   // Note that the crash reporter should be initialized before calling this | 
 |   // function for it to do anything. | 
 |   static int OnWindowProcedureException(EXCEPTION_POINTERS* exinfo); | 
 |  | 
 |   // Breakpad's exception handler. | 
 |   std::unique_ptr<google_breakpad::ExceptionHandler> breakpad_; | 
 |  | 
 |   // This flag is used to indicate that an exception is already being handled. | 
 |   volatile AtomicWord handling_exception_; | 
 |  | 
 |   // The testing hook below allows overriding the crash server pipe name. | 
 |   static const wchar_t* pipe_name_; | 
 |  | 
 |   friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*); | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(BreakpadWin); | 
 | }; | 
 |  | 
 | // |LazyInstance| is used to guarantee that the exception handler will be | 
 | // initialized exactly once. | 
 | // N.B. LazyInstance does not allow this to be a static member of the class. | 
 | static base::LazyInstance<BreakpadWin>::Leaky g_instance = | 
 |     LAZY_INSTANCE_INITIALIZER; | 
 |  | 
 | const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName; | 
 |  | 
 | BreakpadWin::BreakpadWin() : handling_exception_(0) { | 
 |   // Disable the message box for assertions. | 
 |   _CrtSetReportMode(_CRT_ASSERT, 0); | 
 |  | 
 |   // Get the alternate dump directory. We use the temp path. | 
 |   // N.B. We don't use base::GetTempDir() here to avoid running more code then | 
 |   //      necessary before crashes can be properly reported. | 
 |   wchar_t temp_directory[MAX_PATH + 1] = { 0 }; | 
 |   DWORD length = GetTempPath(MAX_PATH, temp_directory); | 
 |   if (length == 0) | 
 |     return; | 
 |  | 
 |   // Minidump with stacks, PEB, TEBs and unloaded module list. | 
 |   MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>( | 
 |       MiniDumpWithProcessThreadData | | 
 |       MiniDumpWithUnloadedModules); | 
 |   breakpad_.reset( | 
 |       new google_breakpad::ExceptionHandler( | 
 |           temp_directory, &OnExceptionCallback, NULL, NULL, | 
 |           google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type, | 
 |           pipe_name_, GetCustomInfo())); | 
 |  | 
 |   if (breakpad_->IsOutOfProcess()) { | 
 |     // Tells breakpad to handle breakpoint and single step exceptions. | 
 |     breakpad_->set_handle_debug_exceptions(true); | 
 |   } | 
 |  | 
 |   // Catch exceptions thrown from a window procedure. | 
 |   base::win::WinProcExceptionFilter exception_filter = | 
 |       base::win::SetWinProcExceptionFilter(&OnWindowProcedureException); | 
 |   CHECK(!exception_filter); | 
 | } | 
 |  | 
 | BreakpadWin::~BreakpadWin() { | 
 |   // This object should be leaked so that crashes which occur during process | 
 |   // shutdown will be caught. | 
 |   NOTREACHED(); | 
 | } | 
 |  | 
 | // static | 
 | BreakpadWin* BreakpadWin::GetInstance() { | 
 |   return &g_instance.Get(); | 
 | } | 
 |  | 
 | // Returns the Custom information to be used for crash reporting. | 
 | google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() { | 
 |   std::unique_ptr<FileVersionInfo> version_info( | 
 |       FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE())); | 
 |  | 
 |   static wchar_t version[64]; | 
 |   if (version_info.get()) { | 
 |     wcscpy_s(version, version_info->product_version().c_str()); | 
 |   } else { | 
 |     wcscpy_s(version, kBreakpadVersionDefault); | 
 |   } | 
 |  | 
 |   static google_breakpad::CustomInfoEntry ver_entry( | 
 |       kBreakpadVersionEntry, version); | 
 |   static google_breakpad::CustomInfoEntry prod_entry( | 
 |       kBreakpadProdEntry, kBreakpadProductName); | 
 |   static google_breakpad::CustomInfoEntry plat_entry( | 
 |       kBreakpadPlatformEntry, kBreakpadPlatformWin32); | 
 |   static google_breakpad::CustomInfoEntry entries[] = { | 
 |       ver_entry, prod_entry, plat_entry  }; | 
 |   static google_breakpad::CustomClientInfo custom_info = { | 
 |       entries, arraysize(entries) }; | 
 |   return &custom_info; | 
 | } | 
 |  | 
 | // static | 
 | bool BreakpadWin::OnExceptionCallback(void* /* context */, | 
 |                                       EXCEPTION_POINTERS* /* exinfo */, | 
 |                                       MDRawAssertionInfo* /* assertion */) { | 
 |   BreakpadWin* self = BreakpadWin::GetInstance(); | 
 |   if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) { | 
 |     // Capture every thread except the first one in the sleep. We don't | 
 |     // want multiple threads to concurrently report exceptions. | 
 |     ::Sleep(INFINITE); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | // static | 
 | int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS* exinfo) { | 
 |   BreakpadWin* self = BreakpadWin::GetInstance(); | 
 |   if (self->breakpad_.get() != NULL) { | 
 |     self->breakpad_->WriteMinidumpForException(exinfo); | 
 |     TerminateProcess(GetCurrentProcess(), | 
 |                      exinfo->ExceptionRecord->ExceptionCode); | 
 |   } | 
 |   return EXCEPTION_CONTINUE_SEARCH; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace remoting { | 
 |  | 
 | void InitializeCrashReporting() { | 
 |   // Touch the object to make sure it is initialized. | 
 |   BreakpadWin::GetInstance(); | 
 | } | 
 |  | 
 | void InitializeCrashReportingForTest(const wchar_t* pipe_name) { | 
 |   BreakpadWin::pipe_name_ = pipe_name; | 
 |   InitializeCrashReporting(); | 
 | } | 
 |  | 
 | }  // namespace remoting |