| 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 |