OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // This module contains the necessary code to register the Breakpad exception | 5 // This module contains the necessary code to register the Breakpad exception |
6 // handler. This implementation is based on Chrome crash reporting code. See: | 6 // handler. This implementation is based on Chrome crash reporting code. |
7 // - src/components/breakpad/app/breakpad_win.cc | |
8 // - src/chrome/installer/setup/setup_main.cc | |
9 | 7 |
10 #include "remoting/base/breakpad.h" | 8 #include "chrome_elf/breakpad.h" |
11 | 9 |
12 #include <windows.h> | |
13 #include <string> | 10 #include <string> |
14 | 11 |
15 #include "base/atomicops.h" | 12 #include "base/atomicops.h" |
16 #include "base/file_version_info.h" | |
17 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
18 #include "base/logging.h" | |
19 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
20 #include "base/process/memory.h" | |
21 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
22 #include "base/win/wrapped_window_proc.h" | 16 #include "breakpad/src/client/windows/crash_generation/crash_generation_client.h " |
23 #include "breakpad/src/client/windows/handler/exception_handler.h" | 17 #include "breakpad/src/client/windows/handler/exception_handler.h" |
24 | 18 #include "version.h" // NOLINT |
25 namespace remoting { | |
26 void InitializeCrashReportingForTest(const wchar_t* pipe_name); | |
27 } // namespace remoting | |
28 | 19 |
29 namespace { | 20 namespace { |
30 | 21 |
31 const wchar_t kBreakpadProductName[] = L"Chromoting"; | 22 const wchar_t kBreakpadProductName[] = L"ChromeElf"; |
32 const wchar_t kBreakpadVersionEntry[] = L"ver"; | 23 const wchar_t kBreakpadVersionEntry[] = L"ver"; |
33 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0"; | 24 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0"; |
34 const wchar_t kBreakpadProdEntry[] = L"prod"; | 25 const wchar_t kBreakpadProdEntry[] = L"prod"; |
35 const wchar_t kBreakpadPlatformEntry[] = L"plat"; | 26 const wchar_t kBreakpadPlatformEntry[] = L"plat"; |
36 const wchar_t kBreakpadPlatformWin32[] = L"Win32"; | 27 const wchar_t kBreakpadPlatformWin32[] = L"Win32"; |
37 | 28 |
29 // TODO(caitkp): figure out what these should be. Current pipe works with local | |
30 // instance of crash_service.exe. | |
38 // The protocol for connecting to the out-of-process Breakpad crash | 31 // The protocol for connecting to the out-of-process Breakpad crash |
39 // reporter is different for x86-32 and x86-64: the message sizes | 32 // reporter is different for x86-32 and x86-64: the message sizes |
40 // are different because the message struct contains a pointer. As | 33 // are different because the message struct contains a pointer. As |
41 // a result, there are two different named pipes to connect to. The | 34 // a result, there are two different named pipes to connect to. The |
42 // 64-bit one is distinguished with an "-x64" suffix. | 35 // 64-bit one is distinguished with an "-x64" suffix. |
43 #if defined(_WIN64) | 36 #if defined(_WIN64) |
44 const wchar_t kGoogleUpdatePipeName[] = | 37 const wchar_t kGoogleUpdatePipeName[] = |
45 L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64"; | 38 L"\\\\.\\pipe\\ChromeCrashServices"; |
39 //L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64"; | |
46 #else | 40 #else |
47 const wchar_t kGoogleUpdatePipeName[] = | 41 const wchar_t kGoogleUpdatePipeName[] = |
48 L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18"; | 42 L"\\\\.\\pipe\\ChromeCrashServices"; |
43 //L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18"; | |
49 #endif | 44 #endif |
50 | 45 |
51 using base::subtle::AtomicWord; | 46 using base::subtle::AtomicWord; |
52 using base::subtle::NoBarrier_CompareAndSwap; | 47 using base::subtle::NoBarrier_CompareAndSwap; |
53 | 48 |
54 class BreakpadWin { | 49 class BreakpadWin { |
55 public: | 50 public: |
56 BreakpadWin(); | 51 BreakpadWin(); |
57 ~BreakpadWin(); | 52 ~BreakpadWin(); |
58 | 53 |
59 static BreakpadWin* GetInstance(); | 54 static BreakpadWin* GetInstance(); |
60 | 55 |
56 // Generates dump on request for given |exinfo|. | |
57 static int OnDumpRequested(EXCEPTION_POINTERS* exinfo); | |
58 | |
61 private: | 59 private: |
62 // Returns the Custom information to be used for crash reporting. | 60 // Returns the Custom information to be used for crash reporting. |
63 google_breakpad::CustomClientInfo* GetCustomInfo(); | 61 google_breakpad::CustomClientInfo* GetCustomInfo(); |
64 | 62 |
65 // This callback is executed when the process has crashed and *before* | 63 // This callback is executed when the process has crashed and *before* |
66 // the crash dump is created. To prevent duplicate crash reports we | 64 // the crash dump is created. To prevent duplicate crash reports we |
67 // make every thread calling this method, except the very first one, | 65 // make every thread calling this method, except the very first one, |
68 // go to sleep. | 66 // go to sleep. |
69 static bool OnExceptionCallback(void* context, | 67 static bool OnExceptionCallback(void* context, |
70 EXCEPTION_POINTERS* exinfo, | 68 EXCEPTION_POINTERS* exinfo, |
71 MDRawAssertionInfo* assertion); | 69 MDRawAssertionInfo* assertion); |
72 | 70 |
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. | 71 // Breakpad's exception handler. |
79 scoped_ptr<google_breakpad::ExceptionHandler> breakpad_; | 72 scoped_ptr<google_breakpad::ExceptionHandler> breakpad_; |
80 | 73 |
81 // This flag is used to indicate that an exception is already being handled. | 74 // This flag is used to indicate that an exception is already being handled. |
82 volatile AtomicWord handling_exception_; | 75 volatile AtomicWord handling_exception_; |
83 | 76 |
84 // The testing hook below allows overriding the crash server pipe name. | 77 // The testing hook below allows overriding the crash server pipe name. |
85 static const wchar_t* pipe_name_; | 78 static const wchar_t* pipe_name_; |
86 | 79 |
87 friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*); | |
88 | |
89 DISALLOW_COPY_AND_ASSIGN(BreakpadWin); | 80 DISALLOW_COPY_AND_ASSIGN(BreakpadWin); |
90 }; | 81 }; |
91 | 82 |
92 // |LazyInstance| is used to guarantee that the exception handler will be | 83 // |LazyInstance| is used to guarantee that the exception handler will be |
93 // initialized exactly once. | 84 // initialized exactly once. |
94 // N.B. LazyInstance does not allow this to be a static member of the class. | 85 // N.B. LazyInstance does not allow this to be a static member of the class. |
95 static base::LazyInstance<BreakpadWin>::Leaky g_instance = | 86 static base::LazyInstance<BreakpadWin>::Leaky g_instance = |
96 LAZY_INSTANCE_INITIALIZER; | 87 LAZY_INSTANCE_INITIALIZER; |
97 | 88 |
98 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName; | 89 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName; |
99 | 90 |
100 BreakpadWin::BreakpadWin() : handling_exception_(0) { | 91 BreakpadWin::BreakpadWin() : handling_exception_(0) { |
101 // Disable the message box for assertions. | 92 // Disable the message box for assertions. |
102 _CrtSetReportMode(_CRT_ASSERT, 0); | 93 _CrtSetReportMode(_CRT_ASSERT, 0); |
103 | 94 |
104 // Get the alternate dump directory. We use the temp path. | 95 // 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 | 96 // N.B. We don't use base::GetTempDir() here to avoid running more code then |
106 // necessary before crashes can be properly reported. | 97 // necessary before crashes can be properly reported. |
107 wchar_t temp_directory[MAX_PATH + 1] = { 0 }; | 98 wchar_t temp_directory[MAX_PATH + 1] = { 0 }; |
108 DWORD length = GetTempPath(MAX_PATH, temp_directory); | 99 DWORD length = GetTempPath(MAX_PATH, temp_directory); |
109 if (length == 0) | 100 if (length == 0) |
110 return; | 101 return; |
111 | 102 |
112 // Minidump with stacks, PEB, TEBs and unloaded module list. | 103 // Minidump with stacks, PEB, TEBs and unloaded module list. |
113 MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>( | 104 MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>( |
114 MiniDumpWithProcessThreadData | | 105 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
115 MiniDumpWithUnloadedModules); | 106 MiniDumpWithUnloadedModules | // Get unloaded modules when available. |
116 breakpad_.reset( | 107 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. |
117 new google_breakpad::ExceptionHandler( | 108 |
118 temp_directory, &OnExceptionCallback, NULL, NULL, | 109 breakpad_.reset(new google_breakpad::ExceptionHandler( |
119 google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type, | 110 temp_directory, |
120 pipe_name_, GetCustomInfo())); | 111 &OnExceptionCallback, |
112 NULL, | |
113 NULL, | |
114 google_breakpad::ExceptionHandler::HANDLER_ALL, | |
115 dump_type, | |
116 pipe_name_, | |
117 GetCustomInfo())); | |
121 | 118 |
122 if (breakpad_->IsOutOfProcess()) { | 119 if (breakpad_->IsOutOfProcess()) { |
123 // Tells breakpad to handle breakpoint and single step exceptions. | 120 // Tells breakpad to handle breakpoint and single step exceptions. |
124 breakpad_->set_handle_debug_exceptions(true); | 121 breakpad_->set_handle_debug_exceptions(true); |
125 } | 122 } |
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 } | 123 } |
132 | 124 |
133 BreakpadWin::~BreakpadWin() { | 125 BreakpadWin::~BreakpadWin() { |
134 // This object should be leaked so that crashes occurred during the process | 126 // This object should be leaked so that crashes occurred during the process |
135 // shutdown will be caught. | 127 // shutdown will be caught. |
136 NOTREACHED(); | 128 NOTREACHED(); |
137 } | 129 } |
138 | 130 |
139 // static | 131 // static |
140 BreakpadWin* BreakpadWin::GetInstance() { | 132 BreakpadWin* BreakpadWin::GetInstance() { |
141 return &g_instance.Get(); | 133 return &g_instance.Get(); |
142 } | 134 } |
143 | 135 |
136 // static | |
137 int BreakpadWin::OnDumpRequested(EXCEPTION_POINTERS* exinfo) { | |
138 BreakpadWin* self = BreakpadWin::GetInstance(); | |
139 if (self->breakpad_.get() != NULL) { | |
140 self->breakpad_->WriteMinidumpForException(exinfo); | |
141 // TODO(caitkp): Is terminating the right thing to do here? | |
Sigurður Ásgeirsson
2014/02/05 13:58:33
It's six of on, half a dozen of the other. If you
Cait (Slow)
2014/02/07 15:32:02
Done -- I'll grab the dump and let the loader take
| |
142 TerminateProcess(GetCurrentProcess(), | |
143 exinfo->ExceptionRecord->ExceptionCode); | |
144 } | |
145 return EXCEPTION_CONTINUE_SEARCH; | |
146 } | |
147 | |
144 // Returns the Custom information to be used for crash reporting. | 148 // Returns the Custom information to be used for crash reporting. |
145 google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() { | 149 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)); | |
150 | |
151 static wchar_t version[64]; | |
152 if (version_info.get()) { | |
153 wcscpy_s(version, | |
154 base::UTF16ToWide(version_info->product_version()).c_str()); | |
155 } else { | |
156 wcscpy_s(version, kBreakpadVersionDefault); | |
157 } | |
158 | |
159 static google_breakpad::CustomInfoEntry ver_entry( | 150 static google_breakpad::CustomInfoEntry ver_entry( |
160 kBreakpadVersionEntry, version); | 151 kBreakpadVersionEntry, TEXT(CHROME_VERSION_STRING)); |
161 static google_breakpad::CustomInfoEntry prod_entry( | 152 static google_breakpad::CustomInfoEntry prod_entry( |
162 kBreakpadProdEntry, kBreakpadProductName); | 153 kBreakpadProdEntry, kBreakpadProductName); |
163 static google_breakpad::CustomInfoEntry plat_entry( | 154 static google_breakpad::CustomInfoEntry plat_entry( |
164 kBreakpadPlatformEntry, kBreakpadPlatformWin32); | 155 kBreakpadPlatformEntry, kBreakpadPlatformWin32); |
165 static google_breakpad::CustomInfoEntry entries[] = { | 156 static google_breakpad::CustomInfoEntry entries[] = { |
166 ver_entry, prod_entry, plat_entry }; | 157 ver_entry, prod_entry, plat_entry }; |
167 static google_breakpad::CustomClientInfo custom_info = { | 158 static google_breakpad::CustomClientInfo custom_info = { |
168 entries, arraysize(entries) }; | 159 entries, arraysize(entries) }; |
169 return &custom_info; | 160 return &custom_info; |
170 } | 161 } |
171 | 162 |
172 // static | 163 // static |
173 bool BreakpadWin::OnExceptionCallback(void* /* context */, | 164 bool BreakpadWin::OnExceptionCallback(void* /* context */, |
174 EXCEPTION_POINTERS* /* exinfo */, | 165 EXCEPTION_POINTERS* /* exinfo */, |
175 MDRawAssertionInfo* /* assertion */) { | 166 MDRawAssertionInfo* /* assertion */) { |
176 BreakpadWin* self = BreakpadWin::GetInstance(); | 167 BreakpadWin* self = BreakpadWin::GetInstance(); |
177 if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) { | 168 if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) { |
178 // Capture every thread except the first one in the sleep. We don't | 169 // Capture every thread except the first one in the sleep. We don't |
179 // want multiple threads to concurrently report exceptions. | 170 // want multiple threads to concurrently report exceptions. |
180 ::Sleep(INFINITE); | 171 ::Sleep(INFINITE); |
181 } | 172 } |
182 return true; | 173 return true; |
183 } | 174 } |
184 | 175 |
185 // static | |
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); | |
192 } | |
193 return EXCEPTION_CONTINUE_SEARCH; | |
194 } | |
195 | |
196 } // namespace | 176 } // namespace |
197 | 177 |
198 namespace remoting { | 178 int GenerateCrashDump(EXCEPTION_POINTERS* exinfo) { |
179 return BreakpadWin::OnDumpRequested(exinfo); | |
Sigurður Ásgeirsson
2014/02/05 13:58:33
As we discussed, you may want to filter out debug
Cait (Slow)
2014/02/07 15:32:02
I tested under windbg and it seemed fine -- no har
| |
180 } | |
199 | 181 |
200 void InitializeCrashReporting() { | 182 void InitializeCrashReporting() { |
201 // Touch the object to make sure it is initialized. | 183 // Touch the object to make sure it is initialized. |
202 BreakpadWin::GetInstance(); | 184 BreakpadWin::GetInstance(); |
203 } | 185 } |
204 | |
205 void InitializeCrashReportingForTest(const wchar_t* pipe_name) { | |
206 BreakpadWin::pipe_name_ = pipe_name; | |
207 InitializeCrashReporting(); | |
208 } | |
209 | |
210 } // namespace remoting | |
OLD | NEW |