Wed, 31 Dec 2014 06:09:35 +0100
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 ¤t.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