PageRenderTime 31ms CodeModel.GetById 8ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc

http://github.com/tomahawk-player/tomahawk
C++ | 345 lines | 193 code | 46 blank | 106 comment | 30 complexity | 3cd84c0cf2ce989b4f13f481144d7fbd MD5 | raw file
  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
 35namespace google_breakpad {
 36
 37const int kPipeBusyWaitTimeoutMs = 2000;
 38
 39#ifdef _DEBUG
 40const DWORD kWaitForServerTimeoutMs = INFINITE;
 41#else
 42const DWORD kWaitForServerTimeoutMs = 15000;
 43#endif
 44
 45const int kPipeConnectMaxAttempts = 2;
 46
 47const DWORD kPipeDesiredAccess = FILE_READ_DATA |
 48                                 FILE_WRITE_DATA |
 49                                 FILE_WRITE_ATTRIBUTES;
 50
 51const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
 52                                      SECURITY_SQOS_PRESENT;
 53
 54const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
 55
 56const 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/**
 66static 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
 93CrashGenerationClient::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
112CrashGenerationClient::~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.
159bool 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
170bool CrashGenerationClient::RequestUpload(DWORD crash_id) {
171  HANDLE pipe = ConnectToServer();
172  if (!pipe) {
173    return false;
174  }
175
176  CustomClientInfo custom_info = {NULL, 0};
177  ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id,
178                      static_cast<MINIDUMP_TYPE>(NULL), NULL, NULL, NULL,
179                      custom_info, NULL, NULL, NULL);
180  DWORD bytes_count = 0;
181  bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0;
182
183  CloseHandle(pipe);
184  return success;
185}
186
187HANDLE CrashGenerationClient::ConnectToServer() {
188  HANDLE pipe = ConnectToPipe(pipe_name_.c_str(),
189                              kPipeDesiredAccess,
190                              kPipeFlagsAndAttributes);
191  if (!pipe) {
192    return NULL;
193  }
194
195  DWORD mode = kPipeMode;
196  if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) {
197    CloseHandle(pipe);
198    pipe = NULL;
199  }
200
201  return pipe;
202}
203
204bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
205  ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST,
206                      GetCurrentProcessId(),
207                      dump_type_,
208                      &thread_id_,
209                      &exception_pointers_,
210                      &assert_info_,
211                      custom_info_,
212                      NULL,
213                      NULL,
214                      NULL);
215  ProtocolMessage reply;
216  DWORD bytes_count = 0;
217  // The call to TransactNamedPipe below can be changed to a call
218  // to TransactNamedPipeDebugHelper to help repro some scenarios.
219  // For details see comments for TransactNamedPipeDebugHelper.
220  if (!TransactNamedPipe(pipe,
221                         &msg,
222                         sizeof(msg),
223                         &reply,
224                         sizeof(ProtocolMessage),
225                         &bytes_count,
226                         NULL)) {
227    return false;
228  }
229
230  if (!ValidateResponse(reply)) {
231    return false;
232  }
233
234  ProtocolMessage ack_msg;
235  ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK;
236
237  if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) {
238    return false;
239  }
240  crash_event_ = reply.dump_request_handle;
241  crash_generated_ = reply.dump_generated_handle;
242  server_alive_ = reply.server_alive_handle;
243  server_process_id_ = reply.id;
244
245  return true;
246}
247
248HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name,
249                                            DWORD pipe_access,
250                                            DWORD flags_attrs) {
251  for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
252    HANDLE pipe = CreateFile(pipe_name,
253                             pipe_access,
254                             0,
255                             NULL,
256                             OPEN_EXISTING,
257                             flags_attrs,
258                             NULL);
259    if (pipe != INVALID_HANDLE_VALUE) {
260      return pipe;
261    }
262
263    // Cannot continue retrying if error is something other than
264    // ERROR_PIPE_BUSY.
265    if (GetLastError() != ERROR_PIPE_BUSY) {
266      break;
267    }
268
269    // Cannot continue retrying if wait on pipe fails.
270    if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
271      break;
272    }
273  }
274
275  return NULL;
276}
277
278bool CrashGenerationClient::ValidateResponse(
279    const ProtocolMessage& msg) const {
280  return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) &&
281         (msg.id != 0) &&
282         (msg.dump_request_handle != NULL) &&
283         (msg.dump_generated_handle != NULL) &&
284         (msg.server_alive_handle != NULL);
285}
286
287bool CrashGenerationClient::IsRegistered() const {
288  return crash_event_ != NULL;
289}
290
291bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
292                                        MDRawAssertionInfo* assert_info) {
293  if (!IsRegistered()) {
294    return false;
295  }
296
297  exception_pointers_ = ex_info;
298  thread_id_ = GetCurrentThreadId();
299
300  if (assert_info) {
301    memcpy(&assert_info_, assert_info, sizeof(assert_info_));
302  } else {
303    memset(&assert_info_, 0, sizeof(assert_info_));
304  }
305
306  return SignalCrashEventAndWait();
307}
308
309bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
310  return RequestDump(ex_info, NULL);
311}
312
313bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
314  return RequestDump(NULL, assert_info);
315}
316
317bool CrashGenerationClient::SignalCrashEventAndWait() {
318  assert(crash_event_);
319  assert(crash_generated_);
320  assert(server_alive_);
321
322  // Reset the dump generated event before signaling the crash
323  // event so that the server can set the dump generated event
324  // once it is done generating the event.
325  if (!ResetEvent(crash_generated_)) {
326    return false;
327  }
328
329  if (!SetEvent(crash_event_)) {
330    return false;
331  }
332
333  HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};
334
335  DWORD result = WaitForMultipleObjects(kWaitEventCount,
336                                        wait_handles,
337                                        FALSE,
338                                        kWaitForServerTimeoutMs);
339
340  // Crash dump was successfully generated only if the server
341  // signaled the crash generated event.
342  return result == WAIT_OBJECT_0;
343}
344
345}  // namespace google_breakpad