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