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