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