Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: remoting/host/breakpad_win.cc

Issue 10495003: Make Chromoting Host report crashes to Breakpad (Windows only). The user must enable crash dumps co… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
Wez 2012/06/06 15:52:03 Is this implementation based on some existing code
alexeypa (please no reviews) 2012/06/06 16:53:56 It is based on: - src/chrome/app/breakpad_win.cc
Wez 2012/06/07 16:56:47 OK. I'm guessing the first one is the one this fi
alexeypa (please no reviews) 2012/06/08 17:26:17 Done.
4
5 #include "remoting/host/breakpad.h"
6
7 #include <windows.h>
8
9 #include "base/atomicops.h"
10 #include "base/logging.h"
11 #include "base/file_version_info.h"
12 #include "base/lazy_instance.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/process_util.h"
15 #include "base/string16.h"
16 #include "base/stringprintf.h"
17 #include "base/win/registry.h"
18 #include "base/win/wrapped_window_proc.h"
19 #include "breakpad/src/client/windows/handler/exception_handler.h"
20 #include "remoting/host/constants.h"
21
22 namespace remoting {
23 void InitializeCrashReportingForTest(const wchar_t*);
24 } // namespace remoting
25
26 namespace {
27
28 const wchar_t kBreakpadProductName[] = L"Chromoting";
29 const wchar_t kBreakpadVersionEntry[] = L"ver";
30 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0";
31 const wchar_t kBreakpadProdEntry[] = L"prod";
32 const wchar_t kBreakpadPlatformEntry[] = L"plat";
33 const wchar_t kBreakpadPlatformWin32[] = L"Win32";
34
35 // The following strings are used to construct the registry key names where
36 // the user's consent to collect crash dumps is recorded.
37 const wchar_t kOmahaClientStateKeyFormat[] = L"Google\\Update\\%ls\\%ls";
38 const wchar_t kOmahaClientState[] = L"ClientState";
39 const wchar_t kOmahaClientStateMedium[] = L"ClientStateMedium";
40 const wchar_t kOmahaUsagestatsValue[] = L"usagestats";
41
42 // The protocol for connecting to the out-of-process Breakpad crash
43 // reporter is different for x86-32 and x86-64: the message sizes
44 // are different because the message struct contains a pointer. As
45 // a result, there are two different named pipes to connect to. The
46 // 64-bit one is distinguished with an "-x64" suffix.
47 #if defined(_WIN64)
48 const wchar_t kGoogleUpdatePipeName[] =
49 L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64";
50 #else
51 const wchar_t kGoogleUpdatePipeName[] =
52 L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18";
53 #endif
54
55 using base::subtle::AtomicWord;
56 using base::subtle::NoBarrier_CompareAndSwap;
57
58 class BreakpadWin {
59 public:
60 BreakpadWin();
61 ~BreakpadWin();
62
63 static BreakpadWin& GetInstance();
64
65 private:
66 // Returns the Custom information to be used for crash reporting.
67 google_breakpad::CustomClientInfo* GetCustomInfo();
68
69 // Checks whether crash dump collection is allowed by the user.
70 bool IsCrashReportingEnabled();
71
72 // This callback is executed when the process has crashed and *before*
73 // the crash dump is created. To prevent duplicate crash reports we
74 // make every thread calling this method, except the very first one,
75 // go to sleep.
76 static bool OnExceptionCallback(void*, EXCEPTION_POINTERS*,
77 MDRawAssertionInfo*);
78
79 // Crashes the process after generating a dump for the provided exception.
80 // Note that the crash reporter should be initialized before calling this
81 // function for it to do anything.
82 static int OnWindowProcedureException(EXCEPTION_POINTERS* info);
83
84 // Breakpad's exception handler.
85 scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
86
87 // This flag is used to indicate that an exception is already being handled.
88 volatile AtomicWord handling_exception_;
89
90 // The testing hooks below allow to override the crash server pipe name and
91 // user's consent.
92 static const wchar_t* pipe_name_;
93 static bool enable_testing_;
94
95 friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*);
96
97 DISALLOW_COPY_AND_ASSIGN(BreakpadWin);
98 };
99
100 // |LazyInstance| is used to guarantee that the exception handler will be
101 // initialized exactly once.
102 // N.B. LazyInstance does not allow this to be a static member of the class.
103 static base::LazyInstance<BreakpadWin>::Leaky g_instance =
104 LAZY_INSTANCE_INITIALIZER;
105
106 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName;
107 bool BreakpadWin::enable_testing_ = false;
108
109 BreakpadWin::BreakpadWin() : handling_exception_(0) {
110 // Only report crashes if the user allows it.
111 if (!IsCrashReportingEnabled())
112 return;
113
114 // Disable the message box for assertions.
115 _CrtSetReportMode(_CRT_ASSERT, 0);
116
117 // Get the alternate dump directory. We use the temp path.
118 wchar_t temp_directory[MAX_PATH + 1] = { 0 };
119 DWORD length = ::GetTempPath(MAX_PATH, temp_directory);
120 if (length == 0)
121 return;
122
123 // Minidump with stacks, PEB, TEB, unloaded module list and memory referenced
124 // from stack.
125 MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>(
126 MiniDumpWithProcessThreadData |
127 MiniDumpWithUnloadedModules |
128 MiniDumpWithIndirectlyReferencedMemory);
129 breakpad_.reset(
130 new google_breakpad::ExceptionHandler(
131 temp_directory, &OnExceptionCallback, NULL, NULL,
132 google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type,
133 pipe_name_, GetCustomInfo()));
134
135 if (breakpad_->IsOutOfProcess()) {
136 // Tells breakpad to handle breakpoint and single step exceptions.
137 breakpad_->set_handle_debug_exceptions(true);
138 }
139
140 // Catch exceptions thrown from a window procedure.
141 base::win::WinProcExceptionFilter exception_filter =
142 base::win::SetWinProcExceptionFilter(&OnWindowProcedureException);
143 CHECK(!exception_filter);
144 }
145
146 BreakpadWin::~BreakpadWin() {
147 // This object should be leaked so that crashes during the process shutdown
148 // will be caught.
149 NOTREACHED();
150 }
151
152 // static
153 BreakpadWin& BreakpadWin::GetInstance() {
154 return g_instance.Get();
155 }
156
157 // Returns the Custom information to be used for crash reporting.
158 google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() {
159 HMODULE binary = base::GetModuleFromAddress(
160 reinterpret_cast<void*>(&remoting::InitializeCrashReporting));
161 scoped_ptr<FileVersionInfo> version_info(
162 FileVersionInfo::CreateFileVersionInfoForModule(binary));
163
164 string16 version;
165 if (version_info.get())
166 version = version_info->product_version();
167 if (version.empty())
168 version = kBreakpadVersionDefault;
169
170 static google_breakpad::CustomInfoEntry ver_entry(
171 kBreakpadVersionEntry, version.c_str());
172 static google_breakpad::CustomInfoEntry prod_entry(
173 kBreakpadProdEntry, kBreakpadProductName);
174 static google_breakpad::CustomInfoEntry plat_entry(
175 kBreakpadPlatformEntry, kBreakpadPlatformWin32);
176 static google_breakpad::CustomInfoEntry entries[] = {
177 ver_entry, prod_entry, plat_entry };
178 static google_breakpad::CustomClientInfo custom_info = {
179 entries, arraysize(entries) };
180 return &custom_info;
181 }
182
183 bool BreakpadWin::IsCrashReportingEnabled() {
184 // The user's consent to collect crash dumps is recored as the "usagestats"
185 // value in the ClientState or ClientStateMedium key. Probe
186 // the ClientStateMedium key first.
187 string16 client_state = StringPrintf(kOmahaClientStateKeyFormat,
188 kOmahaClientStateMedium,
189 remoting::kOmahaAppid);
190 base::win::RegKey key;
191 LONG result = key.Open(HKEY_LOCAL_MACHINE, client_state.c_str(), KEY_READ);
192 if (result == ERROR_SUCCESS) {
193 DWORD value = 0;
194 result = key.ReadValueDW(kOmahaUsagestatsValue, &value);
195 if (result == ERROR_SUCCESS) {
196 return value != 0;
197 }
198 }
199
200 client_state = StringPrintf(kOmahaClientStateKeyFormat,
201 kOmahaClientState,
202 remoting::kOmahaAppid);
203 result = key.Open(HKEY_LOCAL_MACHINE, client_state.c_str(), KEY_READ);
204 if (result == ERROR_SUCCESS) {
205 DWORD value = 0;
206 result = key.ReadValueDW(kOmahaUsagestatsValue, &value);
207 if (result == ERROR_SUCCESS) {
208 return value != 0;
209 }
210 }
211
212 // Do not collect anything if the user hasn't explicitly allowed it. Make sure
213 // though that crash dump collection is enabled when running the tests.
214 return enable_testing_;
215 }
216
217 // static
218 bool BreakpadWin::OnExceptionCallback(void*, EXCEPTION_POINTERS*,
219 MDRawAssertionInfo*) {
220 BreakpadWin& self = BreakpadWin::GetInstance();
221 if (NoBarrier_CompareAndSwap(&self.handling_exception_, 0, 1) != 0) {
222 // Capture every thread except the first one in the sleep. We don't
223 // want multiple threads to concurrently report exceptions.
224 ::Sleep(INFINITE);
225 }
226 return true;
227 }
228
229 // static
230 int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS* info) {
231 BreakpadWin& self = BreakpadWin::GetInstance();
232 if (self.breakpad_.get() != NULL) {
233 self.breakpad_->WriteMinidumpForException(info);
234 ::TerminateProcess(::GetCurrentProcess(),
235 info->ExceptionRecord->ExceptionCode);
236 }
237 return EXCEPTION_CONTINUE_SEARCH;
238 }
239
240 } // namespace
241
242 namespace remoting {
243
244 void InitializeCrashReporting() {
245 // Touch the object to make sure it is initialized.
246 BreakpadWin::GetInstance();
247 }
248
249 void InitializeCrashReportingForTest(const wchar_t* pipe_name) {
250 BreakpadWin::pipe_name_ = pipe_name;
251 BreakpadWin::enable_testing_ = true;
252
253 // Touch the object to make sure it is initialized.
254 BreakpadWin::GetInstance();
255 }
256
257 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698