Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // This module contains the necessary code to register the Breakpad exception
      6 // handler. This implementation is based on Chrome crash reporting code. See:
      7 //   - src/components/breakpad/app/breakpad_win.cc
      8 //   - src/chrome/installer/setup/setup_main.cc
      9 
     10 #include "remoting/base/breakpad.h"
     11 
     12 #include <windows.h>
     13 #include <string>
     14 
     15 #include "base/atomicops.h"
     16 #include "base/file_version_info.h"
     17 #include "base/lazy_instance.h"
     18 #include "base/logging.h"
     19 #include "base/memory/scoped_ptr.h"
     20 #include "base/process/memory.h"
     21 #include "base/strings/utf_string_conversions.h"
     22 #include "base/win/wrapped_window_proc.h"
     23 #include "breakpad/src/client/windows/handler/exception_handler.h"
     24 
     25 namespace remoting {
     26 void InitializeCrashReportingForTest(const wchar_t* pipe_name);
     27 }  // namespace remoting
     28 
     29 namespace {
     30 
     31 const wchar_t kBreakpadProductName[] = L"Chromoting";
     32 const wchar_t kBreakpadVersionEntry[] = L"ver";
     33 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0";
     34 const wchar_t kBreakpadProdEntry[] = L"prod";
     35 const wchar_t kBreakpadPlatformEntry[] = L"plat";
     36 const wchar_t kBreakpadPlatformWin32[] = L"Win32";
     37 
     38 // The protocol for connecting to the out-of-process Breakpad crash
     39 // reporter is different for x86-32 and x86-64: the message sizes
     40 // are different because the message struct contains a pointer.  As
     41 // a result, there are two different named pipes to connect to.  The
     42 // 64-bit one is distinguished with an "-x64" suffix.
     43 #if defined(_WIN64)
     44 const wchar_t kGoogleUpdatePipeName[] =
     45     L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64";
     46 #else
     47 const wchar_t kGoogleUpdatePipeName[] =
     48     L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18";
     49 #endif
     50 
     51 using base::subtle::AtomicWord;
     52 using base::subtle::NoBarrier_CompareAndSwap;
     53 
     54 class BreakpadWin {
     55  public:
     56   BreakpadWin();
     57   ~BreakpadWin();
     58 
     59   static BreakpadWin* GetInstance();
     60 
     61  private:
     62   // Returns the Custom information to be used for crash reporting.
     63   google_breakpad::CustomClientInfo* GetCustomInfo();
     64 
     65   // This callback is executed when the process has crashed and *before*
     66   // the crash dump is created. To prevent duplicate crash reports we
     67   // make every thread calling this method, except the very first one,
     68   // go to sleep.
     69   static bool OnExceptionCallback(void* context,
     70                                   EXCEPTION_POINTERS* exinfo,
     71                                   MDRawAssertionInfo* assertion);
     72 
     73   // Crashes the process after generating a dump for the provided exception.
     74   // Note that the crash reporter should be initialized before calling this
     75   // function for it to do anything.
     76   static int OnWindowProcedureException(EXCEPTION_POINTERS* exinfo);
     77 
     78   // Breakpad's exception handler.
     79   scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
     80 
     81   // This flag is used to indicate that an exception is already being handled.
     82   volatile AtomicWord handling_exception_;
     83 
     84   // The testing hook below allows overriding the crash server pipe name.
     85   static const wchar_t* pipe_name_;
     86 
     87   friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*);
     88 
     89   DISALLOW_COPY_AND_ASSIGN(BreakpadWin);
     90 };
     91 
     92 // |LazyInstance| is used to guarantee that the exception handler will be
     93 // initialized exactly once.
     94 // N.B. LazyInstance does not allow this to be a static member of the class.
     95 static base::LazyInstance<BreakpadWin>::Leaky g_instance =
     96     LAZY_INSTANCE_INITIALIZER;
     97 
     98 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName;
     99 
    100 BreakpadWin::BreakpadWin() : handling_exception_(0) {
    101   // Disable the message box for assertions.
    102   _CrtSetReportMode(_CRT_ASSERT, 0);
    103 
    104   // Get the alternate dump directory. We use the temp path.
    105   // N.B. We don't use base::GetTempDir() here to avoid running more code then
    106   //      necessary before crashes can be properly reported.
    107   wchar_t temp_directory[MAX_PATH + 1] = { 0 };
    108   DWORD length = GetTempPath(MAX_PATH, temp_directory);
    109   if (length == 0)
    110     return;
    111 
    112   // Minidump with stacks, PEB, TEBs and unloaded module list.
    113   MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>(
    114       MiniDumpWithProcessThreadData |
    115       MiniDumpWithUnloadedModules);
    116   breakpad_.reset(
    117       new google_breakpad::ExceptionHandler(
    118           temp_directory, &OnExceptionCallback, NULL, NULL,
    119           google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type,
    120           pipe_name_, GetCustomInfo()));
    121 
    122   if (breakpad_->IsOutOfProcess()) {
    123     // Tells breakpad to handle breakpoint and single step exceptions.
    124     breakpad_->set_handle_debug_exceptions(true);
    125   }
    126 
    127   // Catch exceptions thrown from a window procedure.
    128   base::win::WinProcExceptionFilter exception_filter =
    129       base::win::SetWinProcExceptionFilter(&OnWindowProcedureException);
    130   CHECK(!exception_filter);
    131 }
    132 
    133 BreakpadWin::~BreakpadWin() {
    134   // This object should be leaked so that crashes occurred during the process
    135   // shutdown will be caught.
    136   NOTREACHED();
    137 }
    138 
    139 // static
    140 BreakpadWin* BreakpadWin::GetInstance() {
    141   return &g_instance.Get();
    142 }
    143 
    144 // Returns the Custom information to be used for crash reporting.
    145 google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() {
    146   HMODULE binary = base::GetModuleFromAddress(
    147       reinterpret_cast<void*>(&remoting::InitializeCrashReporting));
    148   scoped_ptr<FileVersionInfo> version_info(
    149       FileVersionInfo::CreateFileVersionInfoForModule(binary));
    150 
    151   static wchar_t version[64];
    152   if (version_info.get()) {
    153     wcscpy_s(version, version_info->product_version().c_str());
    154   } else {
    155     wcscpy_s(version, kBreakpadVersionDefault);
    156   }
    157 
    158   static google_breakpad::CustomInfoEntry ver_entry(
    159       kBreakpadVersionEntry, version);
    160   static google_breakpad::CustomInfoEntry prod_entry(
    161       kBreakpadProdEntry, kBreakpadProductName);
    162   static google_breakpad::CustomInfoEntry plat_entry(
    163       kBreakpadPlatformEntry, kBreakpadPlatformWin32);
    164   static google_breakpad::CustomInfoEntry entries[] = {
    165       ver_entry, prod_entry, plat_entry  };
    166   static google_breakpad::CustomClientInfo custom_info = {
    167       entries, arraysize(entries) };
    168   return &custom_info;
    169 }
    170 
    171 // static
    172 bool BreakpadWin::OnExceptionCallback(void* /* context */,
    173                                       EXCEPTION_POINTERS* /* exinfo */,
    174                                       MDRawAssertionInfo* /* assertion */) {
    175   BreakpadWin* self = BreakpadWin::GetInstance();
    176   if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) {
    177     // Capture every thread except the first one in the sleep. We don't
    178     // want multiple threads to concurrently report exceptions.
    179     ::Sleep(INFINITE);
    180   }
    181   return true;
    182 }
    183 
    184 // static
    185 int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS* exinfo) {
    186   BreakpadWin* self = BreakpadWin::GetInstance();
    187   if (self->breakpad_.get() != NULL) {
    188     self->breakpad_->WriteMinidumpForException(exinfo);
    189     TerminateProcess(GetCurrentProcess(),
    190                      exinfo->ExceptionRecord->ExceptionCode);
    191   }
    192   return EXCEPTION_CONTINUE_SEARCH;
    193 }
    194 
    195 }  // namespace
    196 
    197 namespace remoting {
    198 
    199 void InitializeCrashReporting() {
    200   // Touch the object to make sure it is initialized.
    201   BreakpadWin::GetInstance();
    202 }
    203 
    204 void InitializeCrashReportingForTest(const wchar_t* pipe_name) {
    205   BreakpadWin::pipe_name_ = pipe_name;
    206   InitializeCrashReporting();
    207 }
    208 
    209 }  // namespace remoting
    210