OLD | NEW |
| (Empty) |
1 // Copyright (c) 2008, Google Inc. | |
2 // All rights reserved. | |
3 // | |
4 // Redistribution and use in source and binary forms, with or without | |
5 // modification, are permitted provided that the following conditions are | |
6 // met: | |
7 // | |
8 // * Redistributions of source code must retain the above copyright | |
9 // notice, this list of conditions and the following disclaimer. | |
10 // * Redistributions in binary form must reproduce the above | |
11 // copyright notice, this list of conditions and the following disclaimer | |
12 // in the documentation and/or other materials provided with the | |
13 // distribution. | |
14 // * Neither the name of Google Inc. nor the names of its | |
15 // contributors may be used to endorse or promote products derived from | |
16 // this software without specific prior written permission. | |
17 // | |
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | |
30 #include "client/windows/crash_generation/crash_generation_client.h" | |
31 #include <cassert> | |
32 #include <utility> | |
33 #include "client/windows/common/ipc_protocol.h" | |
34 | |
35 namespace google_breakpad { | |
36 | |
37 const int kPipeBusyWaitTimeoutMs = 2000; | |
38 | |
39 #ifdef _DEBUG | |
40 const DWORD kWaitForServerTimeoutMs = INFINITE; | |
41 #else | |
42 const DWORD kWaitForServerTimeoutMs = 15000; | |
43 #endif | |
44 | |
45 const int kPipeConnectMaxAttempts = 2; | |
46 | |
47 const DWORD kPipeDesiredAccess = FILE_READ_DATA | | |
48 FILE_WRITE_DATA | | |
49 FILE_WRITE_ATTRIBUTES; | |
50 | |
51 const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | | |
52 SECURITY_SQOS_PRESENT; | |
53 | |
54 const DWORD kPipeMode = PIPE_READMODE_MESSAGE; | |
55 | |
56 const size_t kWaitEventCount = 2; | |
57 | |
58 // This function is orphan for production code. It can be used | |
59 // for debugging to help repro some scenarios like the client | |
60 // is slow in writing to the pipe after connecting, the client | |
61 // is slow in reading from the pipe after writing, etc. The parameter | |
62 // overlapped below is not used and it is present to match the signature | |
63 // of this function to TransactNamedPipe Win32 API. Uncomment if needed | |
64 // for debugging. | |
65 /** | |
66 static bool TransactNamedPipeDebugHelper(HANDLE pipe, | |
67 const void* in_buffer, | |
68 DWORD in_size, | |
69 void* out_buffer, | |
70 DWORD out_size, | |
71 DWORD* bytes_count, | |
72 LPOVERLAPPED) { | |
73 // Uncomment the next sleep to create a gap before writing | |
74 // to pipe. | |
75 // Sleep(5000); | |
76 | |
77 if (!WriteFile(pipe, | |
78 in_buffer, | |
79 in_size, | |
80 bytes_count, | |
81 NULL)) { | |
82 return false; | |
83 } | |
84 | |
85 // Uncomment the next sleep to create a gap between write | |
86 // and read. | |
87 // Sleep(5000); | |
88 | |
89 return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE; | |
90 } | |
91 **/ | |
92 | |
93 CrashGenerationClient::CrashGenerationClient( | |
94 const wchar_t* pipe_name, | |
95 MINIDUMP_TYPE dump_type, | |
96 const CustomClientInfo* custom_info) | |
97 : pipe_name_(pipe_name), | |
98 dump_type_(dump_type), | |
99 thread_id_(0), | |
100 server_process_id_(0), | |
101 crash_event_(NULL), | |
102 crash_generated_(NULL), | |
103 server_alive_(NULL), | |
104 exception_pointers_(NULL), | |
105 custom_info_() { | |
106 memset(&assert_info_, 0, sizeof(assert_info_)); | |
107 if (custom_info) { | |
108 custom_info_ = *custom_info; | |
109 } | |
110 } | |
111 | |
112 CrashGenerationClient::~CrashGenerationClient() { | |
113 if (crash_event_) { | |
114 CloseHandle(crash_event_); | |
115 } | |
116 | |
117 if (crash_generated_) { | |
118 CloseHandle(crash_generated_); | |
119 } | |
120 | |
121 if (server_alive_) { | |
122 CloseHandle(server_alive_); | |
123 } | |
124 } | |
125 | |
126 // Performs the registration step with the server process. | |
127 // The registration step involves communicating with the server | |
128 // via a named pipe. The client sends the following pieces of | |
129 // data to the server: | |
130 // | |
131 // * Message tag indicating the client is requesting registration. | |
132 // * Process id of the client process. | |
133 // * Address of a DWORD variable in the client address space | |
134 // that will contain the thread id of the client thread that | |
135 // caused the crash. | |
136 // * Address of a EXCEPTION_POINTERS* variable in the client | |
137 // address space that will point to an instance of EXCEPTION_POINTERS | |
138 // when the crash happens. | |
139 // * Address of an instance of MDRawAssertionInfo that will contain | |
140 // relevant information in case of non-exception crashes like assertion | |
141 // failures and pure calls. | |
142 // | |
143 // In return the client expects the following information from the server: | |
144 // | |
145 // * Message tag indicating successful registration. | |
146 // * Server process id. | |
147 // * Handle to an object that client can signal to request dump | |
148 // generation from the server. | |
149 // * Handle to an object that client can wait on after requesting | |
150 // dump generation for the server to finish dump generation. | |
151 // * Handle to a mutex object that client can wait on to make sure | |
152 // server is still alive. | |
153 // | |
154 // If any step of the expected behavior mentioned above fails, the | |
155 // registration step is not considered successful and hence out-of-process | |
156 // dump generation service is not available. | |
157 // | |
158 // Returns true if the registration is successful; false otherwise. | |
159 bool CrashGenerationClient::Register() { | |
160 HANDLE pipe = ConnectToServer(); | |
161 if (!pipe) { | |
162 return false; | |
163 } | |
164 | |
165 bool success = RegisterClient(pipe); | |
166 CloseHandle(pipe); | |
167 return success; | |
168 } | |
169 | |
170 HANDLE CrashGenerationClient::ConnectToServer() { | |
171 HANDLE pipe = ConnectToPipe(pipe_name_.c_str(), | |
172 kPipeDesiredAccess, | |
173 kPipeFlagsAndAttributes); | |
174 if (!pipe) { | |
175 return NULL; | |
176 } | |
177 | |
178 DWORD mode = kPipeMode; | |
179 if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) { | |
180 CloseHandle(pipe); | |
181 pipe = NULL; | |
182 } | |
183 | |
184 return pipe; | |
185 } | |
186 | |
187 bool CrashGenerationClient::RegisterClient(HANDLE pipe) { | |
188 ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST, | |
189 GetCurrentProcessId(), | |
190 dump_type_, | |
191 &thread_id_, | |
192 &exception_pointers_, | |
193 &assert_info_, | |
194 custom_info_, | |
195 NULL, | |
196 NULL, | |
197 NULL); | |
198 ProtocolMessage reply; | |
199 DWORD bytes_count = 0; | |
200 // The call to TransactNamedPipe below can be changed to a call | |
201 // to TransactNamedPipeDebugHelper to help repro some scenarios. | |
202 // For details see comments for TransactNamedPipeDebugHelper. | |
203 if (!TransactNamedPipe(pipe, | |
204 &msg, | |
205 sizeof(msg), | |
206 &reply, | |
207 sizeof(ProtocolMessage), | |
208 &bytes_count, | |
209 NULL)) { | |
210 return false; | |
211 } | |
212 | |
213 if (!ValidateResponse(reply)) { | |
214 return false; | |
215 } | |
216 | |
217 ProtocolMessage ack_msg; | |
218 ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK; | |
219 | |
220 if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) { | |
221 return false; | |
222 } | |
223 crash_event_ = reply.dump_request_handle; | |
224 crash_generated_ = reply.dump_generated_handle; | |
225 server_alive_ = reply.server_alive_handle; | |
226 server_process_id_ = reply.pid; | |
227 | |
228 return true; | |
229 } | |
230 | |
231 HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, | |
232 DWORD pipe_access, | |
233 DWORD flags_attrs) { | |
234 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { | |
235 HANDLE pipe = CreateFile(pipe_name, | |
236 pipe_access, | |
237 0, | |
238 NULL, | |
239 OPEN_EXISTING, | |
240 flags_attrs, | |
241 NULL); | |
242 if (pipe != INVALID_HANDLE_VALUE) { | |
243 return pipe; | |
244 } | |
245 | |
246 // Cannot continue retrying if error is something other than | |
247 // ERROR_PIPE_BUSY. | |
248 if (GetLastError() != ERROR_PIPE_BUSY) { | |
249 break; | |
250 } | |
251 | |
252 // Cannot continue retrying if wait on pipe fails. | |
253 if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { | |
254 break; | |
255 } | |
256 } | |
257 | |
258 return NULL; | |
259 } | |
260 | |
261 bool CrashGenerationClient::ValidateResponse( | |
262 const ProtocolMessage& msg) const { | |
263 return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) && | |
264 (msg.pid != 0) && | |
265 (msg.dump_request_handle != NULL) && | |
266 (msg.dump_generated_handle != NULL) && | |
267 (msg.server_alive_handle != NULL); | |
268 } | |
269 | |
270 bool CrashGenerationClient::IsRegistered() const { | |
271 return crash_event_ != NULL; | |
272 } | |
273 | |
274 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info, | |
275 MDRawAssertionInfo* assert_info) { | |
276 if (!IsRegistered()) { | |
277 return false; | |
278 } | |
279 | |
280 exception_pointers_ = ex_info; | |
281 thread_id_ = GetCurrentThreadId(); | |
282 | |
283 if (assert_info) { | |
284 memcpy(&assert_info_, assert_info, sizeof(assert_info_)); | |
285 } else { | |
286 memset(&assert_info_, 0, sizeof(assert_info_)); | |
287 } | |
288 | |
289 return SignalCrashEventAndWait(); | |
290 } | |
291 | |
292 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) { | |
293 return RequestDump(ex_info, NULL); | |
294 } | |
295 | |
296 bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) { | |
297 return RequestDump(NULL, assert_info); | |
298 } | |
299 | |
300 bool CrashGenerationClient::SignalCrashEventAndWait() { | |
301 assert(crash_event_); | |
302 assert(crash_generated_); | |
303 assert(server_alive_); | |
304 | |
305 // Reset the dump generated event before signaling the crash | |
306 // event so that the server can set the dump generated event | |
307 // once it is done generating the event. | |
308 if (!ResetEvent(crash_generated_)) { | |
309 return false; | |
310 } | |
311 | |
312 if (!SetEvent(crash_event_)) { | |
313 return false; | |
314 } | |
315 | |
316 HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_}; | |
317 | |
318 DWORD result = WaitForMultipleObjects(kWaitEventCount, | |
319 wait_handles, | |
320 FALSE, | |
321 kWaitForServerTimeoutMs); | |
322 | |
323 // Crash dump was successfully generated only if the server | |
324 // signaled the crash generated event. | |
325 return result == WAIT_OBJECT_0; | |
326 } | |
327 | |
328 } // namespace google_breakpad | |
OLD | NEW |