PageRenderTime 76ms CodeModel.GetById 25ms app.highlight 44ms RepoModel.GetById 2ms app.codeStats 0ms

/thirdparty/breakpad/client/mac/handler/exception_handler.cc

http://github.com/tomahawk-player/tomahawk
C++ | 830 lines | 608 code | 108 blank | 114 comment | 107 complexity | 515074fd1e1d5cf85c260113e437aaed MD5 | raw file
  1// Copyright (c) 2006, 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 <map>
 31#include <mach/exc.h>
 32#include <mach/mig.h>
 33#include <pthread.h>
 34#include <signal.h>
 35#include <TargetConditionals.h>
 36
 37#include "client/mac/handler/exception_handler.h"
 38#include "client/mac/handler/minidump_generator.h"
 39#include "common/mac/macho_utilities.h"
 40#include "common/mac/scoped_task_suspend-inl.h"
 41#include "google_breakpad/common/minidump_exception_mac.h"
 42
 43#ifndef USE_PROTECTED_ALLOCATIONS
 44#if TARGET_OS_IPHONE
 45#define USE_PROTECTED_ALLOCATIONS 1
 46#else
 47#define USE_PROTECTED_ALLOCATIONS 0
 48#endif
 49#endif
 50
 51// If USE_PROTECTED_ALLOCATIONS is activated then the
 52// gBreakpadAllocator needs to be setup in other code
 53// ahead of time.  Please see ProtectedMemoryAllocator.h
 54// for more details.
 55#if USE_PROTECTED_ALLOCATIONS
 56  #include "protected_memory_allocator.h"
 57  extern ProtectedMemoryAllocator *gBreakpadAllocator;
 58#endif
 59
 60namespace google_breakpad {
 61
 62static union {
 63#if USE_PROTECTED_ALLOCATIONS
 64  char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
 65#endif
 66  google_breakpad::ExceptionHandler *handler;
 67} gProtectedData;
 68
 69using std::map;
 70
 71// These structures and techniques are illustrated in
 72// Mac OS X Internals, Amit Singh, ch 9.7
 73struct ExceptionMessage {
 74  mach_msg_header_t           header;
 75  mach_msg_body_t             body;
 76  mach_msg_port_descriptor_t  thread;
 77  mach_msg_port_descriptor_t  task;
 78  NDR_record_t                ndr;
 79  exception_type_t            exception;
 80  mach_msg_type_number_t      code_count;
 81  integer_t                   code[EXCEPTION_CODE_MAX];
 82  char                        padding[512];
 83};
 84
 85struct ExceptionParameters {
 86  ExceptionParameters() : count(0) {}
 87  mach_msg_type_number_t count;
 88  exception_mask_t masks[EXC_TYPES_COUNT];
 89  mach_port_t ports[EXC_TYPES_COUNT];
 90  exception_behavior_t behaviors[EXC_TYPES_COUNT];
 91  thread_state_flavor_t flavors[EXC_TYPES_COUNT];
 92};
 93
 94struct ExceptionReplyMessage {
 95  mach_msg_header_t  header;
 96  NDR_record_t       ndr;
 97  kern_return_t      return_code;
 98};
 99
100// Only catch these three exceptions.  The other ones are nebulously defined
101// and may result in treating a non-fatal exception as fatal.
102exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
103EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
104
105#if !TARGET_OS_IPHONE
106extern "C"
107{
108  // Forward declarations for functions that need "C" style compilation
109  boolean_t exc_server(mach_msg_header_t *request,
110                       mach_msg_header_t *reply);
111
112  // This symbol must be visible to dlsym() - see
113  // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
114  kern_return_t catch_exception_raise(mach_port_t target_port,
115                                      mach_port_t failed_thread,
116                                      mach_port_t task,
117                                      exception_type_t exception,
118                                      exception_data_t code,
119                                      mach_msg_type_number_t code_count)
120      __attribute__((visibility("default")));
121}
122#endif
123
124kern_return_t ForwardException(mach_port_t task,
125                               mach_port_t failed_thread,
126                               exception_type_t exception,
127                               exception_data_t code,
128                               mach_msg_type_number_t code_count);
129
130#if TARGET_OS_IPHONE
131// Implementation is based on the implementation generated by mig.
132boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
133                              mach_msg_header_t *OutHeadP) {
134   OutHeadP->msgh_bits =
135       MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
136   OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
137   /* Minimal size: routine() will update it if different */
138   OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
139   OutHeadP->msgh_local_port = MACH_PORT_NULL;
140   OutHeadP->msgh_id = InHeadP->msgh_id + 100;
141
142  if (InHeadP->msgh_id != 2401) {
143    ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
144    ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
145    return FALSE;
146  }
147
148#ifdef  __MigPackStructs
149#pragma pack(4)
150#endif
151  typedef struct {
152    mach_msg_header_t Head;
153    /* start of the kernel processed data */
154    mach_msg_body_t msgh_body;
155    mach_msg_port_descriptor_t thread;
156    mach_msg_port_descriptor_t task;
157    /* end of the kernel processed data */
158    NDR_record_t NDR;
159    exception_type_t exception;
160    mach_msg_type_number_t codeCnt;
161    integer_t code[2];
162    mach_msg_trailer_t trailer;
163  } Request;
164
165  typedef struct {
166    mach_msg_header_t Head;
167    NDR_record_t NDR;
168    kern_return_t RetCode;
169  } Reply;
170#ifdef  __MigPackStructs
171#pragma pack()
172#endif
173
174  Request *In0P = (Request *)InHeadP;
175  Reply *OutP = (Reply *)OutHeadP;
176
177  if (In0P->task.name != mach_task_self()) {
178    return FALSE;
179  }
180  OutP->RetCode = ForwardException(In0P->task.name,
181                                   In0P->thread.name,
182                                   In0P->exception,
183                                   In0P->code,
184                                   In0P->codeCnt);
185  OutP->NDR = NDR_record;
186  return TRUE;
187}
188#else
189boolean_t breakpad_exc_server(mach_msg_header_t *request,
190                              mach_msg_header_t *reply) {
191  return exc_server(request, reply);
192}
193
194// Callback from exc_server()
195kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
196                                    mach_port_t task,
197                                    exception_type_t exception,
198                                    exception_data_t code,
199                                    mach_msg_type_number_t code_count) {
200  if (task != mach_task_self()) {
201    return KERN_FAILURE;
202  }
203  return ForwardException(task, failed_thread, exception, code, code_count);
204}
205#endif
206
207ExceptionHandler::ExceptionHandler(const string &dump_path,
208                                   FilterCallback filter,
209                                   MinidumpCallback callback,
210                                   void *callback_context,
211                                   bool install_handler,
212                                   const char *port_name)
213    : dump_path_(),
214      filter_(filter),
215      callback_(callback),
216      callback_context_(callback_context),
217      directCallback_(NULL),
218      handler_thread_(NULL),
219      handler_port_(MACH_PORT_NULL),
220      previous_(NULL),
221      installed_exception_handler_(false),
222      is_in_teardown_(false),
223      last_minidump_write_result_(false),
224      use_minidump_write_mutex_(false) {
225  // This will update to the ID and C-string pointers
226  set_dump_path(dump_path);
227  MinidumpGenerator::GatherSystemInformation();
228#if !TARGET_OS_IPHONE
229  if (port_name)
230    crash_generation_client_.reset(new CrashGenerationClient(port_name));
231#endif
232  Setup(install_handler);
233}
234
235// special constructor if we want to bypass minidump writing and
236// simply get a callback with the exception information
237ExceptionHandler::ExceptionHandler(DirectCallback callback,
238                                   void *callback_context,
239                                   bool install_handler)
240    : dump_path_(),
241      filter_(NULL),
242      callback_(NULL),
243      callback_context_(callback_context),
244      directCallback_(callback),
245      handler_thread_(NULL),
246      handler_port_(MACH_PORT_NULL),
247      previous_(NULL),
248      installed_exception_handler_(false),
249      is_in_teardown_(false),
250      last_minidump_write_result_(false),
251      use_minidump_write_mutex_(false) {
252  MinidumpGenerator::GatherSystemInformation();
253  Setup(install_handler);
254}
255
256ExceptionHandler::~ExceptionHandler() {
257  Teardown();
258}
259
260bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
261  // If we're currently writing, just return
262  if (use_minidump_write_mutex_)
263    return false;
264
265  use_minidump_write_mutex_ = true;
266  last_minidump_write_result_ = false;
267
268  // Lock the mutex.  Since we just created it, this will return immediately.
269  if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
270    // Send an empty message to the handle port so that a minidump will
271    // be written
272    SendMessageToHandlerThread(write_exception_stream ?
273                                   kWriteDumpWithExceptionMessage :
274                                   kWriteDumpMessage);
275
276    // Wait for the minidump writer to complete its writing.  It will unlock
277    // the mutex when completed
278    pthread_mutex_lock(&minidump_write_mutex_);
279  }
280
281  use_minidump_write_mutex_ = false;
282  UpdateNextID();
283  return last_minidump_write_result_;
284}
285
286// static
287bool ExceptionHandler::WriteMinidump(const string &dump_path,
288                                     bool write_exception_stream,
289                                     MinidumpCallback callback,
290                                     void *callback_context) {
291  ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
292			   NULL);
293  return handler.WriteMinidump(write_exception_stream);
294}
295
296// static
297bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
298					     mach_port_t child_blamed_thread,
299					     const string &dump_path,
300					     MinidumpCallback callback,
301					     void *callback_context) {
302  ScopedTaskSuspend suspend(child);
303
304  MinidumpGenerator generator(child, MACH_PORT_NULL);
305  string dump_id;
306  string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
307
308  generator.SetExceptionInformation(EXC_BREAKPOINT,
309#if defined (__i386__) || defined(__x86_64__)
310				    EXC_I386_BPT,
311#elif defined (__ppc__) || defined (__ppc64__)
312				    EXC_PPC_BREAKPOINT,
313#elif defined (__arm__)
314				    EXC_ARM_BREAKPOINT,
315#else
316#error architecture not supported
317#endif
318				    0,
319				    child_blamed_thread);
320  bool result = generator.Write(dump_filename.c_str());
321
322  if (callback) {
323    return callback(dump_path.c_str(), dump_id.c_str(),
324		    callback_context, result);
325  }
326  return result;
327}
328
329bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
330                                                  int exception_code,
331                                                  int exception_subcode,
332                                                  mach_port_t thread_name,
333                                                  bool exit_after_write,
334                                                  bool report_current_thread) {
335  bool result = false;
336
337  if (directCallback_) {
338    if (directCallback_(callback_context_,
339                        exception_type,
340                        exception_code,
341                        exception_subcode,
342                        thread_name) ) {
343      if (exit_after_write)
344        _exit(exception_type);
345    }
346#if !TARGET_OS_IPHONE
347  } else if (IsOutOfProcess()) {
348    if (exception_type && exception_code) {
349      // If this is a real exception, give the filter (if any) a chance to
350      // decide if this should be sent.
351      if (filter_ && !filter_(callback_context_))
352	return false;
353      return crash_generation_client_->RequestDumpForException(
354	         exception_type,
355		 exception_code,
356		 exception_subcode,
357		 thread_name);
358    }
359#endif
360  } else {
361    string minidump_id;
362
363    // Putting the MinidumpGenerator in its own context will ensure that the
364    // destructor is executed, closing the newly created minidump file.
365    if (!dump_path_.empty()) {
366      MinidumpGenerator md(mach_task_self(),
367                           report_current_thread ? MACH_PORT_NULL :
368                                                   mach_thread_self());
369      if (exception_type && exception_code) {
370        // If this is a real exception, give the filter (if any) a chance to
371        // decide if this should be sent.
372        if (filter_ && !filter_(callback_context_))
373          return false;
374
375        md.SetExceptionInformation(exception_type, exception_code,
376                                   exception_subcode, thread_name);
377      }
378
379      result = md.Write(next_minidump_path_c_);
380    }
381
382    // Call user specified callback (if any)
383    if (callback_) {
384      // If the user callback returned true and we're handling an exception
385      // (rather than just writing out the file), then we should exit without
386      // forwarding the exception to the next handler.
387      if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
388                    result)) {
389        if (exit_after_write)
390          _exit(exception_type);
391      }
392    }
393  }
394
395  return result;
396}
397
398kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
399                               exception_type_t exception,
400                               exception_data_t code,
401                               mach_msg_type_number_t code_count) {
402  // At this time, we should have called Uninstall() on the exception handler
403  // so that the current exception ports are the ones that we should be
404  // forwarding to.
405  ExceptionParameters current;
406
407  current.count = EXC_TYPES_COUNT;
408  mach_port_t current_task = mach_task_self();
409  task_get_exception_ports(current_task,
410                           s_exception_mask,
411                           current.masks,
412                           &current.count,
413                           current.ports,
414                           current.behaviors,
415                           current.flavors);
416
417  // Find the first exception handler that matches the exception
418  unsigned int found;
419  for (found = 0; found < current.count; ++found) {
420    if (current.masks[found] & (1 << exception)) {
421      break;
422    }
423  }
424
425  // Nothing to forward
426  if (found == current.count) {
427    fprintf(stderr, "** No previous ports for forwarding!! \n");
428    exit(KERN_FAILURE);
429  }
430
431  mach_port_t target_port = current.ports[found];
432  exception_behavior_t target_behavior = current.behaviors[found];
433
434  kern_return_t result;
435  switch (target_behavior) {
436    case EXCEPTION_DEFAULT:
437      result = exception_raise(target_port, failed_thread, task, exception,
438                               code, code_count);
439      break;
440
441    default:
442      fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
443      result = KERN_FAILURE;
444      break;
445  }
446
447  return result;
448}
449
450// static
451void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
452  ExceptionHandler *self =
453    reinterpret_cast<ExceptionHandler *>(exception_handler_class);
454  ExceptionMessage receive;
455
456  // Wait for the exception info
457  while (1) {
458    receive.header.msgh_local_port = self->handler_port_;
459    receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
460    kern_return_t result = mach_msg(&(receive.header),
461                                    MACH_RCV_MSG | MACH_RCV_LARGE, 0,
462                                    receive.header.msgh_size,
463                                    self->handler_port_,
464                                    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
465
466
467    if (result == KERN_SUCCESS) {
468      // Uninstall our handler so that we don't get in a loop if the process of
469      // writing out a minidump causes an exception.  However, if the exception
470      // was caused by a fork'd process, don't uninstall things
471
472      // If the actual exception code is zero, then we're calling this handler
473      // in a way that indicates that we want to either exit this thread or
474      // generate a minidump
475      //
476      // While reporting, all threads (except this one) must be suspended
477      // to avoid misleading stacks.  If appropriate they will be resumed
478      // afterwards.
479      if (!receive.exception) {
480        // Don't touch self, since this message could have been sent
481        // from its destructor.
482        if (receive.header.msgh_id == kShutdownMessage)
483          return NULL;
484
485        self->SuspendThreads();
486
487#if USE_PROTECTED_ALLOCATIONS
488        if (gBreakpadAllocator)
489          gBreakpadAllocator->Unprotect();
490#endif
491
492        mach_port_t thread = MACH_PORT_NULL;
493        int exception_type = 0;
494        int exception_code = 0;
495        if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
496          thread = receive.thread.name;
497          exception_type = EXC_BREAKPOINT;
498#if defined (__i386__) || defined(__x86_64__)
499          exception_code = EXC_I386_BPT;
500#elif defined (__ppc__) || defined (__ppc64__)
501          exception_code = EXC_PPC_BREAKPOINT;
502#elif defined (__arm__)
503          exception_code = EXC_ARM_BREAKPOINT;
504#else
505#error architecture not supported
506#endif
507        }
508
509        // Write out the dump and save the result for later retrieval
510        self->last_minidump_write_result_ =
511          self->WriteMinidumpWithException(exception_type, exception_code,
512                                           0, thread,
513                                           false, false);
514
515#if USE_PROTECTED_ALLOCATIONS
516        if (gBreakpadAllocator)
517          gBreakpadAllocator->Protect();
518#endif
519
520        self->ResumeThreads();
521
522        if (self->use_minidump_write_mutex_)
523          pthread_mutex_unlock(&self->minidump_write_mutex_);
524      } else {
525        // When forking a child process with the exception handler installed,
526        // if the child crashes, it will send the exception back to the parent
527        // process.  The check for task == self_task() ensures that only
528        // exceptions that occur in the parent process are caught and
529        // processed.  If the exception was not caused by this task, we
530        // still need to call into the exception server and have it return
531        // KERN_FAILURE (see catch_exception_raise) in order for the kernel
532        // to move onto the host exception handler for the child task
533        if (receive.task.name == mach_task_self()) {
534          self->SuspendThreads();
535
536#if USE_PROTECTED_ALLOCATIONS
537        if (gBreakpadAllocator)
538          gBreakpadAllocator->Unprotect();
539#endif
540
541        int subcode = 0;
542        if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
543          subcode = receive.code[1];
544
545        // Generate the minidump with the exception data.
546        self->WriteMinidumpWithException(receive.exception, receive.code[0],
547                                         subcode, receive.thread.name, true,
548                                         false);
549
550#if USE_PROTECTED_ALLOCATIONS
551        // This may have become protected again within
552        // WriteMinidumpWithException, but it needs to be unprotected for
553        // UninstallHandler.
554        if (gBreakpadAllocator)
555          gBreakpadAllocator->Unprotect();
556#endif
557
558        self->UninstallHandler(true);
559
560#if USE_PROTECTED_ALLOCATIONS
561        if (gBreakpadAllocator)
562          gBreakpadAllocator->Protect();
563#endif
564        }
565        // Pass along the exception to the server, which will setup the
566        // message and call catch_exception_raise() and put the return
567        // code into the reply.
568        ExceptionReplyMessage reply;
569        if (!breakpad_exc_server(&receive.header, &reply.header))
570          exit(1);
571
572        // Send a reply and exit
573        mach_msg(&(reply.header), MACH_SEND_MSG,
574                 reply.header.msgh_size, 0, MACH_PORT_NULL,
575                 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
576      }
577    }
578  }
579
580  return NULL;
581}
582
583//static
584void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
585#if USE_PROTECTED_ALLOCATIONS
586  if (gBreakpadAllocator)
587    gBreakpadAllocator->Unprotect();
588#endif
589  gProtectedData.handler->WriteMinidumpWithException(
590      EXC_SOFTWARE,
591      MD_EXCEPTION_CODE_MAC_ABORT,
592      0,
593      mach_thread_self(),
594      true,
595      true);
596#if USE_PROTECTED_ALLOCATIONS
597  if (gBreakpadAllocator)
598    gBreakpadAllocator->Protect();
599#endif
600}
601
602bool ExceptionHandler::InstallHandler() {
603  // If a handler is already installed, something is really wrong.
604  if (gProtectedData.handler != NULL) {
605    return false;
606  }
607#if TARGET_OS_IPHONE
608  if (!IsOutOfProcess()) {
609    struct sigaction sa;
610    memset(&sa, 0, sizeof(sa));
611    sigemptyset(&sa.sa_mask);
612    sigaddset(&sa.sa_mask, SIGABRT);
613    sa.sa_sigaction = ExceptionHandler::SignalHandler;
614    sa.sa_flags = SA_SIGINFO;
615
616    scoped_ptr<struct sigaction> old(new struct sigaction);
617    if (sigaction(SIGABRT, &sa, old.get()) == -1) {
618      return false;
619    }
620    old_handler_.swap(old);
621    gProtectedData.handler = this;
622#if USE_PROTECTED_ALLOCATIONS
623    assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
624    mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
625#endif
626  }
627#endif
628
629  try {
630#if USE_PROTECTED_ALLOCATIONS
631    previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
632      ExceptionParameters();
633#else
634    previous_ = new ExceptionParameters();
635#endif
636
637  }
638  catch (std::bad_alloc) {
639    return false;
640  }
641
642  // Save the current exception ports so that we can forward to them
643  previous_->count = EXC_TYPES_COUNT;
644  mach_port_t current_task = mach_task_self();
645  kern_return_t result = task_get_exception_ports(current_task,
646                                                  s_exception_mask,
647                                                  previous_->masks,
648                                                  &previous_->count,
649                                                  previous_->ports,
650                                                  previous_->behaviors,
651                                                  previous_->flavors);
652
653  // Setup the exception ports on this task
654  if (result == KERN_SUCCESS)
655    result = task_set_exception_ports(current_task, s_exception_mask,
656                                      handler_port_, EXCEPTION_DEFAULT,
657                                      THREAD_STATE_NONE);
658
659  installed_exception_handler_ = (result == KERN_SUCCESS);
660
661  return installed_exception_handler_;
662}
663
664bool ExceptionHandler::UninstallHandler(bool in_exception) {
665  kern_return_t result = KERN_SUCCESS;
666
667  if (old_handler_.get()) {
668    sigaction(SIGABRT, old_handler_.get(), NULL);
669#if USE_PROTECTED_ALLOCATIONS
670    mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
671        PROT_READ | PROT_WRITE);
672#endif
673    old_handler_.reset();
674    gProtectedData.handler = NULL;
675  }
676
677  if (installed_exception_handler_) {
678    mach_port_t current_task = mach_task_self();
679
680    // Restore the previous ports
681    for (unsigned int i = 0; i < previous_->count; ++i) {
682       result = task_set_exception_ports(current_task, previous_->masks[i],
683                                        previous_->ports[i],
684                                        previous_->behaviors[i],
685                                        previous_->flavors[i]);
686      if (result != KERN_SUCCESS)
687        return false;
688    }
689
690    // this delete should NOT happen if an exception just occurred!
691    if (!in_exception) {
692#if USE_PROTECTED_ALLOCATIONS
693      previous_->~ExceptionParameters();
694#else
695      delete previous_;
696#endif
697    }
698
699    previous_ = NULL;
700    installed_exception_handler_ = false;
701  }
702
703  return result == KERN_SUCCESS;
704}
705
706bool ExceptionHandler::Setup(bool install_handler) {
707  if (pthread_mutex_init(&minidump_write_mutex_, NULL))
708    return false;
709
710  // Create a receive right
711  mach_port_t current_task = mach_task_self();
712  kern_return_t result = mach_port_allocate(current_task,
713                                            MACH_PORT_RIGHT_RECEIVE,
714                                            &handler_port_);
715  // Add send right
716  if (result == KERN_SUCCESS)
717    result = mach_port_insert_right(current_task, handler_port_, handler_port_,
718                                    MACH_MSG_TYPE_MAKE_SEND);
719
720  if (install_handler && result == KERN_SUCCESS)
721    if (!InstallHandler())
722      return false;
723
724  if (result == KERN_SUCCESS) {
725    // Install the handler in its own thread, detached as we won't be joining.
726    pthread_attr_t attr;
727    pthread_attr_init(&attr);
728    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
729    int thread_create_result = pthread_create(&handler_thread_, &attr,
730                                              &WaitForMessage, this);
731    pthread_attr_destroy(&attr);
732    result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
733  }
734
735  return result == KERN_SUCCESS ? true : false;
736}
737
738bool ExceptionHandler::Teardown() {
739  kern_return_t result = KERN_SUCCESS;
740  is_in_teardown_ = true;
741
742  if (!UninstallHandler(false))
743    return false;
744
745  // Send an empty message so that the handler_thread exits
746  if (SendMessageToHandlerThread(kShutdownMessage)) {
747    mach_port_t current_task = mach_task_self();
748    result = mach_port_deallocate(current_task, handler_port_);
749    if (result != KERN_SUCCESS)
750      return false;
751  } else {
752    return false;
753  }
754
755  handler_thread_ = NULL;
756  handler_port_ = MACH_PORT_NULL;
757  pthread_mutex_destroy(&minidump_write_mutex_);
758
759  return result == KERN_SUCCESS;
760}
761
762bool ExceptionHandler::SendMessageToHandlerThread(
763    HandlerThreadMessage message_id) {
764  ExceptionMessage msg;
765  memset(&msg, 0, sizeof(msg));
766  msg.header.msgh_id = message_id;
767  if (message_id == kWriteDumpMessage ||
768      message_id == kWriteDumpWithExceptionMessage) {
769    // Include this thread's port.
770    msg.thread.name = mach_thread_self();
771    msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
772    msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
773  }
774  msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
775  msg.header.msgh_remote_port = handler_port_;
776  msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
777                                          MACH_MSG_TYPE_MAKE_SEND_ONCE);
778  kern_return_t result = mach_msg(&(msg.header),
779                                  MACH_SEND_MSG | MACH_SEND_TIMEOUT,
780                                  msg.header.msgh_size, 0, 0,
781                                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
782
783  return result == KERN_SUCCESS;
784}
785
786void ExceptionHandler::UpdateNextID() {
787  next_minidump_path_ =
788    (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
789
790  next_minidump_path_c_ = next_minidump_path_.c_str();
791  next_minidump_id_c_ = next_minidump_id_.c_str();
792}
793
794bool ExceptionHandler::SuspendThreads() {
795  thread_act_port_array_t   threads_for_task;
796  mach_msg_type_number_t    thread_count;
797
798  if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
799    return false;
800
801  // suspend all of the threads except for this one
802  for (unsigned int i = 0; i < thread_count; ++i) {
803    if (threads_for_task[i] != mach_thread_self()) {
804      if (thread_suspend(threads_for_task[i]))
805        return false;
806    }
807  }
808
809  return true;
810}
811
812bool ExceptionHandler::ResumeThreads() {
813  thread_act_port_array_t   threads_for_task;
814  mach_msg_type_number_t    thread_count;
815
816  if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
817    return false;
818
819  // resume all of the threads except for this one
820  for (unsigned int i = 0; i < thread_count; ++i) {
821    if (threads_for_task[i] != mach_thread_self()) {
822      if (thread_resume(threads_for_task[i]))
823        return false;
824    }
825  }
826
827  return true;
828}
829
830}  // namespace google_breakpad