toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial