OLD | NEW |
---|---|
(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 | |
OLD | NEW |