Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / breakpad / src / client / mac / handler / exception_handler.cc
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   char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
78 #endif
79   google_breakpad::ExceptionHandler *handler;
80 } gProtectedData;
81
82 using std::map;
83
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 };
97
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 };
106
107 struct ExceptionReplyMessage {
108   mach_msg_header_t  header;
109   NDR_record_t       ndr;
110   kern_return_t      return_code;
111 };
112
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;
117
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);
123
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
135
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);
141
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;
153
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   }
159
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;
176
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
185
186   Request* In0P = (Request*)InHeadP;
187   Reply* OutP = (Reply*)OutHeadP;
188
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 }
205
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
218
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 }
246
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 }
267
268 ExceptionHandler::~ExceptionHandler() {
269   Teardown();
270 }
271
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;
276
277   use_minidump_write_mutex_ = true;
278   last_minidump_write_result_ = false;
279
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     }
291
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   }
296
297   use_minidump_write_mutex_ = false;
298   UpdateNextID();
299   return last_minidump_write_result_;
300 }
301
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 }
311
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);
319
320   MinidumpGenerator generator(child, MACH_PORT_NULL);
321   string dump_id;
322   string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
323
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__) || defined(__aarch64__)
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());
337
338   if (callback) {
339     return callback(dump_path.c_str(), dump_id.c_str(),
340                     callback_context, result);
341   }
342   return result;
343 }
344
345 bool ExceptionHandler::WriteMinidumpWithException(
346     int exception_type,
347     int exception_code,
348     int exception_subcode,
349     breakpad_ucontext_t* task_context,
350     mach_port_t thread_name,
351     bool exit_after_write,
352     bool report_current_thread) {
353   bool result = false;
354
355   if (directCallback_) {
356     if (directCallback_(callback_context_,
357                         exception_type,
358                         exception_code,
359                         exception_subcode,
360                         thread_name) ) {
361       if (exit_after_write)
362         _exit(exception_type);
363     }
364 #if !TARGET_OS_IPHONE
365   } else if (IsOutOfProcess()) {
366     if (exception_type && exception_code) {
367       // If this is a real exception, give the filter (if any) a chance to
368       // decide if this should be sent.
369       if (filter_ && !filter_(callback_context_))
370         return false;
371       result = crash_generation_client_->RequestDumpForException(
372           exception_type,
373           exception_code,
374           exception_subcode,
375           thread_name);
376       if (result && exit_after_write) {
377         _exit(exception_type);
378       }
379     }
380 #endif
381   } else {
382     string minidump_id;
383
384     // Putting the MinidumpGenerator in its own context will ensure that the
385     // destructor is executed, closing the newly created minidump file.
386     if (!dump_path_.empty()) {
387       MinidumpGenerator md(mach_task_self(),
388                            report_current_thread ? MACH_PORT_NULL :
389                                                    mach_thread_self());
390       md.SetTaskContext(task_context);
391       if (exception_type && exception_code) {
392         // If this is a real exception, give the filter (if any) a chance to
393         // decide if this should be sent.
394         if (filter_ && !filter_(callback_context_))
395           return false;
396
397         md.SetExceptionInformation(exception_type, exception_code,
398                                    exception_subcode, thread_name);
399       }
400
401       result = md.Write(next_minidump_path_c_);
402     }
403
404     // Call user specified callback (if any)
405     if (callback_) {
406       // If the user callback returned true and we're handling an exception
407       // (rather than just writing out the file), then we should exit without
408       // forwarding the exception to the next handler.
409       if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
410                     result)) {
411         if (exit_after_write)
412           _exit(exception_type);
413       }
414     }
415   }
416
417   return result;
418 }
419
420 kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
421                                exception_type_t exception,
422                                exception_data_t code,
423                                mach_msg_type_number_t code_count) {
424   // At this time, we should have called Uninstall() on the exception handler
425   // so that the current exception ports are the ones that we should be
426   // forwarding to.
427   ExceptionParameters current;
428
429   current.count = EXC_TYPES_COUNT;
430   mach_port_t current_task = mach_task_self();
431   task_get_exception_ports(current_task,
432                            s_exception_mask,
433                            current.masks,
434                            &current.count,
435                            current.ports,
436                            current.behaviors,
437                            current.flavors);
438
439   // Find the first exception handler that matches the exception
440   unsigned int found;
441   for (found = 0; found < current.count; ++found) {
442     if (current.masks[found] & (1 << exception)) {
443       break;
444     }
445   }
446
447   // Nothing to forward
448   if (found == current.count) {
449     fprintf(stderr, "** No previous ports for forwarding!! \n");
450     exit(KERN_FAILURE);
451   }
452
453   mach_port_t target_port = current.ports[found];
454   exception_behavior_t target_behavior = current.behaviors[found];
455
456   kern_return_t result;
457   // TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES
458   // set. https://code.google.com/p/google-breakpad/issues/detail?id=551
459   switch (target_behavior) {
460     case EXCEPTION_DEFAULT:
461       result = exception_raise(target_port, failed_thread, task, exception,
462                                code, code_count);
463       break;
464     default:
465       fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
466       result = KERN_FAILURE;
467       break;
468   }
469
470   return result;
471 }
472
473 // static
474 void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
475   ExceptionHandler* self =
476     reinterpret_cast<ExceptionHandler*>(exception_handler_class);
477   ExceptionMessage receive;
478
479   // Wait for the exception info
480   while (1) {
481     receive.header.msgh_local_port = self->handler_port_;
482     receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
483     kern_return_t result = mach_msg(&(receive.header),
484                                     MACH_RCV_MSG | MACH_RCV_LARGE, 0,
485                                     receive.header.msgh_size,
486                                     self->handler_port_,
487                                     MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
488
489
490     if (result == KERN_SUCCESS) {
491       // Uninstall our handler so that we don't get in a loop if the process of
492       // writing out a minidump causes an exception.  However, if the exception
493       // was caused by a fork'd process, don't uninstall things
494
495       // If the actual exception code is zero, then we're calling this handler
496       // in a way that indicates that we want to either exit this thread or
497       // generate a minidump
498       //
499       // While reporting, all threads (except this one) must be suspended
500       // to avoid misleading stacks.  If appropriate they will be resumed
501       // afterwards.
502       if (!receive.exception) {
503         // Don't touch self, since this message could have been sent
504         // from its destructor.
505         if (receive.header.msgh_id == kShutdownMessage)
506           return NULL;
507
508         self->SuspendThreads();
509
510 #if USE_PROTECTED_ALLOCATIONS
511         if (gBreakpadAllocator)
512           gBreakpadAllocator->Unprotect();
513 #endif
514
515         mach_port_t thread = MACH_PORT_NULL;
516         int exception_type = 0;
517         int exception_code = 0;
518         if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
519           thread = receive.thread.name;
520           exception_type = EXC_BREAKPOINT;
521 #if defined(__i386__) || defined(__x86_64__)
522           exception_code = EXC_I386_BPT;
523 #elif defined(__ppc__) || defined(__ppc64__)
524           exception_code = EXC_PPC_BREAKPOINT;
525 #elif defined(__arm__) || defined(__aarch64__)
526           exception_code = EXC_ARM_BREAKPOINT;
527 #else
528 #error architecture not supported
529 #endif
530         }
531
532         // Write out the dump and save the result for later retrieval
533         self->last_minidump_write_result_ =
534           self->WriteMinidumpWithException(exception_type, exception_code,
535                                            0, NULL, thread,
536                                            false, false);
537
538 #if USE_PROTECTED_ALLOCATIONS
539         if (gBreakpadAllocator)
540           gBreakpadAllocator->Protect();
541 #endif
542
543         self->ResumeThreads();
544
545         if (self->use_minidump_write_mutex_)
546           pthread_mutex_unlock(&self->minidump_write_mutex_);
547       } else {
548         // When forking a child process with the exception handler installed,
549         // if the child crashes, it will send the exception back to the parent
550         // process.  The check for task == self_task() ensures that only
551         // exceptions that occur in the parent process are caught and
552         // processed.  If the exception was not caused by this task, we
553         // still need to call into the exception server and have it return
554         // KERN_FAILURE (see catch_exception_raise) in order for the kernel
555         // to move onto the host exception handler for the child task
556         if (receive.task.name == mach_task_self()) {
557           self->SuspendThreads();
558
559 #if USE_PROTECTED_ALLOCATIONS
560         if (gBreakpadAllocator)
561           gBreakpadAllocator->Unprotect();
562 #endif
563
564         int subcode = 0;
565         if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
566           subcode = receive.code[1];
567
568         // Generate the minidump with the exception data.
569         self->WriteMinidumpWithException(receive.exception, receive.code[0],
570                                          subcode, NULL, receive.thread.name,
571                                          true, false);
572
573 #if USE_PROTECTED_ALLOCATIONS
574         // This may have become protected again within
575         // WriteMinidumpWithException, but it needs to be unprotected for
576         // UninstallHandler.
577         if (gBreakpadAllocator)
578           gBreakpadAllocator->Unprotect();
579 #endif
580
581         self->UninstallHandler(true);
582
583 #if USE_PROTECTED_ALLOCATIONS
584         if (gBreakpadAllocator)
585           gBreakpadAllocator->Protect();
586 #endif
587         }
588         // Pass along the exception to the server, which will setup the
589         // message and call catch_exception_raise() and put the return
590         // code into the reply.
591         ExceptionReplyMessage reply;
592         if (!breakpad_exc_server(&receive.header, &reply.header))
593           exit(1);
594
595         // Send a reply and exit
596         mach_msg(&(reply.header), MACH_SEND_MSG,
597                  reply.header.msgh_size, 0, MACH_PORT_NULL,
598                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
599       }
600     }
601   }
602
603   return NULL;
604 }
605
606 // static
607 void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
608 #if USE_PROTECTED_ALLOCATIONS
609   if (gBreakpadAllocator)
610     gBreakpadAllocator->Unprotect();
611 #endif
612   gProtectedData.handler->WriteMinidumpWithException(
613       EXC_SOFTWARE,
614       MD_EXCEPTION_CODE_MAC_ABORT,
615       0,
616       static_cast<breakpad_ucontext_t*>(uc),
617       mach_thread_self(),
618       true,
619       true);
620 #if USE_PROTECTED_ALLOCATIONS
621   if (gBreakpadAllocator)
622     gBreakpadAllocator->Protect();
623 #endif
624 }
625
626 bool ExceptionHandler::InstallHandler() {
627   // If a handler is already installed, something is really wrong.
628   if (gProtectedData.handler != NULL) {
629     return false;
630   }
631   if (!IsOutOfProcess()) {
632     struct sigaction sa;
633     memset(&sa, 0, sizeof(sa));
634     sigemptyset(&sa.sa_mask);
635     sigaddset(&sa.sa_mask, SIGABRT);
636     sa.sa_sigaction = ExceptionHandler::SignalHandler;
637     sa.sa_flags = SA_SIGINFO;
638
639     scoped_ptr<struct sigaction> old(new struct sigaction);
640     if (sigaction(SIGABRT, &sa, old.get()) == -1) {
641       return false;
642     }
643     old_handler_.swap(old);
644     gProtectedData.handler = this;
645 #if USE_PROTECTED_ALLOCATIONS
646     assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
647     mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
648 #endif
649   }
650
651   try {
652 #if USE_PROTECTED_ALLOCATIONS
653     previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
654       ExceptionParameters();
655 #else
656     previous_ = new ExceptionParameters();
657 #endif
658   }
659   catch (std::bad_alloc) {
660     return false;
661   }
662
663   // Save the current exception ports so that we can forward to them
664   previous_->count = EXC_TYPES_COUNT;
665   mach_port_t current_task = mach_task_self();
666   kern_return_t result = task_get_exception_ports(current_task,
667                                                   s_exception_mask,
668                                                   previous_->masks,
669                                                   &previous_->count,
670                                                   previous_->ports,
671                                                   previous_->behaviors,
672                                                   previous_->flavors);
673
674   // Setup the exception ports on this task
675   if (result == KERN_SUCCESS)
676     result = task_set_exception_ports(current_task, s_exception_mask,
677                                       handler_port_, EXCEPTION_DEFAULT,
678                                       THREAD_STATE_NONE);
679
680   installed_exception_handler_ = (result == KERN_SUCCESS);
681
682   return installed_exception_handler_;
683 }
684
685 bool ExceptionHandler::UninstallHandler(bool in_exception) {
686   kern_return_t result = KERN_SUCCESS;
687
688   if (old_handler_.get()) {
689     sigaction(SIGABRT, old_handler_.get(), NULL);
690 #if USE_PROTECTED_ALLOCATIONS
691     mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
692         PROT_READ | PROT_WRITE);
693 #endif
694     old_handler_.reset();
695     gProtectedData.handler = NULL;
696   }
697
698   if (installed_exception_handler_) {
699     mach_port_t current_task = mach_task_self();
700
701     // Restore the previous ports
702     for (unsigned int i = 0; i < previous_->count; ++i) {
703        result = task_set_exception_ports(current_task, previous_->masks[i],
704                                         previous_->ports[i],
705                                         previous_->behaviors[i],
706                                         previous_->flavors[i]);
707       if (result != KERN_SUCCESS)
708         return false;
709     }
710
711     // this delete should NOT happen if an exception just occurred!
712     if (!in_exception) {
713 #if USE_PROTECTED_ALLOCATIONS
714       previous_->~ExceptionParameters();
715 #else
716       delete previous_;
717 #endif
718     }
719
720     previous_ = NULL;
721     installed_exception_handler_ = false;
722   }
723
724   return result == KERN_SUCCESS;
725 }
726
727 bool ExceptionHandler::Setup(bool install_handler) {
728   if (pthread_mutex_init(&minidump_write_mutex_, NULL))
729     return false;
730
731   // Create a receive right
732   mach_port_t current_task = mach_task_self();
733   kern_return_t result = mach_port_allocate(current_task,
734                                             MACH_PORT_RIGHT_RECEIVE,
735                                             &handler_port_);
736   // Add send right
737   if (result == KERN_SUCCESS)
738     result = mach_port_insert_right(current_task, handler_port_, handler_port_,
739                                     MACH_MSG_TYPE_MAKE_SEND);
740
741   if (install_handler && result == KERN_SUCCESS)
742     if (!InstallHandler())
743       return false;
744
745   if (result == KERN_SUCCESS) {
746     // Install the handler in its own thread, detached as we won't be joining.
747     pthread_attr_t attr;
748     pthread_attr_init(&attr);
749     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
750     int thread_create_result = pthread_create(&handler_thread_, &attr,
751                                               &WaitForMessage, this);
752     pthread_attr_destroy(&attr);
753     result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
754   }
755
756   return result == KERN_SUCCESS;
757 }
758
759 bool ExceptionHandler::Teardown() {
760   kern_return_t result = KERN_SUCCESS;
761   is_in_teardown_ = true;
762
763   if (!UninstallHandler(false))
764     return false;
765
766   // Send an empty message so that the handler_thread exits
767   if (SendMessageToHandlerThread(kShutdownMessage)) {
768     mach_port_t current_task = mach_task_self();
769     result = mach_port_deallocate(current_task, handler_port_);
770     if (result != KERN_SUCCESS)
771       return false;
772   } else {
773     return false;
774   }
775
776   handler_thread_ = NULL;
777   handler_port_ = MACH_PORT_NULL;
778   pthread_mutex_destroy(&minidump_write_mutex_);
779
780   return result == KERN_SUCCESS;
781 }
782
783 bool ExceptionHandler::SendMessageToHandlerThread(
784     HandlerThreadMessage message_id) {
785   ExceptionMessage msg;
786   memset(&msg, 0, sizeof(msg));
787   msg.header.msgh_id = message_id;
788   if (message_id == kWriteDumpMessage ||
789       message_id == kWriteDumpWithExceptionMessage) {
790     // Include this thread's port.
791     msg.thread.name = mach_thread_self();
792     msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
793     msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
794   }
795   msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
796   msg.header.msgh_remote_port = handler_port_;
797   msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
798                                           MACH_MSG_TYPE_MAKE_SEND_ONCE);
799   kern_return_t result = mach_msg(&(msg.header),
800                                   MACH_SEND_MSG | MACH_SEND_TIMEOUT,
801                                   msg.header.msgh_size, 0, 0,
802                                   MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
803
804   return result == KERN_SUCCESS;
805 }
806
807 void ExceptionHandler::UpdateNextID() {
808   next_minidump_path_ =
809     (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
810
811   next_minidump_path_c_ = next_minidump_path_.c_str();
812   next_minidump_id_c_ = next_minidump_id_.c_str();
813 }
814
815 bool ExceptionHandler::SuspendThreads() {
816   thread_act_port_array_t   threads_for_task;
817   mach_msg_type_number_t    thread_count;
818
819   if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
820     return false;
821
822   // suspend all of the threads except for this one
823   for (unsigned int i = 0; i < thread_count; ++i) {
824     if (threads_for_task[i] != mach_thread_self()) {
825       if (thread_suspend(threads_for_task[i]))
826         return false;
827     }
828   }
829
830   return true;
831 }
832
833 bool ExceptionHandler::ResumeThreads() {
834   thread_act_port_array_t   threads_for_task;
835   mach_msg_type_number_t    thread_count;
836
837   if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
838     return false;
839
840   // resume all of the threads except for this one
841   for (unsigned int i = 0; i < thread_count; ++i) {
842     if (threads_for_task[i] != mach_thread_self()) {
843       if (thread_resume(threads_for_task[i]))
844         return false;
845     }
846   }
847
848   return true;
849 }
850
851 }  // namespace google_breakpad