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.
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
10 #include "remoting/base/breakpad.h"
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"
26 void InitializeCrashReportingForTest(const wchar_t* pipe_name
);
27 } // namespace remoting
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";
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.
44 const wchar_t kGoogleUpdatePipeName
[] =
45 L
"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64";
47 const wchar_t kGoogleUpdatePipeName
[] =
48 L
"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18";
51 using base::subtle::AtomicWord
;
52 using base::subtle::NoBarrier_CompareAndSwap
;
59 static BreakpadWin
* GetInstance();
62 // Returns the Custom information to be used for crash reporting.
63 google_breakpad::CustomClientInfo
* GetCustomInfo();
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,
69 static bool OnExceptionCallback(void* context
,
70 EXCEPTION_POINTERS
* exinfo
,
71 MDRawAssertionInfo
* assertion
);
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
);
78 // Breakpad's exception handler.
79 scoped_ptr
<google_breakpad::ExceptionHandler
> breakpad_
;
81 // This flag is used to indicate that an exception is already being handled.
82 volatile AtomicWord handling_exception_
;
84 // The testing hook below allows overriding the crash server pipe name.
85 static const wchar_t* pipe_name_
;
87 friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*);
89 DISALLOW_COPY_AND_ASSIGN(BreakpadWin
);
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
;
98 const wchar_t* BreakpadWin::pipe_name_
= kGoogleUpdatePipeName
;
100 BreakpadWin::BreakpadWin() : handling_exception_(0) {
101 // Disable the message box for assertions.
102 _CrtSetReportMode(_CRT_ASSERT
, 0);
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
);
112 // Minidump with stacks, PEB, TEBs and unloaded module list.
113 MINIDUMP_TYPE dump_type
= static_cast<MINIDUMP_TYPE
>(
114 MiniDumpWithProcessThreadData
|
115 MiniDumpWithUnloadedModules
);
117 new google_breakpad::ExceptionHandler(
118 temp_directory
, &OnExceptionCallback
, NULL
, NULL
,
119 google_breakpad::ExceptionHandler::HANDLER_ALL
, dump_type
,
120 pipe_name_
, GetCustomInfo()));
122 if (breakpad_
->IsOutOfProcess()) {
123 // Tells breakpad to handle breakpoint and single step exceptions.
124 breakpad_
->set_handle_debug_exceptions(true);
127 // Catch exceptions thrown from a window procedure.
128 base::win::WinProcExceptionFilter exception_filter
=
129 base::win::SetWinProcExceptionFilter(&OnWindowProcedureException
);
130 CHECK(!exception_filter
);
133 BreakpadWin::~BreakpadWin() {
134 // This object should be leaked so that crashes occurred during the process
135 // shutdown will be caught.
140 BreakpadWin
* BreakpadWin::GetInstance() {
141 return &g_instance
.Get();
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
));
151 static wchar_t version
[64];
152 if (version_info
.get()) {
154 base::UTF16ToWide(version_info
->product_version()).c_str());
156 wcscpy_s(version
, kBreakpadVersionDefault
);
159 static google_breakpad::CustomInfoEntry
ver_entry(
160 kBreakpadVersionEntry
, version
);
161 static google_breakpad::CustomInfoEntry
prod_entry(
162 kBreakpadProdEntry
, kBreakpadProductName
);
163 static google_breakpad::CustomInfoEntry
plat_entry(
164 kBreakpadPlatformEntry
, kBreakpadPlatformWin32
);
165 static google_breakpad::CustomInfoEntry entries
[] = {
166 ver_entry
, prod_entry
, plat_entry
};
167 static google_breakpad::CustomClientInfo custom_info
= {
168 entries
, arraysize(entries
) };
173 bool BreakpadWin::OnExceptionCallback(void* /* context */,
174 EXCEPTION_POINTERS
* /* exinfo */,
175 MDRawAssertionInfo
* /* assertion */) {
176 BreakpadWin
* self
= BreakpadWin::GetInstance();
177 if (NoBarrier_CompareAndSwap(&self
->handling_exception_
, 0, 1) != 0) {
178 // Capture every thread except the first one in the sleep. We don't
179 // want multiple threads to concurrently report exceptions.
186 int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS
* exinfo
) {
187 BreakpadWin
* self
= BreakpadWin::GetInstance();
188 if (self
->breakpad_
.get() != NULL
) {
189 self
->breakpad_
->WriteMinidumpForException(exinfo
);
190 TerminateProcess(GetCurrentProcess(),
191 exinfo
->ExceptionRecord
->ExceptionCode
);
193 return EXCEPTION_CONTINUE_SEARCH
;
200 void InitializeCrashReporting() {
201 // Touch the object to make sure it is initialized.
202 BreakpadWin::GetInstance();
205 void InitializeCrashReportingForTest(const wchar_t* pipe_name
) {
206 BreakpadWin::pipe_name_
= pipe_name
;
207 InitializeCrashReporting();
210 } // namespace remoting