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
25namespace remoting {
26void InitializeCrashReportingForTest(const wchar_t* pipe_name);
27}  // namespace remoting
28
29namespace {
30
31const wchar_t kBreakpadProductName[] = L"Chromoting";
32const wchar_t kBreakpadVersionEntry[] = L"ver";
33const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0";
34const wchar_t kBreakpadProdEntry[] = L"prod";
35const wchar_t kBreakpadPlatformEntry[] = L"plat";
36const 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)
44const wchar_t kGoogleUpdatePipeName[] =
45    L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64";
46#else
47const wchar_t kGoogleUpdatePipeName[] =
48    L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18";
49#endif
50
51using base::subtle::AtomicWord;
52using base::subtle::NoBarrier_CompareAndSwap;
53
54class 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.
95static base::LazyInstance<BreakpadWin>::Leaky g_instance =
96    LAZY_INSTANCE_INITIALIZER;
97
98const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName;
99
100BreakpadWin::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
133BreakpadWin::~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
140BreakpadWin* BreakpadWin::GetInstance() {
141  return &g_instance.Get();
142}
143
144// Returns the Custom information to be used for crash reporting.
145google_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
172bool 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
185int 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
197namespace remoting {
198
199void InitializeCrashReporting() {
200  // Touch the object to make sure it is initialized.
201  BreakpadWin::GetInstance();
202}
203
204void InitializeCrashReportingForTest(const wchar_t* pipe_name) {
205  BreakpadWin::pipe_name_ = pipe_name;
206  InitializeCrashReporting();
207}
208
209}  // namespace remoting
210