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